diff --git a/filters/karbon/image/ImageExport.cpp b/filters/karbon/image/ImageExport.cpp index 98526374acf..0f74dc5793f 100644 --- a/filters/karbon/image/ImageExport.cpp +++ b/filters/karbon/image/ImageExport.cpp @@ -1,124 +1,124 @@ /* This file is part of the KDE project Copyright (C) 2002-2004 Rob Buis Copyright (C) 2002 Lennart Kudling Copyright (C) 2002 Werner Trobin Copyright (C) 2004 Nicolas Goutte Copyright (C) 2005 Tim Beaulen Copyright (C) 2005 Thomas Zander Copyright (C) 2005-2006 David Faure Copyright (C) 2006 Inge Wallin Copyright (C) 2006 Laurent Montel Copyright (C) 2006 Christian Mueller Copyright (C) 2007-2008,2012 Jan Hambrecht 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 "ImageExport.h" #include "ImageExportOptionsWidget.h" #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(PngExportFactory, registerPlugin();) K_EXPORT_PLUGIN(PngExportFactory("calligrafilters")) ImageExport::ImageExport(QObject*parent, const QVariantList&) : KoFilter(parent) { } KoFilter::ConversionStatus ImageExport::convert(const QByteArray& from, const QByteArray& to) { QString format; if (to == "image/png") { format = "PNG"; } else if(to == "image/jpeg") { format = "JPG"; } if (format.isEmpty()) { return KoFilter::NotImplemented; } if (from != "application/vnd.oasis.opendocument.graphics") { return KoFilter::NotImplemented; } KoDocument * document = m_chain->inputDocument(); if (! document) return KoFilter::ParsingError; KarbonKoDocument * karbonPart = dynamic_cast(document); if (! karbonPart) return KoFilter::WrongFormat; KoShapePainter painter; painter.setShapes(karbonPart->document().shapes()); // get the bounding rect of the content QRectF shapesRect = painter.contentRect(); // get the size in point QSizeF pointSize = shapesRect.size(); // get the size in pixel (100% zoom) KoZoomHandler zoomHandler; QSize pixelSize = zoomHandler.documentToView(pointSize).toSize(); QColor backgroundColor(Qt::white); if (! m_chain->manager()->getBatchMode()) { ImageExportOptionsWidget * widget = new ImageExportOptionsWidget(pointSize); widget->setUnit(karbonPart->unit()); widget->setBackgroundColor(backgroundColor); widget->enableBackgroundOpacity(format == "PNG"); KDialog dlg; dlg.setCaption(i18n("PNG Export Options")); dlg.setButtons(KDialog::Ok | KDialog::Cancel); dlg.setMainWidget(widget); if (dlg.exec() != QDialog::Accepted) return KoFilter::UserCancelled; pixelSize = widget->pixelSize(); backgroundColor = widget->backgroundColor(); } QImage image(pixelSize, QImage::Format_ARGB32); // draw the background of the image image.fill(backgroundColor.rgba()); // paint the shapes painter.paint(image); - if(!image.save(m_chain->outputFile(), format.toAscii())) { + if(!image.save(m_chain->outputFile(), format.toLatin1())) { return KoFilter::CreationError; } return KoFilter::OK; } #include "ImageExport.moc" diff --git a/filters/karbon/pdf/SvgOutputDev.cpp b/filters/karbon/pdf/SvgOutputDev.cpp index e0b203dbf69..092e9a8d2f1 100644 --- a/filters/karbon/pdf/SvgOutputDev.cpp +++ b/filters/karbon/pdf/SvgOutputDev.cpp @@ -1,550 +1,550 @@ /* This file is part of the KDE project * Copyright (C) 2008 Jan Hambrecht * * 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 "SvgOutputDev.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class SvgOutputDev::Private { public: Private(const QString &fname) : svgFile(fname), defs(0), body(0), state(gTrue) , brush(Qt::SolidPattern) {} ~Private() { delete defs; delete body; } QFile svgFile; QString bodyData; QString defsData; QTextStream * defs; QTextStream * body; GBool state; QSizeF pageSize; QPen pen; QBrush brush; }; SvgOutputDev::SvgOutputDev(const QString &fileName) : d(new Private(fileName)) { if (! d->svgFile.open(QIODevice::WriteOnly)) { d->state = gFalse; return; } d->body = new QTextStream(&d->bodyData, QIODevice::ReadWrite); d->defs = new QTextStream(&d->defsData, QIODevice::ReadWrite); } SvgOutputDev::~SvgOutputDev() { delete d; } GBool SvgOutputDev::isOk() { return d->state; } GBool SvgOutputDev::upsideDown() { return gTrue; } GBool SvgOutputDev::useDrawChar() { return gFalse; } GBool SvgOutputDev::interpretType3Chars() { return gFalse; } void SvgOutputDev::startPage(int pageNum, GfxState *state) { kDebug(30516) << "starting page" << pageNum; d->pageSize = QSizeF(state->getPageWidth(), state->getPageHeight()); kDebug(30516) << "page size =" << d->pageSize; *d->body << "body << " display=\"none\""; *d->body << ">" << endl; } void SvgOutputDev::endPage() { kDebug(30516) << "ending page"; *d->body << "" << endl; } void SvgOutputDev::dumpContent() { kDebug(30516) << "dumping pages"; QTextStream stream(&d->svgFile); stream << "" << endl; stream << "" << endl; // add some PR. one line is more than enough. stream << "" << endl; stream << "pageSize.width() << "px\" height=\"" << d->pageSize.height() << "px\">" << endl; stream << "" << endl; stream << d->defsData; stream << "" << endl; stream << d->bodyData; stream << "" << endl; d->svgFile.close(); } void SvgOutputDev::stroke(GfxState * state) { QString path = convertPath(state->getPath()); *d->body << "body << " transform=\"" << convertMatrix(state->getCTM()) << "\""; *d->body << printStroke(); *d->body << " fill=\"none\""; *d->body << " d=\"" << path << "\""; *d->body << "/>" << endl; } void SvgOutputDev::fill(GfxState * state) { QString path = convertPath(state->getPath()); *d->body << "body << " transform=\"" << convertMatrix(state->getCTM()) << "\""; *d->body << printFill(); *d->body << " fill-rule=\"nonzero\""; *d->body << " d=\"" << path << "\""; *d->body << "/>" << endl; } void SvgOutputDev::eoFill(GfxState *state) { QString path = convertPath(state->getPath()); *d->body << "body << " transform=\"" << convertMatrix(state->getCTM()) << "\""; *d->body << printFill(); *d->body << " fill-rule=\"evenodd\""; *d->body << " d=\"" << path << "\""; *d->body << "/>" << endl; } QString SvgOutputDev::convertPath(GfxPath *path) { if (! path) return QString(); QString output; for (int i = 0; i < path->getNumSubpaths(); ++i) { GfxSubpath * subpath = path->getSubpath(i); if (subpath->getNumPoints() > 0) { output += QString("M%1 %2").arg(subpath->getX(0)).arg(subpath->getY(0)); int j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { output += QString("C%1 %2 %3 %4 %5 %6") .arg(subpath->getX(j)).arg(subpath->getY(j)) .arg(subpath->getX(j + 1)).arg(subpath->getY(j + 1)) .arg(subpath->getX(j + 2)).arg(subpath->getY(j + 2)); j += 3; } else { output += QString("L%1 %2").arg(subpath->getX(j)).arg(subpath->getY(j)); ++j; } } if (subpath->isClosed()) { output += QString("Z"); } } } return output; } QString SvgOutputDev::convertMatrix(const QMatrix &matrix) { return QString("matrix(%1 %2 %3 %4 %5 %6)") .arg(matrix.m11()).arg(matrix.m12()) .arg(matrix.m21()).arg(matrix.m22()) .arg(matrix.dx()) .arg(matrix.dy()); } QString SvgOutputDev::convertMatrix(double * matrix) { return QString("matrix(%1 %2 %3 %4 %5 %6)") .arg(matrix[0]).arg(matrix[1]) .arg(matrix[2]).arg(matrix[3]) .arg(matrix[4]) .arg(matrix[5]); } void SvgOutputDev::updateAll(GfxState *state) { kDebug(30516) << "update complete state"; //updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); //updateFlatness(state); updateMiterLimit(state); updateFillColor(state); updateStrokeColor(state); updateFillOpacity(state); updateStrokeOpacity(state); } void SvgOutputDev::updateFillColor(GfxState *state) { GfxRGB rgb; state->getFillRGB(&rgb); QColor brushColour = d->brush.color(); brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), brushColour.alphaF()); d->brush.setColor(brushColour); kDebug(30516) << "update fill color" << brushColour; } void SvgOutputDev::updateStrokeColor(GfxState *state) { GfxRGB rgb; state->getStrokeRGB(&rgb); QColor penColour = d->pen.color(); penColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), penColour.alphaF()); d->pen.setColor(penColour); kDebug(30516) << "update stroke color" << penColour; } void SvgOutputDev::updateFillOpacity(GfxState *state) { QColor brushColour = d->brush.color(); brushColour.setAlphaF(state->getFillOpacity()); d->brush.setColor(brushColour); kDebug(30516) << "update fill opacity" << state->getFillOpacity(); } void SvgOutputDev::updateStrokeOpacity(GfxState *state) { QColor penColour = d->pen.color(); penColour.setAlphaF(state->getStrokeOpacity()); d->pen.setColor(penColour); kDebug(30516) << "update stroke opacity" << state->getStrokeOpacity(); } void SvgOutputDev::updateLineJoin(GfxState *state) { switch (state->getLineJoin()) { case 0: d->pen.setJoinStyle(Qt::MiterJoin); break; case 1: d->pen.setJoinStyle(Qt::RoundJoin); break; case 2: d->pen.setJoinStyle(Qt::BevelJoin); break; } } void SvgOutputDev::updateLineCap(GfxState *state) { switch (state->getLineCap()) { case 0: d->pen.setCapStyle(Qt::FlatCap); break; case 1: d->pen.setCapStyle(Qt::RoundCap); break; case 2: d->pen.setCapStyle(Qt::SquareCap); break; } } void SvgOutputDev::updateMiterLimit(GfxState *state) { d->pen.setMiterLimit(state->getMiterLimit()); } void SvgOutputDev::updateLineWidth(GfxState *state) { //d->pen.setWidthF( state->getTransformedLineWidth() ); d->pen.setWidthF(state->getLineWidth()); } QString SvgOutputDev::printFill() { QString fill; fill += " fill=\""; switch (d->brush.style()) { case Qt::NoBrush: fill += "none"; break; case Qt::SolidPattern: fill += d->brush.color().name(); break; default: kDebug() << "unhandled fill style (" << d->brush.style() << ")"; return QString(); break; } fill += "\""; if (d->brush.color().alphaF() < 1.0) fill += QString(" fill-opacity=\"%1\"").arg(d->brush.color().alphaF()); return fill; } QString SvgOutputDev::printStroke() { QString stroke; stroke += " stroke=\""; if (d->pen.style() == Qt::NoPen) stroke += "none"; else stroke += d->pen.color().name(); stroke += "\""; if (d->pen.color().alphaF() < 1.0) stroke += QString(" stroke-opacity=\"%1\"").arg(d->pen.color().alphaF()); stroke += QString(" stroke-width=\"%1\"").arg(d->pen.widthF()); if (d->pen.capStyle() == Qt::FlatCap) stroke += " stroke-linecap=\"butt\""; else if (d->pen.capStyle() == Qt::RoundCap) stroke += " stroke-linecap=\"round\""; else if (d->pen.capStyle() == Qt::SquareCap) stroke += " stroke-linecap=\"square\""; if (d->pen.joinStyle() == Qt::MiterJoin) { stroke += " stroke-linejoin=\"miter\""; stroke += QString(" stroke-miterlimit=\"%1\"").arg(d->pen.miterLimit()); } else if (d->pen.joinStyle() == Qt::RoundJoin) stroke += " stroke-linejoin=\"round\""; else if (d->pen.joinStyle() == Qt::BevelJoin) stroke += " stroke-linejoin=\"bevel\""; // dash if (d->pen.style() > Qt::SolidLine) { //*stream << " stroke-dashoffset=\"" << line->dashPattern().offset() << "\""; stroke += " stroke-dasharray=\" "; foreach(qreal dash, d->pen.dashPattern()) { stroke += dash + ' '; } stroke += "\""; } return stroke; } void SvgOutputDev::drawString(GfxState * state, GooString * s) { int render = state->getRender(); // check for invisible text -- this is used by Acrobat Capture if (render == 3) return; // ignore empty strings if (s->getLength() == 0) return; GfxFont * font = state->getFont(); QString str; char * p = s->getCString(); int len = s->getLength(); CharCode code; Unicode *u = NULL; int uLen; double dx, dy, originX, originY; while (len > 0) { int n = font->getNextChar(p, len, &code, &u, &uLen, &dx, &dy, &originX, &originY); p += n; len -= n; if (!u) break; str += QChar(*u); } str = str.simplified(); if (str.isEmpty()) return; // escape special characters str.replace('&', "&"); str.replace('<', "<"); str.replace('>', ">"); double x = state->getCurX(); double y = state->getCurY(); double * ctm = state->getCTM(); QMatrix transform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]); QMatrix mirror; mirror.translate(x, y); mirror.scale(1.0, -1.0); mirror.translate(-x, -y); transform = mirror * transform; bool writeTransform = true; if (transform.m11() == 1.0 && transform.m12() == 0.0 && transform.m21() == 0.0 && transform.m22() == 1.0) { writeTransform = false; x += transform.dx(); y += transform.dy(); } *d->body << "body << " x=\"" << x << "px\""; *d->body << " y=\"" << y << "px\""; if (font && font->getFamily()) { - *d->body << " font-family=\"" << QString::fromAscii(font->getFamily()->getCString()) << "\""; - //kDebug(30516) << "font family:" << QString::fromAscii( font->getFamily()->getCString() ); + *d->body << " font-family=\"" << QString::fromLatin1(font->getFamily()->getCString()) << "\""; + //kDebug(30516) << "font family:" << QString::fromLatin1( font->getFamily()->getCString() ); } else if (font && font->getName()) { - *d->body << " font-family=\"" << QString::fromAscii(font->getName()->getCString()) << "\""; - //kDebug(30516) << "font name:" << QString::fromAscii( font->getName()->getCString() ); + *d->body << " font-family=\"" << QString::fromLatin1(font->getName()->getCString()) << "\""; + //kDebug(30516) << "font name:" << QString::fromLatin1( font->getName()->getCString() ); } *d->body << " font-size=\"" << qMax(state->getFontSize(), state->getTransformedFontSize()) << "px\""; if (writeTransform) *d->body << " transform=\"" << convertMatrix(transform) << "\""; // fill if (!(render & 1)) *d->body << printFill(); // stroke if ((render & 3) == 1 || (render & 3) == 2) *d->body << printStroke(); *d->body << ">"; *d->body << str; *d->body << "" << endl; } void SvgOutputDev::drawImage(GfxState *state, Object */*ref*/, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool /*inlineImg*/) { ImageStream * imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); unsigned int *dest = 0; unsigned char * buffer = new unsigned char[width * height * 4]; QImage * image = 0; if (maskColors) { for (int y = 0; y < height; y++) { dest = (unsigned int *)(buffer + y * 4 * width); Guchar * pix = imgStr->getLine(); colorMap->getRGBLine(pix, dest, width); for (int x = 0; x < width; x++) { for (int i = 0; i < colorMap->getNumPixelComps(); ++i) { if (pix[i] < maskColors[2*i] * 255 || pix[i] > maskColors[2*i+1] * 255) { *dest = *dest | 0xff000000; break; } } pix += colorMap->getNumPixelComps(); dest++; } } image = new QImage(buffer, width, height, QImage::Format_ARGB32); } else { for (int y = 0; y < height; y++) { dest = (unsigned int *)(buffer + y * 4 * width); Guchar * pix = imgStr->getLine(); colorMap->getRGBLine(pix, dest, width); } image = new QImage(buffer, width, height, QImage::Format_RGB32); } if (image == NULL || image->isNull()) { kDebug(30516) << "Null image"; delete imgStr; delete[] buffer; delete image; return; } double * ctm = state->getCTM(); QMatrix m; m.setMatrix(ctm[0] / width, ctm[1] / width, -ctm[2] / height, -ctm[3] / height, ctm[2] + ctm[4], ctm[3] + ctm[5]); QByteArray ba; QBuffer device(&ba); device.open(QIODevice::WriteOnly); if (image->save(&device, "PNG")) { *d->body << "body << " transform=\"" << convertMatrix(m) << "\""; *d->body << " width=\"" << width << "px\""; *d->body << " height=\"" << height << "px\""; *d->body << " xlink:href=\"data:image/png;base64," << ba.toBase64() << "\""; *d->body << " />" << endl; } delete image; delete[] buffer; delete imgStr; } void SvgOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool /*interpolate*/, int *maskColors, GBool inlineImg) { drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg); } diff --git a/filters/sheets/excel/sidewinder/XlsRecordOutputStream.cpp b/filters/sheets/excel/sidewinder/XlsRecordOutputStream.cpp index c15ccf1d6fe..03fd0a68415 100644 --- a/filters/sheets/excel/sidewinder/XlsRecordOutputStream.cpp +++ b/filters/sheets/excel/sidewinder/XlsRecordOutputStream.cpp @@ -1,216 +1,216 @@ /* This file is part of the KDE project Copyright (C) 2010 Marijn Kruisselbrink 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 "XlsRecordOutputStream.h" #include #include #include "utils.h" using namespace Swinder; static const unsigned NORECORD = 0xFFFFFFFF; XlsRecordOutputStream::XlsRecordOutputStream(QIODevice* device) : m_dataStream(device), m_currentRecord(NORECORD), m_buffer(0) { Q_ASSERT(device->isOpen()); Q_ASSERT(device->isWritable()); Q_ASSERT(!device->isSequential()); m_dataStream.setByteOrder(QDataStream::LittleEndian); } qint64 XlsRecordOutputStream::pos() const { if (m_currentRecord != NORECORD) { return m_dataStream.device()->pos() + 4 + m_buffer->size(); } else { return m_dataStream.device()->pos(); } } qint64 XlsRecordOutputStream::recordPos() const { Q_ASSERT(m_currentRecord != NORECORD); Q_ASSERT(m_curBitOffset == 0); return m_buffer->size(); } void XlsRecordOutputStream::writeRecord(const Record& record) { startRecord(record.rtti()); record.writeData(*this); endRecord(); } void XlsRecordOutputStream::writeRecord(Record& record) { record.setPosition(pos()); writeRecord(const_cast(record)); } void XlsRecordOutputStream::rewriteRecord(const Record& record) { qint64 oldPos = pos(); m_dataStream.device()->seek(record.position()); writeRecord(record); m_dataStream.device()->seek(oldPos); } void XlsRecordOutputStream::startRecord(unsigned recordType) { Q_ASSERT(m_currentRecord == NORECORD); m_currentRecord = recordType; m_buffer = new QBuffer(); m_buffer->open(QIODevice::WriteOnly); m_curByte = 0; m_curBitOffset = 0; } void XlsRecordOutputStream::endRecord() { Q_ASSERT(m_currentRecord != NORECORD); Q_ASSERT(m_curBitOffset == 0); // TODO: add support for larger-than-8224 records Q_ASSERT(m_buffer->data().size() <= 8224); m_dataStream << quint16(m_currentRecord); m_dataStream << quint16(m_buffer->data().size()); m_dataStream.writeRawData(m_buffer->data().data(), m_buffer->data().size()); delete m_buffer; m_currentRecord = NORECORD; } void XlsRecordOutputStream::writeUnsigned(unsigned bits, unsigned value) { Q_ASSERT(m_currentRecord != NORECORD); unsigned mask = bits == 32 ? 0xFFFFFFFF : (unsigned(1) << bits) - 1; value &= mask; if (m_curBitOffset) { if (bits < 8-m_curBitOffset) { m_curByte |= value << m_curBitOffset; m_curBitOffset += bits; return; } else if (bits == 8-m_curBitOffset) { m_curByte |= value << m_curBitOffset; m_curBitOffset += bits; m_buffer->write(reinterpret_cast(&m_curByte), 1); m_curByte = 0; m_curBitOffset = 0; return; } else { unsigned bitsLeft = 8-m_curBitOffset; unsigned maskVal = value & ((1 << bitsLeft) - 1); m_curByte |= maskVal << m_curBitOffset; m_buffer->write(reinterpret_cast(&m_curByte), 1); m_curByte = 0; m_curBitOffset = 0; bits -= bitsLeft; value >>= bitsLeft; mask >>= bitsLeft; } } while (bits >= 8) { m_buffer->write(reinterpret_cast(&value), 1); bits -= 8; value >>= 8; mask >>= 8; } m_curByte = value; m_curBitOffset = bits; } void XlsRecordOutputStream::writeSigned(unsigned bits, signed value) { writeUnsigned(bits, value); } void XlsRecordOutputStream::writeFloat(unsigned bits, double value) { Q_ASSERT(bits == 32 || bits == 64); QBuffer b; b.open(QIODevice::WriteOnly); QDataStream ds(&b); ds.setByteOrder(QDataStream::LittleEndian); ds.setFloatingPointPrecision(bits == 32 ? QDataStream::SinglePrecision : QDataStream::DoublePrecision); ds << value; Q_ASSERT(b.data().size() == (bits / 8)); writeBlob(b.data()); } void XlsRecordOutputStream::writeUnicodeString(const QString& value) { QBuffer b; b.open(QIODevice::WriteOnly); QDataStream ds(&b); ds.setByteOrder(QDataStream::LittleEndian); const ushort* d = value.utf16(); while (*d) { ds << quint16(*d); d++; } writeBlob(b.data()); } void XlsRecordOutputStream::writeUnicodeStringWithFlags(const QString& value) { char flags = 1; // just unicode, nothing else m_buffer->write(&flags, 1); writeUnicodeString(value); } void XlsRecordOutputStream::writeUnicodeStringWithFlagsAndLength(const QString& value) { if (m_buffer->size() + 7 > 8224) { // header doesn't fit in record, add continue record endRecord(); startRecord(0x003C); } writeUnsigned(16, value.length()); writeUnsigned(8, 1); // unicode, no other flags int pos = 0; while (pos < value.length()) { int len = (8224 - m_buffer->size()) / 2; writeUnicodeString(value.mid(pos, len)); pos += len; if (pos < value.length()) { endRecord(); startRecord(0x003C); writeUnsigned(8, 1); // unicode, no other flags } } } void XlsRecordOutputStream::writeByteString(const QString& value) { - writeBlob(value.toAscii()); + writeBlob(value.toLatin1()); } void XlsRecordOutputStream::writeBlob(const QByteArray& value) { Q_ASSERT(m_currentRecord != NORECORD); Q_ASSERT(m_curBitOffset == 0); m_buffer->write(value); } diff --git a/filters/sheets/excel/sidewinder/formulas.cpp b/filters/sheets/excel/sidewinder/formulas.cpp index ff3aae91d26..df4a15c3696 100644 --- a/filters/sheets/excel/sidewinder/formulas.cpp +++ b/filters/sheets/excel/sidewinder/formulas.cpp @@ -1,1914 +1,1914 @@ /* Swinder - Portable library for spreadsheet Copyright (C) 2003-2005 Ariya Hidayat Copyright (C) 2006,2009 Marijn Kruisselbrink Copyright (C) 2009,2010 Sebastian Sauer 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 "formulas.h" #include #include #include #include #include #include "excel.h" #include "utils.h" namespace Swinder { //============================================= // FormulaToken //============================================= class FormulaToken::Private { public: unsigned ver; unsigned id; std::vector data; }; FormulaToken::FormulaToken() { d = new Private; d->ver = Excel97; d->id = Unused; } FormulaToken::FormulaToken(unsigned t) { d = new Private; d->ver = Excel97; d->id = t; } FormulaToken::FormulaToken(const FormulaToken& token) { d = new Private; d->ver = token.d->ver; d->id = token.id(); d->data.resize(token.d->data.size()); for (unsigned i = 0; i < d->data.size(); i++) d->data[i] = token.d->data[i]; } FormulaToken& FormulaToken::operator=(const FormulaToken& token) { d->ver = token.d->ver; d->id = token.id(); d->data.resize(token.d->data.size()); for (unsigned i = 0; i < d->data.size(); i++) d->data[i] = token.d->data[i]; return *this; } FormulaToken::~FormulaToken() { delete d; } FormulaToken FormulaToken::createBool(bool value) { FormulaToken t(Bool); unsigned char data = value ? 1: 0; t.setData(1, &data); return t; } FormulaToken FormulaToken::createNum(double value) { FormulaToken t(Float); QBuffer b; b.open(QIODevice::WriteOnly); QDataStream ds(&b); ds.setByteOrder(QDataStream::LittleEndian); ds.setFloatingPointPrecision(QDataStream::DoublePrecision); ds << value; Q_ASSERT(b.data().size() == 8); t.setData(b.data().size(), reinterpret_cast(b.data().data())); return t; } FormulaToken FormulaToken::createStr(const QString &value) { FormulaToken t(String); QBuffer b; b.open(QIODevice::WriteOnly); QDataStream ds(&b); ds.setByteOrder(QDataStream::LittleEndian); ds << quint8(value.length()); ds << quint8(1); // unicode for (int i = 0; i < value.length(); i++) { ds << quint16(value[i].unicode()); } t.setData(b.data().size(), reinterpret_cast(b.data().data())); return t; } FormulaToken FormulaToken::createRef(const QPoint& pos, bool rowFixed, bool colFixed) { FormulaToken t(Ref); QBuffer b; b.open(QIODevice::WriteOnly); QDataStream ds(&b); ds.setByteOrder(QDataStream::LittleEndian); unsigned row = pos.y(); unsigned col = pos.x(); bool rowRel = !rowFixed; bool colRel = !colFixed; if (rowRel) col |= 0x4000; if (colRel) col |= 0x8000; ds << quint16(row); ds << quint16(col); t.setData(b.data().size(), reinterpret_cast(b.data().data())); return t; } FormulaToken FormulaToken::createRefErr() { FormulaToken t(RefErr); quint32 zero = 0; t.setData(4, reinterpret_cast(&zero)); return t; } FormulaToken FormulaToken::createArea(const QRect& area, bool topFixed, bool bottomFixed, bool leftFixed, bool rightFixed) { FormulaToken t(Area); QBuffer b; b.open(QIODevice::WriteOnly); QDataStream ds(&b); ds.setByteOrder(QDataStream::LittleEndian); unsigned row1 = area.top(); unsigned col1 = area.left(); bool rowRel1 = !topFixed; bool colRel1 = !leftFixed; unsigned row2 = area.bottom(); unsigned col2 = area.right(); bool rowRel2 = !bottomFixed; bool colRel2 = !rightFixed; if (rowRel1) col1 |= 0x4000; if (colRel1) col1 |= 0x8000; if (rowRel2) col2 |= 0x4000; if (colRel2) col2 |= 0x8000; ds << quint16(row1); ds << quint16(row2); ds << quint16(col1); ds << quint16(col2); t.setData(b.data().size(), reinterpret_cast(b.data().data())); return t; } FormulaToken FormulaToken::createAreaErr() { FormulaToken t(AreaErr); quint64 zero = 0; t.setData(8, reinterpret_cast(&zero)); return t; } FormulaToken FormulaToken::createFunc(const QString &func, unsigned argCount) { unsigned paramCount = functionParams(func); bool isVarArgs = !fixedFunctionParams(func) || argCount != paramCount; FormulaToken t(isVarArgs ? FunctionVar : Function); QBuffer b; b.open(QIODevice::WriteOnly); QDataStream ds(&b); ds.setByteOrder(QDataStream::LittleEndian); if (isVarArgs) { ds << quint8(argCount); } ds << quint16(functionIndex(func)); t.setData(b.data().size(), reinterpret_cast(b.data().data())); return t; } unsigned FormulaToken::version() const { return d->ver; } void FormulaToken::setVersion(unsigned v) { d->ver = v; } unsigned FormulaToken::id() const { return d->id; } const char* FormulaToken::idAsString() const { const char* s = 0; switch (d->id) { case Matrix: s = "Matrix"; break; case Table: s = "Table"; break; case Add: s = "Add"; break; case Sub: s = "Sub"; break; case Mul: s = "Mul"; break; case Div: s = "Div"; break; case Power: s = "Power"; break; case Concat: s = "Concat"; break; case LT: s = "LT"; break; case LE: s = "LE"; break; case EQ: s = "EQ"; break; case GE: s = "GE"; break; case GT: s = "GT"; break; case NE: s = "NE"; break; case Intersect: s = "Intersect"; break; case Union: s = "Union"; break; case Range: s = "Range"; break; case UPlus: s = "UPlus"; break; case UMinus: s = "UMinus"; break; case Percent: s = "Percent"; break; case Paren: s = "Paren"; break; case String: s = "String"; break; case MissArg: s = "MissArg"; break; case ErrorCode: s = "ErrorCode"; break; case Bool: s = "Bool"; break; case Integer: s = "Integer"; break; case Array: s = "Array"; break; case Function: s = "Function"; break; case FunctionVar: s = "FunctionVar"; break; case Name: s = "Name"; break; case Ref: s = "Ref"; break; case RefErr: s = "RefErr"; break; case RefN: s = "RefN"; break; case Area: s = "Area"; break; case AreaErr: s = "AreaErr"; break; case AreaN: s = "AreaN"; break; case NameX: s = "NameX"; break; case Ref3d: s = "Ref3d"; break; case RefErr3d: s = "RefErr3d"; break; case Float: s = "Float"; break; case Area3d: s = "Area3d"; break; case AreaErr3d: s = "AreaErr3d"; break; case NatFormula: s = "NatFormula"; break; case Sheet: s = "Sheet"; break; case EndSheet: s = "EndSheet"; break; case MemArea: s = "MemArea"; break; case MemErr: s = "MemErr"; break; case MemNoMem: s = "MemNoMem"; break; case MemFunc: s = "MemFunc"; break; case MemAreaN: s = "MemAreaN"; break; case MemNoMemN: s = "MemNoMemN"; break; case Attr: switch (attr()) { case AttrChoose: s = "AttrChoose"; break; default: s = "Attr"; break; } break; case 0: s = ""; break; // NOPE... default: s = "Unknown"; printf("Unhandled formula id %i as string\n", d->id); break; } return s; } unsigned FormulaToken::size() const { unsigned s = 0; // on most cases no data switch (d->id) { case Add: case Sub: case Mul: case Div: case Power: case Concat: case LT: case LE: case EQ: case GE: case GT: case NE: case Intersect: case Union: case Range: case UPlus: case UMinus: case Percent: case Paren: case MissArg: s = 0; break; case Attr: switch (attr()) { case AttrChoose: s = 3 + 2 * (1+readU16(&(d->data[1]))); break; default: s = 3; break; } break; case ErrorCode: case Bool: s = 1; break; case Integer: s = 2; break; case Array: s = 7; break; case Function: s = 2; break; case FunctionVar: s = 3; break; case Matrix: case Table: s = (d->ver == Excel97) ? 4 : 3; break; case Name: s = (d->ver == Excel97) ? 4 : 14; break; case Ref: case RefErr: case RefN: s = (d->ver == Excel97) ? 4 : 3; break; case Area: case AreaErr: case AreaN: s = (d->ver == Excel97) ? 8 : 6; break; case NameX: s = (d->ver == Excel97) ? 6 : 24; break; case Ref3d: case RefErr3d: s = (d->ver == Excel97) ? 6 : 17; break; case Float: s = 8; break; case Area3d: case AreaErr3d: s = (d->ver == Excel97) ? 10 : 20; break; case MemArea: s = 6; break; case MemErr: s = 6; break; case 0: // NOPE s = 0; break; case MemFunc: s = 2; break; case NatFormula: case Sheet: case EndSheet: case MemNoMem: case MemAreaN: case MemNoMemN: default: if (d->data.empty()) // WARNING this is unhandled case printf("Unhandled formula token with id %i\n", d->id); else s = d->data.size(); break; } return s; } void FormulaToken::setData(unsigned size, const unsigned char* data) { d->data.resize(size); for (unsigned i = 0; i < size; i++) d->data[i] = data[i]; } std::vector FormulaToken::data() const { return d->data; } Value FormulaToken::value() const { Value result; unsigned char* buf; buf = new unsigned char[d->data.size()]; for (unsigned k = 0; k < d->data.size(); k++) buf[k] = d->data[k]; // FIXME sanity check: verify size of data switch (d->id) { case ErrorCode: result = errorAsValue(buf[0]); break; case Bool: result = Value(buf[0] != 0); break; case Integer: result = Value((int)readU16(buf)); break; case Float: result = Value(readFloat64(buf)); break; case String: { EString estr = (version() == Excel97) ? EString::fromUnicodeString(buf, false, d->data.size()) : EString::fromByteString(buf, false, d->data.size()); result = Value(estr.str()); break; } default: break; } delete [] buf; return result; } unsigned FormulaToken::functionIndex() const { // FIXME check data size unsigned index = 0; unsigned char buf[2]; if (d->id == Function) { buf[0] = d->data[0]; buf[1] = d->data[1]; index = readU16(buf); } if (d->id == FunctionVar) { buf[0] = d->data[1]; buf[1] = d->data[2]; index = readU16(buf); } return index; } struct FunctionEntry { const char *name; int params; bool varParams; }; static const FunctionEntry FunctionEntries[] = { { "COUNT", 1, true }, // 0 { "IF", 0, true }, // 1 { "ISNA", 1, false }, // 2 { "ISERROR", 1, false }, // 3 { "SUM", 0, true }, // 4 { "AVERAGE", 0, true }, // 5 { "MIN", 0, true }, // 6 { "MAX", 0, true }, // 7 { "ROW", 0, true }, // 8 { "COLUMN", 0, true }, // 9 { "NA", 0, false }, // 10 { "NPV", 0, true }, // 11 { "STDEV", 0, true }, // 12 { "DOLLAR", 0, true }, // 13 { "FIXED", 0, true }, // 14 { "SIN", 1, false }, // 15 { "COS", 1, false }, // 16 { "TAN", 1, false }, // 17 { "ATAN", 1, false }, // 18 { "PI", 0, false }, // 19 { "SQRT", 1, false }, // 20 { "EXP", 1, false }, // 21 { "LN", 1, false }, // 22 { "LOG10", 1, false }, // 23 { "ABS", 1, false }, // 24 { "INT", 1, false }, // 25 { "SIGN", 1, false }, // 26 { "ROUND", 2, false }, // 27 { "LOOKUP", 0, true }, // 28 { "INDEX", 0, true }, // 29 { "REPT", 2, false }, // 30 { "MID", 3, false }, // 31 { "LEN", 1, false }, // 32 { "VALUE", 1, false }, // 33 { "TRUE", 0, false }, // 34 { "FALSE", 0, false }, // 35 { "AND", 0, true }, // 36 { "OR", 0, true }, // 37 { "NOT", 1, false }, // 38 { "MOD", 2, false }, // 39 { "DCOUNT", 3, false }, // 40 { "DSUM", 3, false }, // 41 { "DAVERAGE", 3, false }, // 42 { "DMIN", 3, false }, // 43 { "DMAX", 3, false }, // 44 { "DSTDEV", 3, false }, // 45 { "VAR", 0, true }, // 46 { "DVAR", 3, false }, // 47 { "TEXT", 2, false }, // 48 { "LINEST", 0, true }, // 49 { "TREND", 0, true }, // 50 { "LOGEST", 0, true }, // 51 { "GROWTH", 0, true }, // 52 { "GOTO", 0, false }, // 53 { "HALT", 0, true }, // 54 { "RETURN", 0, true }, // 55 { "PV", 0, true }, // 56 { "FV", 0, true }, // 57 { "NPER", 0, true }, // 58 { "PMT", 0, true }, // 59 { "RATE", 0, true }, // 60 { "MIRR", 3, false }, // 61 { "IRR", 0, true }, // 62 { "RAND", 0, false }, // 63 { "MATCH", 0, true }, // 64 { "DATE", 3, false }, // 65 { "TIME", 3, false }, // 66 { "DAY", 1, false }, // 67 { "MONTH", 1, false }, // 68 { "YEAR", 1, false }, // 69 { "WEEKDAY", 0, true }, // 70 { "HOUR", 1, false }, // 71 { "MINUTE", 1, false }, // 72 { "SECOND", 1, false }, // 73 { "NOW", 0, false }, // 74 { "AREAS", 1, false }, // 75 { "ROWS", 1, false }, // 76 { "COLUMNS", 1, false }, // 77 { "OFFSET", 0, true }, // 78 { "ABSREF", 2, false }, // 79 { "RELREF", 2, false }, // 80 { "ARGUMENT", 0, true }, // 81 { "SEARCH", 0, true }, // 82 { "TRANSPOSE", 1, false }, // 83 { "ERROR", 0, true }, // 84 { "STEP", 0, false }, // 85 { "TYPE", 1, false }, // 86 { "ECHO", 0, true }, { "SETNAME", 0, true }, // deprecated... { "CALLER", 0, false }, { "DEREF", 1, false }, { "WINDOWS", 0, true }, { "SERIES", 4, true }, { "DOCUMENTS", 0, true }, { "ACTIVECELL", 0, false }, // deprecated... { "SELECTION", 0, false }, { "RESULT", 0, true }, { "ATAN2", 2, false }, // 97 { "ASIN", 1, false }, // 98 { "ACOS", 1, false }, // 99 { "CHOOSE", 0, true }, // 100 { "HLOOKUP", 0, true }, // 101 { "VLOOKUP", 0, true }, // 102 { "LINKS", 0, true }, { "INPUT", 0, true }, { "ISREF", 1, false }, // 105 { "GETFORMULA", 1, false }, // deprecated... { "GETNAME", 0, true }, // deprecated... { "SETVALUE", 2, false }, // deprecated... { "LOG", 0, true }, // 109 { "EXEC", 0, true }, { "CHAR", 1, false }, // 111 { "LOWER", 1, false }, // 112 { "UPPER", 1, false }, // 113 { "PROPER", 1, false }, // 114 { "LEFT", 0, true }, // 115 { "RIGHT", 0, true }, // 116 { "EXACT", 2, false }, // 117 { "TRIM", 1, false }, // 118 { "REPLACE", 4, false }, // 119 { "SUBSTITUTE", 0, true }, // 120 { "CODE", 1, false }, // 121 { "NAMES", 0, true }, { "DIRECTORY", 0, true }, { "FIND", 0, true }, // 124 { "CELL", 0, true }, // 125 { "ISERR", 1, false }, // 126 { "ISTEXT", 1, false }, // 127 { "ISNUMBER", 1, false }, // 128 { "ISBLANK", 1, false }, // 129 { "T", 1, false }, // 130 { "N", 1, false }, // 131 { "FOPEN", 0, true }, // not portable, insecure, deprecated { "FCLOSE", 1, false }, // not portable, insecure, deprecated { "FSIZE", 1, false }, // not portable, insecure, deprecated { "FREADLN", 1, false }, // not portable, insecure, deprecated { "FREAD", 2, false }, // not portable, insecure, deprecated { "FWRITELN", 2, false }, // not portable, insecure, deprecated { "FWRITE", 2, false }, // not portable, insecure, deprecated { "FPOS", 0, true }, // not portable, insecure, deprecated { "DATEVALUE", 1, false }, // 140 { "TIMEVALUE", 1, false }, // 141 { "SLN", 3, false }, // 142 { "SYD", 4, false }, // 143 { "DDB", 0, true }, // 144 { "GETDEF", 0, true }, { "REFTEXT", 0, true }, { "TEXTREF", 0, true }, { "INDIRECT", 0, true }, // 148 { "REGISTER", 0, true }, { "CALL", 0, true }, { "ADDBAR", 0, true }, // deprecated { "ADDMENU", 0, true }, // deprecated { "ADDCOMMAND", 0, true }, // deprecated { "ENABLECOMMAND", 0, true }, // deprecated { "CHECKCOMMAND", 0, true }, // deprecated { "RENAMECOMMAND", 0, true }, // deprecated { "SHOWBAR", 0, true }, // deprecated { "DELETEMENU", 0, true }, // deprecated { "DELETECOMMAND", 0, true }, // deprecated { "GETCHARTITEM", 0, true }, // deprecated { "DIALOGBOX", 0, true }, // deprecated { "CLEAN", 1, false }, // 162 { "MDETERM", 1, false }, // 163 { "MINVERSE", 1, false }, // 164 { "MMULT", 2, false }, // 165 { "FILES", 0, true }, // not portable, insecure, deprecated { "IPMT", 0, true }, // 167 { "PPMT", 0, true }, // 168 { "COUNTA", 0, true }, // 169 { "CANCELKEY", 1, true }, { "FOR", 0, true }, { "WHILE", 1, false }, { "BREAK", 0, false }, { "NEXT", 0, false }, { "INITIATE", 2, false }, { "REQUEST", 2, false }, { "POKE", 3, false }, { "EXECUTE", 2, false }, { "TERMINATE", 1, false }, { "RESTART", 0, true }, { "HELP", 0, true }, { "GETBAR", 0, true }, { "PRODUCT", 0, true }, // 183 { "FACT", 1, false }, // 184 { "GETCELL", 0, true }, { "GETWORKSPACE", 1, false }, { "GETWINDOW", 0, true }, { "GETDOCUMENT", 0, true }, { "DPRODUCT", 3, false }, // 189 { "ISNONTEXT", 1, false }, // 190 { "GETNOTE", 0, true }, { "NOTE", 0, true }, { "STDEVP", 0, true }, // 193 { "VARP", 0, true }, // 194 { "DSTDEVP", 3, false }, // 195 { "DVARP", 3, false }, // 196 { "TRUNC", 0, true }, // 197 { "ISLOGICAL", 1, false }, // 198 { "DCOUNTA", 3, false }, // 199 { "DELETEBAR", 1, false }, { "UNREGISTER", 1, false }, { "Unknown202", 0, true }, { "Unknown203", 0, true }, { "USDOLLAR", 0, true }, { "FINDB", 0, true }, { "SEARCHB", 0, true }, { "REPLACEB", 4, false }, { "LEFTB", 0, true }, { "RIGHTB", 0, true }, { "MIDB", 3, false }, { "LENB", 1, false }, { "ROUNDUP", 2, false }, // 212 { "ROUNDDOWN", 2, false }, // 213 { "ASC", 1, false }, { "DBCS", 1, false }, { "RANK", 0, true }, // 216 { "Unknown217", 0, true }, { "Unknown218", 0, true }, { "ADDRESS", 0, true }, // 219 { "DAYS360", 0, true }, // 220 { "CURRENTDATE", 0, false }, // 221 { "VBD", 0, true }, // 222 { "ELSE", 0, false }, { "ELSE.IF", 1, false }, { "END.IF", 0, false }, { "FOR.CELL", 0, true }, { "MEDIAN", 0, true }, // 227 { "SUMPRODUCT", 0, true }, // 228 { "SINH", 1, false }, // 229 { "COSH", 1, false }, // 230 { "TANH", 1, false }, // 231 { "ASINH", 1, false }, // 232 { "ACOSH", 1, false }, // 233 { "ATANH", 1, false }, // 234 { "DGET", 3, false }, // 235 { "CREATEOBJECT", 0, true }, { "VOLATILE", 0, true }, { "LASTERROR", 0, false }, { "CUSTOMUNDO", 0, true }, { "CUSTOMREPEAT", 0, true }, { "FORMULACONVERT", 0, true }, { "GETLINKINFO", 0, true }, { "TEXTBOX", 0, true }, { "INFO", 1, false }, // 244 { "GROUP", 0, false }, { "GETOBJECT", 0, true }, { "DB", 0, true }, // 247 { "PAUSE", 0, true }, { "Unknown249", 0, true }, { "Unknown250", 0, true }, { "RESUME", 0, true }, { "FREQUENCY", 2, false }, // 252 { "ADDTOOLBAR", 0, true }, { "DELETETOOLBAR", 0, true }, { "USER.DEFINED.FUNCTION", 0, true }, { "RESETTOOLBAR", 1, false }, { "EVALUATE", 1, false }, { "GETTOOLBAR", 0, true }, { "GETTOOL", 0, true }, { "SPELLINGCHECK", 0, true }, { "ERRORTYPE", 1, false }, // 261 { "APPTITLE", 0, true }, { "WINDOWTITLE", 0, true }, { "SAVETOOLBAR", 0, true }, { "ENABLETOOL", 3, false }, { "PRESSTOOL", 3, false }, { "REGISTERID", 0, true }, { "GETWORKBOOK", 0, true }, { "AVEDEV", 0, true }, // 269 { "BETADIST", 0, true }, // 270 { "GAMMALN", 1, false }, // 271 { "BETAINV", 0, true }, // 272 { "BINOMDIST", 4, false }, // 273 { "CHIDIST", 2, false }, // 274 { "CHIINV", 2, false }, // 275 { "COMBIN", 2, false }, // 276 { "CONFIDENCE", 3, false }, // 277 { "CRITBINOM", 3, false }, // 278 { "EVEN", 1, false }, // 279 { "EXPONDIST", 3, false }, // 280 { "FDIST", 3, false }, // 281 { "FINV", 3, false }, // 282 { "FISHER", 1, false }, // 283 { "FISHERINV", 1, false }, // 284 { "FLOOR", 2, false }, // 285 { "GAMMADIST", 4, false }, // 286 { "GAMMAINV", 3, false }, // 287 { "CEILING", 2, false }, // 288 { "HYPGEOMDIST", 4, false }, // 289 { "LOGNORMDIST", 3, false }, // 290 { "LOGINV", 3, false }, // 291 { "NEGBINOMDIST", 3, false }, // 292 { "NORMDIST", 4, false }, // 293 { "NORMSDIST", 1, false }, // 294 { "NORMINV", 3, false }, // 295 { "NORMSINV", 1, false }, // 296 { "STANDARDIZE", 3, false }, // 297 { "ODD", 1, false }, // 298 { "PERMUT", 2, false }, // 299 { "POISSON", 3, false }, // 300 { "TDIST", 3, false }, // 301 { "WEIBULL", 4, false }, // 302 { "SUMXMY2", 2, false }, // 303 { "SUMX2MY2", 2, false }, // 304 { "SUMX2DY2", 2, false }, // 305 { "CHITEST", 2, false }, // 306 { "CORREL", 2, false }, // 307 { "COVAR", 2, false }, // 308 { "FORECAST", 3, false }, // 309 { "FTEST", 2, false }, // 310 { "INTERCEPT", 2, false }, // 311 { "PEARSON", 2, false }, // 312 { "RSQ", 2, false }, // 313 { "STEYX", 2, false }, // 314 { "SLOPE", 2, false }, // 315 { "TTEST", 4, false }, // 316 { "PROB", 0, true }, // 317 { "DEVSQ", 0, true }, // 318 { "GEOMEAN", 0, true }, // 319 { "HARMEAN", 0, true }, // 320 { "SUMSQ", 0, true }, // 321 { "KURT", 0, true }, // 322 { "SKEW", 0, true }, // 323 { "ZTEST", 0, true }, // 324 { "LARGE", 2, false }, // 325 { "SMALL", 2, false }, // 326 { "QUARTILE", 2, false }, // 327 { "PERCENTILE", 2, false }, // 328 { "PERCENTRANK", 0, true }, // 329 { "MODALVALUE", 0, true }, // 330 { "TRIMMEAN", 2, false }, // 331 { "TINV", 2, false }, // 332 { "Unknown333", 0, true }, { "MOVIECOMMAND", 0, true }, { "GETMOVIE", 0, true }, { "CONCATENATE", 0, true }, // 336 { "POWER", 2, false }, // 337 { "PIVOTADDDATA", 0, true }, { "GETPIVOTTABLE", 0, true }, { "GETPIVOTFIELD", 0, true }, { "GETPIVOTITEM", 0, true }, { "RADIANS", 1, false }, // 342 { "DEGREES", 1, false }, // 343 { "SUBTOTAL", 0, true }, // 344 { "SUMIF", 0, true }, // 345 { "COUNTIF", 2, true }, // 346 { "COUNTBLANK", 1, true }, // 347 { "SCENARIOGET", 0, true }, { "OPTIONSLISTSGET", 1, false }, { "ISPMT", 4, false }, { "DATEDIF", 3, false }, { "DATESTRING", 1, false }, { "NUMBERSTRING", 2, false }, { "ROMAN", 0, true }, // 354 { "OPENDIALOG", 0, true }, { "SAVEDIALOG", 0, true }, { "VIEWGET", 0, true }, { "GETPIVOTDATA", 2, true }, // 358 { "HYPERLINK", 1, true }, { "PHONETIC", 1, false }, { "AVERAGEA", 0, true }, // 361 { "MAXA", 0, true }, // 362 { "MINA", 0, true }, // 363 { "STDEVPA", 0, true }, // 364 { "VARPA", 0, true }, // 365 { "STDEVA", 0, true }, // 366 { "VARA", 0, true }, // 367 { "BAHTTEXT", 1, false }, // 368 //TODO; following formulas are not supported in Calligra Tables yet { "THAIDAYOFWEEK", 1, false }, // 369 { "THAIDIGIT", 1, false }, // 370 { "THAIMONTHOFYEAR", 1, false }, // 371 { "THAINUMSOUND", 1, false }, // 372 { "THAINUMSTRING", 1, false }, // 373 { "THAISTRINGLENGTH", 1, false }, // 374 { "ISTHAIDIGIT", 1, false }, // 375 { "ROUNDBAHTDOWN", 1, false }, // 376 { "ROUNDBAHTUP", 1, false }, // 377 { "THAIYEAR", 1, false }, // 378 { "RTD", 1, false }, // 379 { "ISHYPERLINK", 1, false } // 380 }; static const FunctionEntry* functionEntry(const QString& functionName) { static QHash entries; if (entries.isEmpty()) { for (int i = 0; i <= 380; i++) { - entries[QString::fromAscii(FunctionEntries[i].name)] = &FunctionEntries[i]; + entries[QString::fromLatin1(FunctionEntries[i].name)] = &FunctionEntries[i]; } } return entries.value(functionName); } const char* FormulaToken::functionName() const { if (functionIndex() > 367) return 0; return FunctionEntries[ functionIndex()].name; } unsigned FormulaToken::functionParams() const { unsigned params = 0; if (d->id == Function) { if (functionIndex() > 367) return 0; params = FunctionEntries[ functionIndex()].params; } if (d->id == FunctionVar) { params = (unsigned)d->data[0]; params &= 0x7f; } return params; } unsigned FormulaToken::functionIndex(const QString &functionName) { const FunctionEntry* e = functionEntry(functionName); if (e) return e - FunctionEntries; return -1; } unsigned FormulaToken::functionParams(const QString &functionName) { const FunctionEntry* e = functionEntry(functionName); if (e) return e->params; return 0; } bool FormulaToken::fixedFunctionParams(const QString &functionName) { const FunctionEntry* e = functionEntry(functionName); if (e) return !e->varParams; return false; } unsigned FormulaToken::attr() const { unsigned attr = 0; if (d->id == Attr && d->data.size() > 0) { attr = (unsigned) d->data[0]; } return attr; } unsigned long FormulaToken::nameIndex() const { // FIXME check data size ! unsigned long ni = 0; unsigned char buf[4]; if (d->id == Name) { if (d->ver == Excel97) { buf[0] = d->data[0]; buf[1] = d->data[1]; buf[2] = d->data[2]; buf[3] = d->data[3]; ni = readU32(buf); } else if (d->ver == Excel95) { buf[0] = d->data[8]; buf[1] = d->data[9]; ni = readU16(buf); } } return ni; } unsigned long FormulaToken::nameXIndex() const { // FIXME check data size ! unsigned long ni = 0; unsigned char buf[4]; if (d->id == NameX) { if (d->ver == Excel97) { buf[0] = d->data[2]; buf[1] = d->data[3]; buf[2] = d->data[4]; buf[3] = d->data[5]; ni = readU32(buf); } else if (d->ver == Excel95) { buf[0] = d->data[10]; buf[1] = d->data[11]; ni = readU16(buf); } } return ni; } static QString escapeSheetName(const QString& sheetName) { bool hasSpecial = false; for (int i = 0; i < sheetName.length(); i++) { if (!sheetName[i].isLetterOrNumber()) { hasSpecial = true; break; } } if (!hasSpecial) return sheetName; QString res = sheetName; while(res.startsWith('\'') && res.endsWith('\'')) res = res.mid(1, res.length() - 2); return "$'" + res.replace('\'', QLatin1String("\'\'")) + "'"; } QString FormulaToken::area(unsigned row, unsigned col, bool relative) const { // FIXME check data size ! unsigned char buf[2]; int row1Ref, row2Ref, col1Ref, col2Ref; bool row1Relative, col1Relative; bool row2Relative, col2Relative; if (version() == Excel97) { buf[0] = d->data[0]; buf[1] = d->data[1]; row1Ref = readU16(buf); buf[0] = d->data[2]; buf[1] = d->data[3]; row2Ref = readU16(buf); buf[0] = d->data[4]; buf[1] = d->data[5]; col1Ref = readU16(buf); buf[0] = d->data[6]; buf[1] = d->data[7]; col2Ref = readU16(buf); row1Relative = col1Ref & 0x8000; col1Relative = col1Ref & 0x4000; col1Ref &= 0x3fff; row2Relative = col2Ref & 0x8000; col2Relative = col2Ref & 0x4000; col2Ref &= 0x3fff; if (relative) { if (row1Ref & 0x8000) row1Ref -= 0x10000; if (row2Ref & 0x8000) row2Ref -= 0x10000; if (col1Ref & 0x80) col1Ref -= 0x100; if (col2Ref & 0x80) col2Ref -= 0x100; } } else { buf[0] = d->data[0]; buf[1] = d->data[1]; row1Ref = readU16(buf); buf[0] = d->data[2]; buf[1] = d->data[3]; row2Ref = readU16(buf); buf[0] = d->data[4]; buf[1] = 0; col1Ref = readU16(buf); buf[0] = d->data[5]; buf[1] = 0; col2Ref = readU16(buf); row1Relative = row2Ref & 0x8000; col1Relative = row2Ref & 0x4000; row1Ref &= 0x3fff; row2Relative = row2Ref & 0x8000; col2Relative = row2Ref & 0x4000; row2Ref &= 0x3fff; if (relative) { if (row1Ref & 0x2000) row1Ref -= 0x4000; if (row2Ref & 0x2000) row2Ref -= 0x4000; if (col1Ref & 0x80) col1Ref -= 0x100; if (col2Ref & 0x80) col2Ref -= 0x100; } } if (relative) { row1Ref += row; row2Ref += row; col1Ref += col; col2Ref += col; } QString result; result.append(QString("[")); // OpenDocument format if (!col1Relative) result.append(QString("$")); result.append(Cell::columnLabel(col1Ref)); if (!row1Relative) result.append(QString("$")); result.append(QString::number(row1Ref + 1)); result.append(QString(":")); if (!col2Relative) result.append(QString("$")); result.append(Cell::columnLabel(col2Ref)); if (!row2Relative) result.append(QString("$")); result.append(QString::number(row2Ref + 1)); result.append(QString("]")); // OpenDocument format return result; } QString FormulaToken::area3d(const std::vector& externSheets, unsigned /*row*/, unsigned /*col*/) const { if (version() != Excel97) { return QString("Unknown"); } unsigned sheetRef = readU16(&d->data[0]); // FIXME check data size ! unsigned char buf[2]; int row1Ref, row2Ref, col1Ref, col2Ref; bool row1Relative, col1Relative; bool row2Relative, col2Relative; buf[0] = d->data[2]; buf[1] = d->data[3]; row1Ref = readU16(buf); buf[0] = d->data[4]; buf[1] = d->data[5]; row2Ref = readU16(buf); buf[0] = d->data[6]; buf[1] = d->data[7]; col1Ref = readU16(buf); buf[0] = d->data[8]; buf[1] = d->data[9]; col2Ref = readU16(buf); row1Relative = col1Ref & 0x8000; col1Relative = col1Ref & 0x4000; col1Ref &= 0x3fff; row2Relative = col2Ref & 0x8000; col2Relative = col2Ref & 0x4000; col2Ref &= 0x3fff; QString result; result.append(QString("[")); // OpenDocument format if (sheetRef >= externSheets.size()) result.append(QString("Error")); else result.append(escapeSheetName(externSheets[sheetRef])); result.append(QString(".")); if (!col1Relative) result.append(QString("$")); result.append(Cell::columnLabel(col1Ref)); if (!row1Relative) result.append(QString("$")); result.append(QString::number(row1Ref + 1)); result.append(QString(":")); if (!col2Relative) result.append(QString("$")); result.append(Cell::columnLabel(col2Ref)); if (!row2Relative) result.append(QString("$")); result.append(QString::number(row2Ref + 1)); result.append(QString("]")); // OpenDocument format return result; } std::pair FormulaToken::filterArea3d() const { if (version() != Excel97) { return std::make_pair(0, QRect()); } unsigned sheetRef = readU16(&d->data[0]); // FIXME check data size ! unsigned char buf[2]; int row1Ref, row2Ref, col1Ref, col2Ref; buf[0] = d->data[2]; buf[1] = d->data[3]; row1Ref = readU16(buf); buf[0] = d->data[4]; buf[1] = d->data[5]; row2Ref = readU16(buf); buf[0] = d->data[6]; buf[1] = d->data[7]; col1Ref = readU16(buf); buf[0] = d->data[8]; buf[1] = d->data[9]; col2Ref = readU16(buf); col1Ref &= 0x3fff; col2Ref &= 0x3fff; QRect range(col1Ref, row1Ref, col2Ref - col1Ref + 1, row2Ref - row1Ref + 1); return std::make_pair(sheetRef, range); } QString FormulaToken::areaMap(unsigned row, unsigned col) { unsigned char buf[4]; buf[0] = d->data[0]; unsigned ptg = readU8(buf); const int type = (ptg & 0x20 ? 1 : 0) + (ptg & 0x60 ? 2 : 0); //Q_ASSERT(type == 1 || type == 2 || type == 3); buf[0] = d->data[5]; buf[1] = d->data[6]; unsigned cce = readU16(buf); //printf( "SIZE=%i\n", cce ); if (cce < 7) { printf("Error: Invalid size %i for formula areaMap of type %i\n", cce, type); return QString(); } // remove the first seven elements cause they are done d->data.erase(d->data.begin(), d->data.begin() + 7); //unsigned size, const unsigned char* data QString result; switch (type) { case 0x01: // REFERENCE, specifies a reference to a range. result = ref(row, col); break; case 0x02: // VALUE, specifies a single value of a simple type. The type can be a Boolean, a number, a string, or an error code. result = value().asString(); break; case 0x03: // ARRAY, specifies an array of values. result = array(row, col); break; } //d->data.erase(d->data.begin(), d->data.begin() + cce); return result; } QString FormulaToken::ref(unsigned /*row*/, unsigned /*col*/) const { // FIXME check data size ! // FIXME handle shared formula unsigned char buf[2]; int rowRef, colRef; bool rowRelative, colRelative; if (version() == Excel97) { buf[0] = d->data[0]; buf[1] = d->data[1]; rowRef = readU16(buf); buf[0] = d->data[2]; buf[1] = d->data[3]; colRef = readU16(buf); rowRelative = colRef & 0x8000; colRelative = colRef & 0x4000; colRef &= 0x3fff; } else { buf[0] = d->data[0]; buf[1] = d->data[1]; rowRef = readU16(buf); buf[0] = d->data[2]; buf[1] = 0; colRef = readU16(buf); rowRelative = rowRef & 0x8000; colRelative = rowRef & 0x4000; rowRef &= 0x3fff; } QString result; result.append(QString("[")); // OpenDocument format if (!colRelative) result.append(QString("$")); result.append(Cell::columnLabel(colRef)); if (!rowRelative) result.append(QString("$")); result.append(QString::number(rowRef + 1)); result.append(QString("]")); // OpenDocument format return result; } QString FormulaToken::refn(unsigned row, unsigned col) const { // FIXME check data size ! // FIXME handle shared formula unsigned char buf[2]; int rowRef, colRef; bool rowRelative, colRelative; if (version() == Excel97) { buf[0] = d->data[0]; buf[1] = d->data[1]; rowRef = readS16(buf); buf[0] = d->data[2]; buf[1] = d->data[3]; colRef = readU16(buf); rowRelative = colRef & 0x8000; colRelative = colRef & 0x4000; colRef &= 0xff; if (colRef & 0x80) { colRef = colRef - 0x100; } } else { buf[0] = d->data[0]; buf[1] = d->data[1]; rowRef = readU16(buf); buf[0] = d->data[2]; colRef = readS8(buf); rowRelative = rowRef & 0x8000; colRelative = rowRef & 0x4000; rowRef &= 0x3fff; if (rowRef & 0x2000) { rowRef = rowRef - 0x4000; } } if (colRelative) colRef += col; if (rowRelative) rowRef += row; QString result; result.append(QString("[")); // OpenDocument format if (!colRelative) result.append(QString("$")); result.append(Cell::columnLabel(qMax(0, colRef))); if (!rowRelative) result.append(QString("$")); result.append(QString::number(rowRef + 1)); result.append(QString("]")); // OpenDocument format return result; } QString FormulaToken::ref3d(const std::vector& externSheets, unsigned /*row*/, unsigned /*col*/) const { if (version() != Excel97) { return QString("Unknown"); } unsigned sheetRef = readU16(&d->data[0]); // FIXME check data size ! // FIXME handle shared formula unsigned char buf[2]; int rowRef, colRef; bool rowRelative, colRelative; buf[0] = d->data[2]; buf[1] = d->data[3]; rowRef = readU16(buf); buf[0] = d->data[4]; buf[1] = d->data[5]; colRef = readU16(buf); rowRelative = colRef & 0x8000; colRelative = colRef & 0x4000; colRef &= 0x3fff; QString result; result.append(QString("[")); // OpenDocument format if (sheetRef >= externSheets.size()) result.append(QString("Error")); else result.append(escapeSheetName(externSheets[sheetRef])); result.append(QString(".")); if (!colRelative) result.append(QString("$")); result.append(Cell::columnLabel(colRef)); if (!rowRelative) result.append(QString("$")); result.append(QString::number(rowRef + 1)); result.append(QString("]")); // OpenDocument format return result; } QString FormulaToken::array(unsigned row, unsigned col) const { Q_UNUSED(row); Q_UNUSED(col); #ifdef __GNUC__ #warning TODO Implement FormulaToken::array() #endif printf("Unhandled formula array-token with row=%i and column=%i\n", row, col); /* unsigned char buf[2]; buf[0] = d->data[1]; // specs say this should be at the first byte but seems its not true... const unsigned opts = readU8(buf); const bool reserved1 = opts & 0x01; const bool reserved2 = opts & 0x02; const bool reserved3 = opts & 0x04; const bool reserved4 = opts & 0x08; const bool reserved5 = opts & 0x10; Q_ASSERT(!reserved1 && !reserved2 && !reserved3 && !reserved4 && !reserved5); const int type = ((opts & 0x20) ? 1 : 0) + ((opts & 0x60) ? 2 : 0); printf("%i\n",type); Q_ASSERT(type == 2 || type == 3); // remove the first two elements cause they are done d->data.erase(d->data.begin(), d->data.begin() + 2); QString result; switch (type) { case 0x01: // REFERENCE, specifies a reference to a range. result = ref(row, col); break; case 0x02: // VALUE, specifies a single value of a simple type. The type can be a Boolean, a number, a string, or an error code. result = value().asString(); break; case 0x03: // ARRAY, specifies an array of values. result = array(row, col); break; } //Q_ASSERT(false); return result; */ return QString(); } std::pair FormulaToken::baseFormulaRecord() const { if (version() == Excel97) { return std::make_pair(readU16(&d->data[0]), readU16(&d->data[2])); } else { return std::make_pair(readU16(&d->data[0]), (unsigned)d->data[2]); } } std::ostream& operator<<(std::ostream& s, Swinder::FormulaToken token) { s << std::setw(2) << std::hex << token.id() << std::dec; // s << " Size: " << std::dec << token.size(); s << " "; switch (token.id()) { case FormulaToken::ErrorCode: case FormulaToken::Bool: case FormulaToken::Integer: case FormulaToken::Float: case FormulaToken::String: { Value v = token.value(); s << v; } break; case FormulaToken::Function: s << "Function " << token.functionName(); break; default: s << token.idAsString(); break; } return s; } FormulaTokens FormulaDecoder::decodeFormula(unsigned size, unsigned pos, const unsigned char* data, unsigned version) { FormulaTokens tokens; const unsigned formula_len = readU16(data + pos); if (formula_len + pos + 2 > size) { std::cerr << "formula is longer than available data" << std::endl; return tokens; } for (unsigned j = pos + 2; j < size;) { unsigned ptg = data[j++]; ptg = ((ptg & 0x40) ? (ptg | 0x20) : ptg) & 0x3F; FormulaToken t(ptg); t.setVersion(version); if (t.id() == FormulaToken::String) { // find bytes taken to represent the string EString estr = (version == Excel97) ? EString::fromUnicodeString(data + j, false, formula_len) : EString::fromByteString(data + j, false, formula_len); t.setData(estr.size(), data + j); j += estr.size(); } else { // normal, fixed-size token if (t.size() > 0) { t.setData(t.size(), data + j); j += t.size(); } } tokens.push_back(t); } return tokens; } typedef std::vector UStringStack; static void mergeTokens(UStringStack* stack, unsigned count, QString mergeString) { if (!stack) return; if (stack->size() < count) return; QString s1, s2; while (count) { count--; QString last = (*stack)[stack->size()-1]; QString tmp = last; tmp.append(s1); s1 = tmp; if (count) { tmp = mergeString; tmp.append(s1); s1 = tmp; } stack->resize(stack->size() - 1); } stack->push_back(s1); } #ifdef SWINDER_XLS2RAW static void dumpStack(std::vector stack) { std::cout << std::endl; std::cout << "Stack now is: " ; if (stack.empty()) std::cout << "(empty)" ; for (unsigned i = 0; i < stack.size(); i++) std::cout << " " << i << ": " << stack[i] << std::endl; std::cout << std::endl; } #endif QString FormulaDecoder::decodeFormula(unsigned row, unsigned col, bool isShared, const FormulaTokens& tokens) { UStringStack stack; for (unsigned c = 0; c < tokens.size(); c++) { FormulaToken token = tokens[c]; #ifdef SWINDER_XLS2RAW std::cout << "Formula Token " << c << ": "; std::cout << token.id() << " "; std::cout << token.idAsString() << std::endl; #endif switch (token.id()) { case FormulaToken::Add: mergeTokens(&stack, 2, QString("+")); break; case FormulaToken::Sub: mergeTokens(&stack, 2, QString("-")); break; case FormulaToken::Mul: mergeTokens(&stack, 2, QString("*")); break; case FormulaToken::Div: mergeTokens(&stack, 2, QString("/")); break; case FormulaToken::Power: mergeTokens(&stack, 2, QString("^")); break; case FormulaToken::Concat: mergeTokens(&stack, 2, QString("&")); break; case FormulaToken::LT: mergeTokens(&stack, 2, QString("<")); break; case FormulaToken::LE: mergeTokens(&stack, 2, QString("<=")); break; case FormulaToken::EQ: mergeTokens(&stack, 2, QString("=")); break; case FormulaToken::GE: mergeTokens(&stack, 2, QString(">=")); break; case FormulaToken::GT: mergeTokens(&stack, 2, QString(">")); break; case FormulaToken::NE: mergeTokens(&stack, 2, QString("<>")); break; case FormulaToken::Intersect: mergeTokens(&stack, 2, QString("!")); break; case FormulaToken::Union: mergeTokens(&stack, 2, QString("~")); break; case FormulaToken::Range: mergeTokens(&stack, 2, QString(":")); break; case FormulaToken::UPlus: { QString str("+"); str.append(stack[stack.size()-1]); stack[stack.size()-1] = str; break; } case FormulaToken::UMinus: { QString str("-"); str.append(stack[ stack.size()-1 ]); stack[stack.size()-1] = str; break; } case FormulaToken::Percent: stack[stack.size()-1].append(QString("%")); break; case FormulaToken::Paren: { QString str("("); str.append(stack[ stack.size()-1 ]); str.append(QString(")")); stack[stack.size()-1] = str; break; } case FormulaToken::MissArg: // just ignore stack.push_back(QString(" ")); break; case FormulaToken::String: { QString str('\"'); str.append(token.value().asString()); str.append(QString('\"')); stack.push_back(str); break; } case FormulaToken::Bool: if (token.value().asBoolean()) stack.push_back(QString("TRUE")); else stack.push_back(QString("FALSE")); break; case FormulaToken::Integer: stack.push_back(QString::number(token.value().asInteger())); break; case FormulaToken::Float: stack.push_back(QString::number(token.value().asFloat())); break; case FormulaToken::Array: stack.push_back(token.array(row, col)); break; case FormulaToken::Ref: stack.push_back(token.ref(row, col)); break; case FormulaToken::RefN: stack.push_back(token.refn(row, col)); break; case FormulaToken::Ref3d: stack.push_back(token.ref3d(externSheets(), row, col)); break; case FormulaToken::Area: stack.push_back(token.area(row, col)); break; case FormulaToken::AreaN: stack.push_back(token.area(row, col, true)); break; case FormulaToken::Area3d: stack.push_back(token.area3d(externSheets(), row, col)); break; case FormulaToken::Function: { mergeTokens(&stack, token.functionParams(), QString(";")); if (!stack.empty()) { QString str(token.functionName() ? token.functionName() : "??"); str.append(QString("(")); str.append(stack[stack.size()-1]); str.append(QString(")")); stack[stack.size()-1] = str; } break; } case FormulaToken::FunctionVar: if (token.functionIndex() != 255) { mergeTokens(&stack, token.functionParams(), QString(";")); if (!stack.empty()) { QString str; if (token.functionIndex() != 255) str = token.functionName() ? token.functionName() : "??"; str.append(QString("(")); str.append(stack[stack.size()-1]); str.append(QString(")")); stack[stack.size()-1] = str; } } else { unsigned count = token.functionParams() - 1; mergeTokens(&stack, count, QString(";")); if (!stack.empty()) { QString str; str.append(QString("(")); str.append(stack[ stack.size()-1 ]); str.append(QString(")")); stack[stack.size()-1] = str; } } break; case FormulaToken::Attr: if (token.attr() & 0x10) { // SUM mergeTokens(&stack, 1, QString(";")); if (!stack.empty()) { QString str("SUM"); str.append(QString("(")); str.append(stack[ stack.size()-1 ]); str.append(QString(")")); stack[stack.size()-1] = str; } } break; case FormulaToken::Name: stack.push_back(nameFromIndex(token.nameIndex()-1)); break; case FormulaToken::NameX: stack.push_back(externNameFromIndex(token.nameXIndex()-1)); break; case FormulaToken::Matrix: { std::pair formulaCellPos = token.baseFormulaRecord(); if( isShared ) { FormulaTokens ft = sharedFormulas(formulaCellPos); if (!ft.empty()) stack.push_back(decodeFormula(row, col, isShared, ft)); } else { // "2.5.198.58 PtgExp" says that if its not a sharedFormula then it's an indication that the // result is an reference to cells. So, we can savly ignore that case... std::cout << "MATRIX first=%i second=" << formulaCellPos.first << " " << formulaCellPos.second << std::endl; } break; } case FormulaToken::Table: { std::pair formulaCellPos = token.baseFormulaRecord(); if( isShared ) { DataTableRecord* dt = tableRecord(formulaCellPos); if(dt) stack.push_back(dataTableFormula(row, col, dt)); } else { std::cout << "TABLE first=%i second=" << formulaCellPos.first << " " << formulaCellPos.second << std::endl; } break; } case FormulaToken::MemArea: // does nothing break; case FormulaToken::AreaErr: case FormulaToken::AreaErr3d: case FormulaToken::RefErr: case FormulaToken::RefErr3d: stack.push_back(QString("#REF!")); break; case FormulaToken::MemErr: // specifies that the result is an error-code break; case FormulaToken::ErrorCode: stack.push_back(token.value().asString()); break; case 0: break; // NOPE case FormulaToken::MemFunc: // as far as I can tell this is only meta-data break; case FormulaToken::NatFormula: case FormulaToken::Sheet: case FormulaToken::EndSheet: case FormulaToken::MemNoMem: case FormulaToken::MemAreaN: case FormulaToken::MemNoMemN: default: // FIXME handle this ! std::cout << "Unhandled token=" << token.idAsString() << std::endl; stack.push_back(QString("Unknown")); break; }; #ifdef SWINDER_XLS2RAW dumpStack(stack); #endif } QString result; for (unsigned i = 0; i < stack.size(); i++) result.append(stack[i]); #ifdef SWINDER_XLS2RAW std::cout << "FORMULA Result: " << result << std::endl; #endif return result; } QString FormulaDecoder::dataTableFormula(unsigned row, unsigned col, const DataTableRecord* record) { QString result("MULTIPLE.OPERATIONS("); unsigned formulaRow = 0, formulaCol = 0; switch (record->direction()) { case DataTableRecord::InputRow: formulaRow = row; formulaCol = record->firstColumn() - 1; break; case DataTableRecord::InputColumn: formulaRow = record->firstRow() - 1; formulaCol = col; break; case DataTableRecord::Input2D: formulaRow = record->firstRow() - 1; formulaCol = record->firstColumn() - 1; break; } result.append(QString("[.$")); result.append(Cell::columnLabel(formulaCol)); result.append(QString("$")); result.append(QString::number(formulaRow + 1)); result.append(QString("]")); if (record->direction() == DataTableRecord::Input2D) { result.append(QString(";[.$")); result.append(Cell::columnLabel(record->inputColumn2())); result.append(QString("$")); result.append(QString::number(record->inputRow2() + 1)); result.append(QString("]")); } else { result.append(QString(";[.$")); result.append(Cell::columnLabel(record->inputColumn1())); result.append(QString("$")); result.append(QString::number(record->inputRow1() + 1)); result.append(QString("]")); } if (record->direction() == DataTableRecord::Input2D || record->direction() == DataTableRecord::InputColumn) { result.append(QString(";[.$")); result.append(Cell::columnLabel(record->firstColumn() - 1)); result.append(QString::number(row + 1)); result.append(QString("]")); } if (record->direction() == DataTableRecord::Input2D) { result.append(QString(";[.$")); result.append(Cell::columnLabel(record->inputColumn1())); result.append(QString("$")); result.append(QString::number(record->inputRow1() + 1)); result.append(QString("]")); } if (record->direction() == DataTableRecord::Input2D || record->direction() == DataTableRecord::InputRow) { result.append(QString(";[.")); result.append(Cell::columnLabel(col)); result.append(QString("$")); result.append(QString::number(record->firstRow() - 1 + 1)); result.append(QString("]")); } result.append(QString(")")); #ifdef SWINDER_XLS2RAW std::cout << "DATATABLE Result: " << result << std::endl; #endif return result; } } // namespace Swinder diff --git a/filters/sheets/excel/sidewinder/recordsxml2cpp.cpp b/filters/sheets/excel/sidewinder/recordsxml2cpp.cpp index cba7d0cc4b5..1013ef773ed 100644 --- a/filters/sheets/excel/sidewinder/recordsxml2cpp.cpp +++ b/filters/sheets/excel/sidewinder/recordsxml2cpp.cpp @@ -1,981 +1,981 @@ /* Swinder - Portable library for spreadsheet Copyright (C) 2010 Sebastian Sauer 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 #include #include #include #include static inline QString ucFirst(QString s) { return s[0].toUpper() + s.mid(1); } static inline QString lcFirst(QString s) { return s[0].toLower() + s.mid(1); } struct Field { QString name; QString type; bool isArray; bool isArrayLength; bool isStringLength; bool isEnum; QList arrayFields; QString defaultValue; QString lengthFor; Field(QString name = QString(), QString type = QString()) : name(name), type(type), isArray(false), isArrayLength(false), isStringLength(false), isEnum(false) {} QString getterName() const { if (type == "bool" && !(name.startsWith("has") || name.startsWith("is"))) { return "is" + ucFirst(name); } else { return name; } } QString setterName() const { return "set" + ucFirst(name); } }; static QString getFieldType(QString xmlType, unsigned bits, QString otherType, const QMap& extraTypes) { Q_UNUSED(bits); Q_UNUSED(otherType); if (xmlType == "unsigned") return "unsigned"; else if (xmlType == "signed") return "int"; else if (xmlType == "float" || xmlType == "fixed") return "double"; else if (xmlType == "bool") return "bool"; else if (xmlType == "bytestring" || xmlType == "unicodestring" || xmlType == "unicodechars") return "QString"; else if (xmlType == "blob") return "QByteArray"; else if (xmlType == "uuid") return "QUuid"; else if (extraTypes.contains(xmlType)) return getFieldType(extraTypes[xmlType], bits, otherType, extraTypes); return "ERROR"; } bool hasParentNode(const QDomNode& n, const QString& name) { QDomNode p = n.parentNode(); if (p.isNull()) return false; if (p.nodeName() == name) return true; return hasParentNode(p, name); } static QMap getFields(QDomElement record, bool* foundStrings = 0) { QDomNodeList types = record.elementsByTagName("type"); QMap extraTypes; QMap extraTypesDefaults; for (int i = 0; i < types.size(); i++) { QDomElement e = types.at(i).toElement(); extraTypes[e.attribute("name")] = e.attribute("type"); if (e.elementsByTagName("enum").size() > 0) extraTypesDefaults[e.attribute("name")] = e.elementsByTagName("enum").at(0).toElement().attribute("name"); } QDomNodeList fields = record.elementsByTagName("field"); QMap map; for (int i = 0; i < fields.size(); i++) { QDomElement e = fields.at(i).toElement(); QString name = e.attribute("name"); if (!name.startsWith("reserved")) { map[name] = Field(name, getFieldType(e.attribute("type"), e.attribute("size").toUInt(), map[name].type, extraTypes)); if (foundStrings && map[name].type == "QString") *foundStrings = true; if (hasParentNode(e, "array")) { map[name].isArray = true; } if (e.elementsByTagName("enum").size() > 0) { map[name].isEnum = true; map[name].type = ucFirst(name); map[name].defaultValue = e.elementsByTagName("enum").at(0).toElement().attribute("name"); } if (extraTypes.contains(e.attribute("type"))) { map[name].isEnum = true; map[name].type = e.attribute("type"); map[name].defaultValue = extraTypesDefaults[e.attribute("type")]; } if (e.hasAttribute("default")) map[name].defaultValue = e.attribute("default"); } } for (int i = 0; i < fields.size(); i++) { QDomElement e = fields.at(i).toElement(); if (e.hasAttribute("length")) { QString name = e.attribute("length"); if (map.contains(name)) { map[name].isStringLength = true; map[name].lengthFor = e.attribute("name"); } } } QDomNodeList arrays = record.elementsByTagName("array"); for (int i = 0; i < arrays.size(); i++) { QDomElement e = arrays.at(i).toElement(); QString name = e.attribute("length"); if (map.contains(name)) { Field& field = map[name]; field.isArrayLength = true; QDomNodeList afields = e.elementsByTagName("field"); for (int j = 0; j < afields.size(); j++) { QDomElement af = afields.at(j).toElement(); QString fname = af.attribute("name"); if (!fname.startsWith("reserved")) field.arrayFields.append(map[fname]); } } } return map; } void processEnumsForHeader(QDomNodeList fieldList, QTextStream& out) { for (int i = 0; i < fieldList.size(); i++) { QDomElement f = fieldList.at(i).toElement(); QDomNodeList enumNodes = f.elementsByTagName("enum"); if (enumNodes.size()) { QString name = ucFirst(f.attribute("name")); out << " enum " << name << " {\n"; for (int j = 0; j < enumNodes.size(); j++) { QDomElement en = enumNodes.at(j).toElement(); out << " " << en.attribute("name"); if (en.hasAttribute("value")) out << " = " << en.attribute("value"); if (j != enumNodes.size() - 1) out << ","; out << "\n"; } out << " };\n\n" << " static QString " << lcFirst(f.attribute("name")) << "ToString(" << name << " " << f.attribute("name") << ");\n\n"; } } } void processRecordForHeader(QDomElement e, QTextStream& out) { QString className = e.attribute("name") + "Record"; QList fields = getFields(e).values(); out << "class " << className << " : public Record\n{\npublic:\n"; // add id field and rtti method out << " static const unsigned id;\n\n" << " virtual unsigned rtti() const { return this->id; }\n\n"; // constructor and destructor out << " " << className << "(Swinder::Workbook *book);\n virtual ~" << className << "();\n\n"; // copy and assignment out << " " << className << "( const " << className << "& record );\n" << " " << className << "& operator=( const " << className << "& record );\n\n"; // enums QDomNodeList fieldNodes = e.elementsByTagName("field"); processEnumsForHeader(fieldNodes, out); QDomNodeList cfieldNodes = e.elementsByTagName("computedField"); processEnumsForHeader(cfieldNodes, out); QDomNodeList typeNodes = e.elementsByTagName("type"); processEnumsForHeader(typeNodes, out); // getters and setters foreach(const Field& f, fields) { out << " " << f.type << " " << f.getterName() << "("; if (f.isArray) out << " unsigned index "; out << ") const;\n" << " void " << f.setterName() << "("; if (f.isArray) out << " unsigned index,"; out << " " << f.type << " " << f.name << " );\n\n"; } // computed fields for (int i = 0; i < cfieldNodes.size(); i++) { QDomElement f = cfieldNodes.at(i).toElement(); QString type = f.attribute("ctype"); if (type.isEmpty()) type = ucFirst(f.attribute("name")); out << " " << type << " " << f.attribute("name") << "() const;\n\n"; } // array lengths QDomNodeList arrayNodes = e.elementsByTagName("array"); for (int i = 0; i < arrayNodes.size(); i++) { QDomElement f = arrayNodes.at(i).toElement(); if (f.hasAttribute("lengthField")) { QString name = f.attribute("lengthField"); out << " unsigned " << lcFirst(name) << "() const;\n"; out << " void set" << ucFirst(name) << "( unsigned " << name << " );\n\n"; } } // optional groups QDomNodeList optionalNodes = e.elementsByTagName("optional"); for (int i = 0; i < optionalNodes.size(); i++) { QDomElement f = optionalNodes.at(i).toElement(); out << " bool has" << ucFirst(f.attribute("name")) << "() const;\n\n"; out << " void setHas" << ucFirst(f.attribute("name")) << "( bool value );\n\n"; } // setData method out << " virtual void setData( unsigned size, const unsigned char* data, const unsigned* continuePositions );\n\n"; // writeData method out << " virtual void writeData( XlsRecordOutputStream& out ) const;\n\n"; // name method out << " virtual const char* name() const { return \"" << e.attribute("name") << "\"; }\n\n"; // dump method out << " virtual void dump( std::ostream& out ) const;\n\n"; // private stuff out << "private:\n class Private;\n Private * const d;\n};\n\n"; } static void invalidFound(QString indent, QTextStream& out, QString currentOptional) { if (currentOptional.isEmpty()) { out << indent << "setIsValid(false);\n"; out << indent << "return;\n"; } else { out << indent << "d->has" << ucFirst(currentOptional) << " = false;\n"; out << indent << "goto " << lcFirst(currentOptional) << "Failed;\n"; } } static void sizeCheck(QString indent, QTextStream& out, QDomElement firstField, unsigned offset, bool dynamicOffset, QString currentOptional) { // find size of all fields until first unsized field or first if/array unsigned size = 0; for (QDomElement e = firstField; !e.isNull(); e = e.nextSiblingElement()) { if (e.tagName() != "field") break; unsigned bits = e.attribute("size", "0").toUInt(); if (bits == 0 && e.attribute("type") == "uuid") { bits = 128; e.setAttribute("size", "128"); } if (bits == 0) break; size += bits; } if (size % 8 != 0) qFatal("Invalid non-byte-sized chunk of fields found"); if (offset % 8 != 0) qFatal("Invalid non-byte-aligned chunk of fields found"); if (size != 0) { out << indent << "if (size < "; if (dynamicOffset) out << "curOffset + "; if (offset) out << (offset / 8) << " + "; out << (size / 8) << ") {\n"; invalidFound(indent + " ", out, currentOptional); out << indent << "}\n"; } } static void processFieldElement(QString indent, QTextStream& out, QDomElement field, unsigned& offset, bool& dynamicOffset, QMap& fieldsMap, QString setterArgs = QString(), QString currentOptional = QString()) { if (field.tagName() == "fail") { invalidFound(indent, out, currentOptional); } else if (field.tagName() == "field") { unsigned bits = field.attribute("size").toUInt(); QString name = field.attribute("name"); if (!name.startsWith("reserved")) { //if (bits >= 8 && offset % 8 != 0) // qFatal("Unaligned byte-or-larger field"); if (bits >= 16 && bits % 8 != 0) qFatal("Fields of 16 bits and larger must always be an exact number of bytes"); Field& f = fieldsMap[name]; if (f.type == "QString") { if (offset % 8 != 0) qFatal("Unaligned string"); if (!dynamicOffset) out << indent << "curOffset = " << (offset / 8) << ";\n"; else if (offset) out << indent << "curOffset += " << (offset / 8) << ";\n"; out << indent << f.setterName() << "(" << setterArgs; dynamicOffset = true; offset = 0; if (field.attribute("type") == "unicodestring") out << "readUnicodeString("; else if (field.attribute("type") == "unicodechars") out << "readUnicodeCharArray("; else out << "readByteString("; out << "data + curOffset, "; if (field.hasAttribute("length") || field.attribute("type") != "unicodechars") out << field.attribute("length"); else out << "-1"; out << ", size - curOffset" << ", &stringLengthError, &stringSize));\n"; out << indent << "if (stringLengthError) {\n"; invalidFound(indent + " ", out, currentOptional); out << indent << "}\n"; out << indent << "curOffset += stringSize;\n"; sizeCheck(indent, out, field.nextSiblingElement(), offset, dynamicOffset, currentOptional); } else if (f.type == "QByteArray") { if (offset % 8 != 0) qFatal("Unaligned string"); if (field.hasAttribute("length")) { out << indent << "if ("; if (dynamicOffset) out << "curOffset + "; if (offset) out << (offset / 8) << " + "; out << "(" << field.attribute("length") << ") > size) {\n"; invalidFound(indent + " ", out, currentOptional); out << indent << "}\n"; } out << indent << f.setterName() << "(" << setterArgs; out << "QByteArray(reinterpret_cast("; out << "data"; if (dynamicOffset) out << " + curOffset"; if (offset) out << " + " << (offset / 8); out << "), "; if (field.hasAttribute("length")) out << field.attribute("length"); else out << (bits / 8); out << "));\n"; if (field.hasAttribute("length")) { if (!dynamicOffset) out << indent << "curOffset = " << (offset / 8); else out << indent << "curOffset += " << (offset / 8); out << " + " << field.attribute("length") << ";\n"; dynamicOffset = true; offset = 0; sizeCheck(indent, out, field.nextSiblingElement(), offset, dynamicOffset, currentOptional); } } else if (f.type == "QUuid") { out << indent << f.setterName() << "(" << setterArgs; out << "readUuid(data"; if (dynamicOffset) out << " + curOffset"; if (offset) out << " + " << (offset / 8); out << "));\n"; } else if (bits % 8 == 0) { if (f.isStringLength) out << indent << "unsigned " << name << " = "; else out << indent << f.setterName() << "(" << setterArgs; if (f.isEnum) out << "static_cast<" << f.type << ">("; if (field.attribute("type") == "unsigned" || field.attribute("type") == "bool") out << "readU" << bits; else if (field.attribute("type") == "signed") out << "readS" << bits; else if (field.attribute("type") == "float") out << "readFloat" << bits; else if (field.attribute("type") == "fixed") out << "readFixed" << bits; else qFatal("Unsupported type %s", qPrintable(field.attribute("type"))); out << "(data"; if (dynamicOffset) out << " + curOffset"; if (offset) out << " + " << (offset / 8); out << ")"; if (field.attribute("type") == "bool") out << " != 0"; if (f.isEnum) out << ")"; if (!f.isStringLength) out << ")"; out << ";\n"; } else { if (f.isStringLength) out << indent << "unsigned " << name << " = "; else out << indent << f.setterName() << "(" << setterArgs; if (f.isEnum) out << "static_cast<" << f.type << ">("; unsigned firstByte = offset / 8; unsigned lastByte = (offset + bits - 1) / 8; unsigned bitOffset = offset % 8; unsigned mask = (1 << bits) - 1; if (firstByte == lastByte) { out << "((readU8(data"; } else { out << "((readU16(data"; } if (dynamicOffset) out << " + curOffset"; if (firstByte) out << " + " << firstByte; out << ")"; if (bitOffset) out << " >> " << bitOffset; out << ") & 0x" << hex << mask << dec << ")"; if (field.attribute("type") == "bool") out << " != 0"; if (f.isEnum) out << ")"; if (!f.isStringLength) out << ")"; out << ";\n"; } // evaluate constraints for (QDomElement child = field.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { if (child.tagName() == "constraint") { out << indent << "if (!(" << f.getterName() << "(" << setterArgs << ") " << child.attribute("expr") << ")) {\n"; invalidFound(indent + " ", out, currentOptional); out << indent << "}\n"; } } } offset += bits; } else if (field.tagName() == "if") { if (offset % 8 != 0) qFatal("Ifs should always be byte-aligned"); if (!dynamicOffset) out << indent << "curOffset = " << (offset / 8) << ";\n"; else if (offset) out << indent << "curOffset += " << (offset / 8) << ";\n"; out << indent << "if (" << field.attribute("predicate") << ") {\n"; offset = 0; dynamicOffset = true; sizeCheck(indent + " ", out, field.firstChildElement(), offset, dynamicOffset, currentOptional); for (QDomElement child = field.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { processFieldElement(indent + " ", out, child, offset, dynamicOffset, fieldsMap, setterArgs, currentOptional); } if (offset % 8 != 0) qFatal("Ifs should contain an integer number of bytes"); if (offset) out << indent << " curOffset += " << (offset / 8) << ";\n"; out << indent << "}\n"; offset = 0; sizeCheck(indent, out, field.nextSiblingElement(), offset, dynamicOffset, currentOptional); } else if (field.tagName() == "optional") { if (offset % 8 != 0) qFatal("Optionals should always be byte-aligned"); if (!dynamicOffset) out << indent << "curOffset = " << (offset / 8) << ";\n"; else if (offset) out << indent << "curOffset += " << (offset / 8) << ";\n"; offset = 0; dynamicOffset = true; QString name = ucFirst(field.attribute("name")); QString label = field.attribute("name") + "Failed"; out << indent << "d->has" << name << " = true;\n"; out << indent << "unsigned savedOffset" << name << " = curOffset;\n"; out << indent << "{\n"; sizeCheck(indent + " ", out, field.firstChildElement(), offset, dynamicOffset, name); for (QDomElement child = field.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { processFieldElement(indent + " ", out, child, offset, dynamicOffset, fieldsMap, setterArgs, name); } if (offset % 8 != 0) qFatal("Optionals should contain an integer number of bytes"); if (offset) out << indent << " curOffset += " << (offset / 8) << ";\n"; out << indent << "}\n"; out << label << ":\n"; out << indent << "if (!d->has" << name << ") {\n"; out << indent << " curOffset = savedOffset" << name << ";\n"; out << indent << "}\n"; offset = 0; sizeCheck(indent, out, field.nextSiblingElement(), offset, dynamicOffset, currentOptional); } else if (field.tagName() == "choose") { if (offset % 8 != 0) qFatal("Choose tags should always be byte-aligned"); if (!dynamicOffset) out << indent << "curOffset = " << (offset / 8) << ";\n"; else if (offset) out << indent << "curOffset += " << (offset / 8) << ";\n"; offset = 0; dynamicOffset = true; bool isFirst = true; for (QDomElement childField = field.firstChildElement(); !childField.isNull(); childField = childField.nextSiblingElement()) { if (childField.tagName() != "option") { qFatal("only option tags are allowed inside a choose tag"); } if (isFirst) out << indent; else out << " "; if (!isFirst) out << "else "; isFirst = false; if (childField.hasAttribute("predicate")) out << "if (" << childField.attribute("predicate") << ") "; out << "{\n"; sizeCheck(indent + " ", out, childField.firstChildElement(), offset, dynamicOffset, currentOptional); for (QDomElement child = childField.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { processFieldElement(indent + " ", out, child, offset, dynamicOffset, fieldsMap, setterArgs, currentOptional); } if (offset % 8 != 0) qFatal("options should contain an integer number of bytes"); if (offset) out << indent << " curOffset += " << (offset / 8) << ";\n"; out << indent << "}"; offset = 0; } out << "\n"; sizeCheck(indent, out, field.nextSiblingElement(), offset, dynamicOffset, currentOptional); } else if (field.tagName() == "array") { if (offset % 8 != 0) qFatal("Arrays should always be byte-aligned"); if (!dynamicOffset) out << indent << "curOffset = " << (offset / 8) << ";\n"; else if (offset) out << indent << "curOffset += " << (offset / 8) << ";\n"; QString length = field.attribute("length"); if (fieldsMap.contains(length)) length = fieldsMap[length].getterName() + "()"; else { QDomNodeList fields = field.elementsByTagName("field"); for (int i = 0; i < fields.size(); i++) { QDomElement e = fields.at(i).toElement(); QString name = e.attribute("name"); if (!name.startsWith("reserved")) { out << indent << "d->" << name << ".resize(" << length << ");\n"; } } } out << indent << "for (unsigned i = 0, endi = " << length << "; i < endi; ++i) {\n"; offset = 0; dynamicOffset = true; sizeCheck(indent + " ", out, field.firstChildElement(), offset, dynamicOffset, currentOptional); for (QDomElement child = field.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { processFieldElement(indent + " ", out, child, offset, dynamicOffset, fieldsMap, setterArgs + "i, ", currentOptional); } if (offset % 8 != 0) qFatal("Arrays should contain an integer number of bytes"); if (offset) out << indent << " curOffset += " << (offset / 8) << ";\n"; out << indent << "}\n"; offset = 0; sizeCheck(indent, out, field.nextSiblingElement(), offset, dynamicOffset, currentOptional); } } static void processFieldElementForWrite(QString indent, QTextStream& out, QDomElement field, const QMap& fieldsMap, QString getterArgs = QString()) { if (field.tagName() == "field") { QString name = field.attribute("name"); unsigned bits = field.attribute("size").toUInt(); if (!name.startsWith("reserved")) { const Field& f = fieldsMap[name]; if (field.attribute("type") == "bytestring") { out << indent << "out.writeByteString("; } else if (field.attribute("type") == "unicodestring") { out << indent << "out.writeUnicodeStringWithFlags("; } else if (field.attribute("type") == "unicodechars") { out << indent << "out.writeUnicodeString("; } else if (f.type == "QByteArray") { out << indent << "out.writeBlob("; } else if (f.type == "QUuid") { out << indent << "// TODO "; } else if (field.attribute("type") == "bool" || field.attribute("type") == "unsigned" || f.isEnum) { out << indent << "out.writeUnsigned(" << bits << ", "; } else if (field.attribute("type") == "signed") { out << indent << "out.writeSigned(" << bits << ", "; } else if (field.attribute("type") == "float") { out << indent << "out.writeFloat(" << bits << ", "; } else if (field.attribute("type") == "fixed") { out << indent << "// TODO "; } else { out << f.type; } if (f.isStringLength) { // TODO: figure out length from string const Field& f2 = fieldsMap[f.lengthFor]; out << f2.getterName() << "(" << getterArgs << ").length()"; } else { out << f.getterName() << "(" << getterArgs << ")"; } out << ");\n"; } else { out << indent << "out.writeUnsigned(" << bits << ", "; if (field.hasAttribute("default")) { out << field.attribute("default"); } else { out << "0"; } out << ");\n"; } } else if (field.tagName() == "if") { out << indent << "if (" << field.attribute("predicate") << ") {\n"; for (QDomElement e = field.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) processFieldElementForWrite(indent + " ", out, e, fieldsMap, getterArgs); out << indent << "}\n"; } else if (field.tagName() == "choose") { bool isFirst = true; for (QDomElement childField = field.firstChildElement(); !childField.isNull(); childField = childField.nextSiblingElement()) { if (isFirst) out << indent; else out << " "; if (!isFirst) out << "else "; isFirst = false; if (childField.hasAttribute("predicate")) out << "if (" << childField.attribute("predicate") << ") "; out << "{\n"; for (QDomElement child = childField.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { processFieldElementForWrite(indent + " ", out, child, fieldsMap, getterArgs); } out << indent << "}"; } out << "\n"; } else if (field.tagName() == "array") { QString length = field.attribute("length"); if (fieldsMap.contains(length)) length = fieldsMap[length].getterName() + "()"; else if (field.firstChildElement().hasAttribute("name")) length = "d->" + field.firstChildElement().attribute("name") + ".size()"; out << indent << "for (unsigned i = 0, endi = " << length << "; i < endi; ++i) {\n"; for (QDomElement e = field.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) processFieldElementForWrite(indent + " ", out, e, fieldsMap, "i"); out << indent << "}\n"; } else if (field.tagName() == "optional") { out << indent << "if (has" << ucFirst(field.attribute("name")) << "()) {\n"; for (QDomElement e = field.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) processFieldElementForWrite(indent + " ", out, e, fieldsMap, getterArgs); out << indent << "}\n"; } } static void processFieldElementForDump(QString indent, QTextStream& out, QDomElement field, const QMap& fieldsMap, QString getterArgs = QString()) { if (field.tagName() == "field") { QString name = field.attribute("name"); if (!name.startsWith("reserved")) { const Field& f = fieldsMap[name]; if (!f.isStringLength) { out << indent << "out << \""; if (getterArgs.length() == 0) { out << QString(19 - name.length(), ' '); out << ucFirst(name) << " : \" << "; } else { out << QString(15 - name.length(), ' '); out << ucFirst(name); out << " \" << std::setw(3) << " << getterArgs << " <<\" : \" << "; } if (f.isEnum) out << lcFirst(f.type) << "ToString(" << f.getterName() << "(" << getterArgs << "))"; else out << f.getterName() << "(" << getterArgs << ")"; out << " << std::endl;\n"; } } } else if (field.tagName() == "if") { out << indent << "if (" << field.attribute("predicate") << ") {\n"; for (QDomElement e = field.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) processFieldElementForDump(indent + " ", out, e, fieldsMap, getterArgs); out << indent << "}\n"; } else if (field.tagName() == "choose") { bool isFirst = true; for (QDomElement childField = field.firstChildElement(); !childField.isNull(); childField = childField.nextSiblingElement()) { if (isFirst) out << indent; else out << " "; if (!isFirst) out << "else "; isFirst = false; if (childField.hasAttribute("predicate")) out << "if (" << childField.attribute("predicate") << ") "; out << "{\n"; for (QDomElement child = childField.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { processFieldElementForDump(indent + " ", out, child, fieldsMap, getterArgs); } out << indent << "}"; } out << "\n"; } else if (field.tagName() == "array") { QString length = field.attribute("length"); if (fieldsMap.contains(length)) length = fieldsMap[length].getterName() + "()"; else if (field.firstChildElement().hasAttribute("name")) length = "d->" + field.firstChildElement().attribute("name") + ".size()"; out << indent << "for (unsigned i = 0, endi = " << length << "; i < endi; ++i) {\n"; for (QDomElement e = field.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) processFieldElementForDump(indent + " ", out, e, fieldsMap, "i"); out << indent << "}\n"; } else if (field.tagName() == "optional") { out << indent << "if (has" << ucFirst(field.attribute("name")) << "()) {\n"; for (QDomElement e = field.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) processFieldElementForDump(indent + " ", out, e, fieldsMap, getterArgs); out << indent << "}\n"; } } void processEnumsForImplementation(QDomNodeList fieldList, QString className, QTextStream& out) { for (int i = 0; i < fieldList.size(); i++) { QDomElement f = fieldList.at(i).toElement(); QDomNodeList enumNodes = f.elementsByTagName("enum"); if (enumNodes.size()) { QString name = ucFirst(f.attribute("name")); out << "QString " << className << "::" << lcFirst(f.attribute("name")) << "ToString(" << name << " " << f.attribute("name") << ")\n{\n" << " switch (" << f.attribute("name") << ") {\n"; for (int j = 0; j < enumNodes.size(); j++) { QDomElement en = enumNodes.at(j).toElement(); out << " case " << en.attribute("name") << ": return QString(\"" << en.attribute("name") << "\");\n"; } out << " default: return QString(\"Unknown: %1\").arg(" << f.attribute("name") << ");\n }\n}\n\n"; } } } void processRecordForImplementation(QDomElement e, QTextStream& out) { QString className = e.attribute("name") + "Record"; bool containsStrings = false; QMap fieldsMap = getFields(e, &containsStrings); QList fields = fieldsMap.values(); out << "// ========== " << className << " ==========\n\n"; // id field out << "const unsigned " << className << "::id = " << e.attribute("id") << ";\n\n"; // private class out << "class " << className << "::Private\n{\n" << "public:\n"; foreach(const Field& f, fields) { if (f.isArray) out << " std::vector<" << f.type << "> " << f.name << ";\n"; else if (!f.isStringLength) out << " " << f.type << " " << f.name << ";\n"; } // optional groups QDomNodeList optionalNodes = e.elementsByTagName("optional"); for (int i = 0; i < optionalNodes.size(); i++) { QDomElement f = optionalNodes.at(i).toElement(); out << " bool has" << ucFirst(f.attribute("name")) << ";\n"; } out << "};\n\n"; // constructor out << className << "::" << className << "(Swinder::Workbook *book)\n"; out << " : Record(book), d(new Private)\n{\n"; foreach(const Field& f, fields) { if (f.isArray || f.isStringLength) continue; QString val; if (!f.defaultValue.isEmpty()) { val = f.defaultValue; } else if (f.type == "unsigned" || f.type == "int") { val = "0"; } else if (f.type == "bool") { val = "false"; } if (!val.isEmpty()) out << " " << f.setterName() << "(" << val << ");\n"; } for (int i = 0; i < optionalNodes.size(); i++) { QDomElement f = optionalNodes.at(i).toElement(); out << " d->has" << ucFirst(f.attribute("name")) << " = false;\n"; } out << "}\n\n"; // destructor out << className << "::~" << className << "()\n{\n delete d;\n}\n\n"; // copy constructor out << className << "::" << className << "( const " << className << "& record )\n" << " : Record(record), d(new Private)\n{\n" << " *this = record;\n}\n\n"; // assignment operator out << className << "& " << className << "::operator=( const " << className << "& record )\n{\n" << " *d = *record.d;\n" << " return *this;\n}\n\n"; // enums QDomNodeList fieldNodes = e.elementsByTagName("field"); processEnumsForImplementation(fieldNodes, className, out); QDomNodeList cfieldNodes = e.elementsByTagName("computedField"); processEnumsForImplementation(cfieldNodes, className, out); QDomNodeList typeNodes = e.elementsByTagName("type"); processEnumsForImplementation(typeNodes, className, out); // getters and setters foreach(const Field& f, fields) { if (f.isStringLength) continue; if (f.isEnum) out << className << "::"; out << f.type << " " << className << "::" << f.getterName() << "("; if (f.isArray) out << " unsigned index "; out << ") const\n{\n return d->" << f.name; if (f.isArray) out << "[index]"; out << ";\n}\n\n"; out << "void " << className << "::" << f.setterName() << "("; if (f.isArray) out << " unsigned index, "; out << f.type << " " << f.name << " )\n{\n d->" << f.name; if (f.isArray) out << "[index]"; out << " = " << f.name << ";\n"; if (f.isArrayLength) { foreach(const Field& af, f.arrayFields) { out << " d->" << af.name << ".resize(" << f.name << ");\n"; } } out << "}\n\n"; } // computed fields for (int i = 0; i < cfieldNodes.size(); i++) { QDomElement f = cfieldNodes.at(i).toElement(); QString type = f.attribute("ctype"); if (type.isEmpty()) type = className + "::" + ucFirst(f.attribute("name")); out << type << " " << className << "::" << f.attribute("name") << "() const\n{\n" << " return " << f.attribute("value") << ";\n}\n\n"; } // array lengths QDomNodeList arrayNodes = e.elementsByTagName("array"); for (int i = 0; i < arrayNodes.size(); i++) { QDomElement f = arrayNodes.at(i).toElement(); if (f.hasAttribute("lengthField")) { QString name = f.attribute("lengthField"); QDomNodeList afields = f.elementsByTagName("field"); QDomElement af1 = afields.at(0).toElement(); out << "unsigned " << className << "::" << lcFirst(name) << "() const\n{\n"; out << " return d->" << af1.attribute("name") << ".size();\n}\n\n"; out << "void " << className << "::set" << ucFirst(name) << "( unsigned " << name << " )\n{\n"; for (int j = 0; j < afields.size(); j++) { QDomElement af = afields.at(j).toElement(); if (af.attribute("name").startsWith("reserved")) continue; out << " d->" << af.attribute("name") << ".resize(" << name << ");\n"; } out << "}\n\n"; } } // optional groups for (int i = 0; i < optionalNodes.size(); i++) { QDomElement f = optionalNodes.at(i).toElement(); out << "bool " << className << "::has" << ucFirst(f.attribute("name")) << "() const\n{\n"; out << " return d->has" << ucFirst(f.attribute("name")) << ";\n}\n\n"; out << "void " << className << "::setHas" << ucFirst(f.attribute("name")) << "( bool value )\n{\n"; out << " d->has" << ucFirst(f.attribute("name")) << " = value;\n}\n\n"; } // setData method bool hasFields = !fieldNodes.isEmpty(); if (hasFields) { out << "void " << className << "::setData( unsigned size, const unsigned char* data, const unsigned int* )\n{\n"; } else { out << "void " << className << "::setData( unsigned size, const unsigned char*, const unsigned int* )\n{\n"; } out << " setRecordSize(size);\n\n"; if (e.elementsByTagName("if").size() > 0 || e.elementsByTagName("array").size() > 0 || containsStrings || e.elementsByTagName("optional").size() > 0) out << " unsigned curOffset;\n"; if (containsStrings) { out << " bool stringLengthError = false;\n" << " unsigned stringSize;\n"; } unsigned offset = 0; bool dynamicOffset = false; sizeCheck(" ", out, e.firstChildElement(), offset, dynamicOffset, ""); for (QDomElement child = e.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) processFieldElement(" ", out, child, offset, dynamicOffset, fieldsMap); out << "}\n\n"; // writeData method if (hasFields) { out << "void " << className << "::writeData( XlsRecordOutputStream& out ) const\n{\n"; } else { out << "void " << className << "::writeData( XlsRecordOutputStream& ) const\n{\n"; } for (QDomElement child = e.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) processFieldElementForWrite(" ", out, child, fieldsMap); out << "}\n\n"; // dump method out << "void " << className << "::dump( std::ostream& out ) const\n{\n" << " out << \"" << e.attribute("name") << "\" << std::endl;\n"; for (QDomElement child = e.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) processFieldElementForDump(" ", out, child, fieldsMap); out << "}\n\n"; // creator function out << "static Record* create" << className << "(Swinder::Workbook *book)\n{\n return new " << className << "(book);\n}\n\n"; } int main(int argc, char** argv) { QCoreApplication app(argc, argv); QDomDocument doc("records"); QFile f; if (argc <= 1) { f.setFileName("records.xml"); } else { f.setFileName(argv[1]); } if (!f.open(QIODevice::ReadOnly)) qFatal("Error opening file"); QString errorMsg; int errorLine; int errorCol; if (!doc.setContent(&f, &errorMsg, &errorLine, &errorCol)) { f.close(); errorMsg = "Error parsing file: " + errorMsg + "\n"; - errorMsg += QString::fromAscii("In line ") + QString::number(errorLine) - + QString::fromAscii(", column ") + QString::number(errorCol); - qFatal("%s", errorMsg.toAscii().constData()); + errorMsg += QLatin1String("In line ") + QString::number(errorLine) + + QLatin1String(", column ") + QString::number(errorCol); + qFatal("%s", errorMsg.toLatin1().constData()); } f.close(); QFile hFile("records.h"); hFile.open(QIODevice::WriteOnly); QTextStream hOut(&hFile); QFile cppFile("records.cpp"); cppFile.open(QIODevice::WriteOnly); QTextStream cppOut(&cppFile); hOut << "// This file was automatically generated from records.xml\n" << "#ifndef SWINDER_RECORDS_H\n" << "#define SWINDER_RECORDS_H\n\n" << "#include \"utils.h\"\n\n" << "namespace Swinder {\n\n" << "void registerRecordClasses();\n\n"; cppOut << "// This file was automatically generated from records.xml\n" << "#include \"records.h\"\n" << "#include \n" << "#include \n" << "#include \"XlsRecordOutputStream.h\"\n\n" << "namespace Swinder {\n\n"; QDomNodeList records = doc.elementsByTagName("record"); for (int i = 0; i < records.size(); i++) { QDomElement e = records.at(i).toElement(); processRecordForHeader(e, hOut); processRecordForImplementation(e, cppOut); } cppOut << "void registerRecordClasses()" << endl << "{" << endl; for (int i = 0; i < records.size(); i++) { QDomElement e = records.at(i).toElement(); cppOut << " RecordRegistry::registerRecordClass(" << e.attribute("name") << "Record::id, create" << e.attribute("name") << "Record);\n"; } cppOut << "}\n\n"; hOut << "} // namespace Swinder\n\n"; hOut << "#endif // SWINDER_RECORDS_H\n"; cppOut << "} // namespace Swinder\n"; } diff --git a/filters/sheets/excel/sidewinder/utils.cpp b/filters/sheets/excel/sidewinder/utils.cpp index d81a42477bf..9d57918a8ae 100644 --- a/filters/sheets/excel/sidewinder/utils.cpp +++ b/filters/sheets/excel/sidewinder/utils.cpp @@ -1,344 +1,344 @@ /* Swinder - Portable library for spreadsheet Copyright (C) 2003-2005 Ariya Hidayat Copyright (C) 2006,2009 Marijn Kruisselbrink Copyright (C) 2009,2010 Sebastian Sauer 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 "utils.h" #include #include #include "XlsRecordOutputStream.h" namespace Swinder { // Returns A for 1, B for 2, C for 3, etc. QString columnName(uint column) { QString s; unsigned digits = 1; unsigned offset = 0; for (unsigned limit = 26; column >= limit + offset; limit *= 26, digits++) offset += limit; for (unsigned col = column - offset; digits; --digits, col /= 26) s.prepend(QChar('A' + (col % 26))); return s; } QString encodeSheetName(const QString& name) { QString sheetName = name; if (sheetName.contains(' ') || sheetName.contains('.') || sheetName.contains('\'')) sheetName = '\'' + sheetName.replace('\'', "''") + '\''; return sheetName; } QString encodeAddress(const QString& sheetName, uint column, uint row) { return QString("%1.%2%3").arg(encodeSheetName(sheetName)).arg(columnName(column)).arg(row+1); } QString encodeAddress(const QString& sheetName, const QRect &rect) { int startColumn = rect.left(); int startRow = rect.top(); int endColumn = rect.right(); int endRow = rect.bottom(); if (rect.width() == 1 && rect.height() == 1) return encodeAddress(sheetName, startColumn, startRow); return QString("%1.%2%3:%4%5").arg(encodeSheetName(sheetName)).arg(columnName(startColumn)).arg(startRow+1).arg(columnName(endColumn)).arg(endRow+1); } QString readByteString(const void* p, unsigned length, unsigned maxSize, bool* error, unsigned* size) { const unsigned char* data = reinterpret_cast(p); if (size) *size = length; if (length > maxSize) { if (*error) *error = true; return QString(); } char* buffer = new char[length+1]; memcpy(buffer, data, length); buffer[length] = 0; QString str(buffer); delete[] buffer; return str; } QString readTerminatedUnicodeChars(const void* p, unsigned* pSize, unsigned maxSize, bool* error) { const unsigned char* data = reinterpret_cast(p); QString str; unsigned offset = 0; unsigned size = offset; while (true) { if (size+2 > maxSize) { if (*error) *error = true; return QString(); } unsigned uchar = readU16(data + offset); size += 2; if (uchar == '\0') break; offset += 2; str.append(QChar(uchar)); } if (pSize) *pSize = size; return str; } QString readUnicodeChars(const void* p, unsigned length, unsigned maxSize, bool* error, unsigned* pSize, unsigned continuePosition, unsigned offset, bool unicode, bool asianPhonetics, bool richText) { const unsigned char* data = reinterpret_cast(p); if (maxSize < 1) { if (*error) *error = true; return QString(); } unsigned formatRuns = 0; unsigned asianPhoneticsSize = 0; if (richText) { if (offset + 2 > maxSize) { if (*error) *error = true; return QString(); } formatRuns = readU16(data + offset); offset += 2; } if (asianPhonetics) { if (offset + 4 > maxSize) { if (*error) *error = true; return QString(); } asianPhoneticsSize = readU32(data + offset); offset += 4; } // find out total bytes used in this string unsigned size = offset; if (richText) size += (formatRuns * 4); if (asianPhonetics) size += asianPhoneticsSize; if (size > maxSize) { if (*error) *error = true; return QString(); } QString str; for (unsigned k = 0; k < length; k++) { unsigned uchar; if (unicode) { if (size + 2 > maxSize) { if (*error) *error = true; return QString(); } uchar = readU16(data + offset); offset += 2; size += 2; } else { if (size + 1 > maxSize) { if (*error) *error = true; return QString(); } uchar = data[offset++]; size++; } str.append(QChar(uchar)); if (offset == continuePosition && k < length - 1) { if (size + 1 > maxSize) { if (*error) *error = true; return QString(); } unicode = data[offset] & 1; size++; offset++; } } if (pSize) *pSize = size; return str; } QString readUnicodeString(const void* p, unsigned length, unsigned maxSize, bool* error, unsigned* pSize, unsigned continuePosition) { const unsigned char* data = reinterpret_cast(p); if (maxSize < 1) { if (*error) *error = true; return QString(); } unsigned char flags = data[0]; unsigned offset = 1; bool unicode = flags & 0x01; bool asianPhonetics = flags & 0x04; bool richText = flags & 0x08; return readUnicodeChars(p, length, maxSize, error, pSize, continuePosition, offset, unicode, asianPhonetics, richText); } QString readUnicodeCharArray(const void* p, unsigned length, unsigned maxSize, bool* error, unsigned* pSize, unsigned continuePosition) { if (length == unsigned(-1)) { // null terminated string return readTerminatedUnicodeChars(p, pSize, maxSize, error); } else { return readUnicodeChars(p, length, maxSize, error, pSize, continuePosition, 0, true, false, false); } } std::ostream& operator<<(std::ostream& s, const QString& ustring) { s << qPrintable(ustring); return s; } std::ostream& operator<<(std::ostream& s, const QByteArray& d) { s << std::hex << std::setfill('0'); for (int i = 0; i < d.size(); i++) s << " " << std::setw(2) << int((unsigned char)d[i]); return s << std::dec; } std::ostream& operator<<(std::ostream& s, const QUuid& uuid) { - return s << uuid.toString().toAscii().data(); + return s << uuid.toString().toLatin1().constData(); } Value errorAsValue(int errorCode) { Value result(Value::Error); switch (errorCode) { case 0x00: result = Value::errorNULL(); break; case 0x07: result = Value::errorDIV0(); break; case 0x0f: result = Value::errorVALUE(); break; case 0x17: result = Value::errorREF(); break; case 0x1d: result = Value::errorNAME(); break; case 0x24: result = Value::errorNUM(); break; case 0x2A: result = Value::errorNA(); break; default: break; } return result; } // ========== base record ========== const unsigned int Record::id = 0; // invalid of-course Record::Record(Workbook *book) { m_workbook = book; stream_position = 0; ver = Excel97; valid = true; m_size = 0; } Record::~Record() { } Record* Record::create(unsigned type, Workbook *book) { return RecordRegistry::createRecord(type, book); } void Record::setPosition(unsigned pos) { stream_position = pos; } unsigned Record::position() const { return stream_position; } void Record::setData(unsigned, const unsigned char*, const unsigned int*) { } void Record::writeData(XlsRecordOutputStream &out) const { Q_UNUSED(out); fprintf(stderr, "ERROR! writeData not implemented for record type %u\n", rtti()); } void Record::dump(std::ostream&) const { // nothing to dump } bool Record::isValid() const { return valid; } void Record::setIsValid(bool isValid) { valid = isValid; } void RecordRegistry::registerRecordClass(unsigned id, RecordFactory factory) { instance()->records[id] = factory; } void RecordRegistry::registerRecordClass(unsigned id, RecordFactoryWithArgs factory, void* args) { instance()->recordsWithArgs[id] = factory; instance()->recordArgs[id] = args; } void RecordRegistry::unregisterRecordClass(unsigned id) { instance()->records.erase(id); instance()->recordsWithArgs.erase(id); instance()->recordArgs.erase(id); } Record* RecordRegistry::createRecord(unsigned id, Workbook *book) { RecordRegistry* q = instance(); std::map::iterator it = q->records.find(id); if (it != q->records.end()) { return it->second(book); } std::map::iterator it2 = q->recordsWithArgs.find(id); if (it2 != q->recordsWithArgs.end()) { return it2->second(book, q->recordArgs[id]); } return 0; } RecordRegistry* RecordRegistry::instance() { static RecordRegistry* sinstance = 0; if (!sinstance) sinstance = new RecordRegistry(); return sinstance; } } // namespace Swinder diff --git a/filters/sheets/xlsx/XlsxUtils.h b/filters/sheets/xlsx/XlsxUtils.h index cc0ec5939e6..de3bef73e09 100644 --- a/filters/sheets/xlsx/XlsxUtils.h +++ b/filters/sheets/xlsx/XlsxUtils.h @@ -1,62 +1,62 @@ /* * Copyright (c) 2010 Sebastian Sauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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. */ #ifndef UTILS_H #define UTILS_H #include #include // translate the range-character to a number int rangeCharToInt(char c) { return (c >= 'A' && c <= 'Z') ? (c - 'A' + 1) : -1; } // translates the range-string into a number int rangeStringToInt(const QString &string) { int result = 0; const int size = string.size(); for ( int i = 0; i < size; i++ ) - result += rangeCharToInt( string[i].toAscii() ) * pow( 10.0, ( size - i - 1 ) ); + result += rangeCharToInt( string[i].toLatin1() ) * pow( 10.0, ( size - i - 1 ) ); return result; } // splits a given cellrange like Sheet1.D2:Sheet1.F2, Sheet1.D2:F2, D2:F2 or D2 into its parts QPair splitCellRange(QString range) { range.remove( "$" ); // remove "fixed" character if(range.startsWith('[') && range.endsWith(']')) range = range.mid(1, range.length() - 2); // remove [] QPair result; const bool isPoint = !range.contains( ':' ); QRegExp regEx = isPoint ? QRegExp( "(.*)(\\.|\\!)([A-Z]+)([0-9]+)" ) : QRegExp ( "(.*)(\\.|\\!)([A-Z]+)([0-9]+)\\:(|.*\\.)([A-Z]+)([0-9]+)" ); if ( regEx.indexIn( range ) >= 0 ) { const QString sheetName = regEx.cap( 1 ); QPoint topLeft( rangeStringToInt( regEx.cap(3) ), regEx.cap(4).toInt() ); if ( isPoint ) { result = QPair(sheetName, QRect(topLeft,QSize(1,1))); } else { QPoint bottomRight( rangeStringToInt( regEx.cap(6) ), regEx.cap(7).toInt() ); result = QPair(sheetName, QRect(topLeft,bottomRight)); } } return result; } #endif diff --git a/filters/sheets/xlsx/XlsxXmlChartReader.cpp b/filters/sheets/xlsx/XlsxXmlChartReader.cpp index 73c3eac7d28..ef2d2f6e0a3 100644 --- a/filters/sheets/xlsx/XlsxXmlChartReader.cpp +++ b/filters/sheets/xlsx/XlsxXmlChartReader.cpp @@ -1,3505 +1,3505 @@ /* * This file is part of Office 2007 Filters for Calligra * * Copyright (C) 2010 Sebastian Sauer * Copyright (c) 2010 Carlos Licea * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Suresh Chande suresh.chande@nokia.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "XlsxXmlChartReader.h" #include "Charting.h" #include "ChartExport.h" #include "XlsxUtils.h" #include "NumberFormatParser.h" #define MSOOXML_CURRENT_NS "c" #define MSOOXML_CURRENT_CLASS XlsxXmlChartReader #define BIND_READ_CLASS MSOOXML_CURRENT_CLASS #include #include #include #include class SpPr { public: SpPr() {} }; class NumCache { public: int m_ptCount; QVector< QString > m_cache; QString formatCode; NumCache() : m_ptCount(0) {} }; class StrCache { public: int m_ptCount; QVector< QString > m_cache; StrCache() : m_ptCount(0) {} }; class NumRef { public: QString m_f; NumCache m_numCache; }; class NumLit { public: int m_ptCount; QVector< QString > m_cache; NumLit() : m_ptCount(0) {} }; class StrLit { public: int m_ptCount; QVector< QString > m_cache; StrLit() : m_ptCount(0) {} }; class StrRef { public: QString m_f; StrCache m_strCache; }; class Tx { public: StrRef m_strRef; QString writeRefToInternalTable(XlsxXmlChartReader *chartReader); }; QString Tx::writeRefToInternalTable(XlsxXmlChartReader *chartReader) { chartReader->WriteIntoInternalTable(m_strRef.m_f,m_strRef.m_strCache.m_cache,KoGenStyle::NumericTextStyle); return m_strRef.m_f; } class Cat { public: NumRef m_numRef; StrRef m_strRef; NumLit m_numLit; StrLit m_strLit; QString writeRefToInternalTable(XlsxXmlChartReader *chartReader); QString writeLitToInternalTable(XlsxXmlChartReader *chartReader); }; QString Cat::writeRefToInternalTable(XlsxXmlChartReader *chartReader) { if (m_numRef.m_numCache.m_ptCount != 0) { KoGenStyle::Type formatType = KoGenStyle::NumericNumberStyle; if (!m_numRef.m_numCache.formatCode.isEmpty() && m_numRef.m_numCache.formatCode != "General") { KoGenStyle style = NumberFormatParser::parse(m_numRef.m_numCache.formatCode); formatType = style.type(); } chartReader->WriteIntoInternalTable(m_numRef.m_f,m_numRef.m_numCache.m_cache, formatType, m_numRef.m_numCache.formatCode ); return m_numRef.m_f; } chartReader->WriteIntoInternalTable(m_strRef.m_f,m_strRef.m_strCache.m_cache,KoGenStyle::NumericTextStyle); return m_strRef.m_f; } QString Cat::writeLitToInternalTable(XlsxXmlChartReader *chartReader) { if (m_numLit.m_ptCount != 0) { return chartReader->AlocateAndWriteIntoInternalTable(m_numLit.m_cache,KoGenStyle::NumericNumberStyle); } return chartReader->AlocateAndWriteIntoInternalTable(m_strLit.m_cache,KoGenStyle::NumericTextStyle); } class Val { public: NumRef m_numRef; NumLit m_numLit; QString writeRefToInternalTable(XlsxXmlChartReader *chartReader); QString writeLitToInternalTable(XlsxXmlChartReader *chartReader); }; QString Val::writeRefToInternalTable(XlsxXmlChartReader *chartReader) { chartReader->WriteIntoInternalTable(m_numRef.m_f,m_numRef.m_numCache.m_cache,KoGenStyle::NumericNumberStyle); return m_numRef.m_f; } QString Val::writeLitToInternalTable(XlsxXmlChartReader *chartReader) { return chartReader->AlocateAndWriteIntoInternalTable(m_numLit.m_cache,KoGenStyle::NumericNumberStyle); } class XVal { public: NumRef m_numRef; StrRef m_strRef; NumLit m_numLit; StrLit m_strLit; QString writeRefToInternalTable(XlsxXmlChartReader *chartReader); QString writeLitToInternalTable(XlsxXmlChartReader *chartReader); }; QString XVal::writeRefToInternalTable(XlsxXmlChartReader *chartReader) { if (m_numRef.m_numCache.m_ptCount != 0) { chartReader->WriteIntoInternalTable(m_numRef.m_f,m_numRef.m_numCache.m_cache,KoGenStyle::NumericNumberStyle); return m_numRef.m_f; } chartReader->WriteIntoInternalTable(m_strRef.m_f,m_strRef.m_strCache.m_cache,KoGenStyle::NumericTextStyle); return m_strRef.m_f; } QString XVal::writeLitToInternalTable(XlsxXmlChartReader *chartReader) { if (m_numLit.m_ptCount != 0) { return chartReader->AlocateAndWriteIntoInternalTable(m_numLit.m_cache,KoGenStyle::NumericNumberStyle); } return chartReader->AlocateAndWriteIntoInternalTable(m_strLit.m_cache,KoGenStyle::NumericTextStyle); } class YVal { public: NumRef m_numRef; NumLit m_numLit; QString writeRefToInternalTable(XlsxXmlChartReader *chartReader); QString writeLitToInternalTable(XlsxXmlChartReader *chartReader); }; QString YVal::writeRefToInternalTable(XlsxXmlChartReader *chartReader) { chartReader->WriteIntoInternalTable(m_numRef.m_f,m_numRef.m_numCache.m_cache,KoGenStyle::NumericNumberStyle); return m_numRef.m_f; } QString YVal::writeLitToInternalTable(XlsxXmlChartReader *chartReader) { return chartReader->AlocateAndWriteIntoInternalTable(m_numLit.m_cache,KoGenStyle::NumericNumberStyle); } class BubbleSize { public: NumRef m_numRef; NumLit m_numLit; QString writeRefToInternalTable(XlsxXmlChartReader *chartReader); QString writeLitToInternalTable(XlsxXmlChartReader *chartReader); }; QString BubbleSize::writeRefToInternalTable(XlsxXmlChartReader *chartReader) { chartReader->WriteIntoInternalTable(m_numRef.m_f,m_numRef.m_numCache.m_cache,KoGenStyle::NumericNumberStyle); return m_numRef.m_f; } QString BubbleSize::writeLitToInternalTable(XlsxXmlChartReader *chartReader) { return chartReader->AlocateAndWriteIntoInternalTable(m_numLit.m_cache,KoGenStyle::NumericNumberStyle); } class Ser { }; class ValSeries : public Ser { public: int m_idx; int m_order; Tx m_tx; Cat m_cat; Val m_val; ValSeries() : m_idx(0), m_order(0) {} }; class BubbleSeries : public Ser { public: int m_idx; int m_order; Tx m_tx; XVal m_xVal; YVal m_yVal; BubbleSize m_bubbleSize; BubbleSeries() : m_idx(0), m_order(0) {} }; class ScatterSeries : public Ser { public: int m_idx; int m_order; Tx m_tx; XVal m_xVal; YVal m_yVal; SpPr m_spPr; ScatterSeries() : m_idx(0), m_order(0) {} }; class LineSeries :public ValSeries { public: LineSeries() {} }; class PieSeries :public ValSeries { public: int m_explosion; PieSeries() : m_explosion(0) {} }; class BarSeries :public ValSeries { public: BarSeries() {} }; class AreaSeries :public ValSeries { public: AreaSeries() {} }; class RadarSeries :public ValSeries { public: RadarSeries() {} }; class SurfaceSeries :public ValSeries { public: SurfaceSeries() {} }; class XlsxXmlChartReader::Private { public: Private(); QList m_seriesData; QVariant::Type m_currentType; int *m_currentIdx; int *m_currentOrder; int *m_currentExplosion; Tx *m_currentTx; Cat *m_currentCat; Val *m_currentVal; StrRef *m_currentStrRef; QString *m_currentF; StrCache *m_currentStrCache; int *m_currentPtCount; QVector< QString > *m_currentPtCache; NumRef *m_currentNumRef; NumLit *m_currentNumLit; NumCache *m_currentNumCache; XVal *m_currentXVal; YVal *m_currentYVal; BubbleSize *m_currentBubbleSize; int m_numReadSeries; }; XlsxXmlChartReader::Private::Private ( ) : m_numReadSeries( 0 ) { //sebsauer; hmmmm... does that really make sense? qDeleteAll(m_seriesData); m_seriesData.clear(); } // calculates the column width in pixels int columnWidth(unsigned long col, unsigned long dx = 0, qreal defaultColumnWidth = 8.43) { QFont font("Arial", 10); QFontMetricsF fm(font); const qreal characterWidth = fm.width("h"); defaultColumnWidth *= characterWidth; return (defaultColumnWidth * col) + (dx / 1024.0 * defaultColumnWidth); } // calculates the row height in pixels int rowHeight(unsigned long row, unsigned long dy = 0, qreal defaultRowHeight = 12.75) { return defaultRowHeight * row + dy; } // Returns A for 1, B for 2, C for 3, etc. QString columnName(uint column) { QString s; column = column - 1; unsigned digits = 1; unsigned offset = 0; for (unsigned limit = 26; column >= limit + offset; limit *= 26, digits++) offset += limit; for (unsigned col = column - offset; digits; --digits, col /= 26) s.prepend(QChar('A' + (col % 26))); return s; } XlsxXmlChartReaderContext::XlsxXmlChartReaderContext(KoStore* _storeout, ChartExport* _chartExport) : MSOOXML::MsooXmlReaderContext() , m_storeout(_storeout) , m_chart(_chartExport->chart()) , m_chartExport(_chartExport) { } XlsxXmlChartReaderContext::~XlsxXmlChartReaderContext() { delete m_chart; delete m_chartExport; } XlsxXmlChartReader::XlsxXmlChartReader(KoOdfWriters *writers) : MSOOXML::MsooXmlCommonReader(writers) , m_context(0) , m_currentSeries(0) , m_currentShapeProperties(0) , m_readTxContext( None ) , m_areaContext( ChartArea ) , m_serMarkerDefined(false) , m_autoTitleDeleted(true) , d ( new Private( ) ) { } XlsxXmlChartReader::~XlsxXmlChartReader() { delete d; } //! chart (Chart) /*! ECMA-376, 21.2.2.29, p.3768. Parent elements: - chartSpace (§21.2.2.29) Child elements: - [Done]autoTitleDeleted (Auto Title Is Deleted) §21.2.2.7 - backWall (Back Wall) §21.2.2.11 - dispBlanksAs (Display Blanks As) §21.2.2.42 - extLst (Chart Extensibility) §21.2.2.64 - floor (Floor) §21.2.2.69 - [Done]legend (Legend) §21.2.2.93 - pivotFmts (Pivot Formats) §21.2.2.143 - [Done]plotArea (Plot Area) §21.2.2.145 - plotVisOnly (Plot Visible Only) §21.2.2.146 - showDLblsOverMax (Show Data Labels over Maximum) §21.2.2.180 - sideWall (Side Wall) §21.2.2.191 - [Done]title (Title) §21.2.2.210 - view3D (View In 3D) §21.2.2.228 */ KoFilter::ConversionStatus XlsxXmlChartReader::read(MSOOXML::MsooXmlReaderContext* context) { m_context = dynamic_cast(context); Q_ASSERT(m_context); readNext(); if (!isStartDocument()) { return KoFilter::WrongFormat; } readNext(); if (!expectEl("c:chartSpace")) { return KoFilter::WrongFormat; } while (!atEnd()) { QXmlStreamReader::TokenType tokenType = readNext(); if(tokenType == QXmlStreamReader::Invalid || tokenType == QXmlStreamReader::EndDocument) break; if (isStartElement()) { m_areaContext = ChartArea; TRY_READ_IF(plotArea) ELSE_TRY_READ_IF(title) ELSE_TRY_READ_IF(legend) ELSE_TRY_READ_IF(spPr) ELSE_TRY_READ_IF(txPr) if (qualifiedName() == QLatin1String(QUALIFIED_NAME(autoTitleDeleted))) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) m_autoTitleDeleted = MSOOXML::Utils::convertBooleanAttr(val, true); } if (qualifiedName() == QLatin1String(QUALIFIED_NAME(style))) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) m_context->m_chart->m_style = val.toInt(); } } } if (!m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty()) m_context->m_chart->m_title = "Chart Title"; // static is fine here cause we only need to take care that that number is unique in the // exported ODS file and do not take if the number is continuous or whatever. static int chartNumber = 0; m_context->m_chartExport->m_href = QString("Chart%1").arg(++chartNumber); Charting::Chart* c = m_context->m_chart; if (!c->m_cellRangeAddress.isNull() ) { m_context->m_chartExport->m_cellRangeAddress.clear(); if (!c->m_sheetName.isEmpty()) m_context->m_chartExport->m_cellRangeAddress += c->m_sheetName + '.'; m_context->m_chartExport->m_cellRangeAddress += columnName(c->m_cellRangeAddress.left()) + QString::number(c->m_cellRangeAddress.top()) + ":" + columnName(c->m_cellRangeAddress.right()) + QString::number(c->m_cellRangeAddress.bottom()); } if (m_currentSeries) { m_context->m_chartExport->m_notifyOnUpdateOfRanges = m_currentSeries->m_valuesCellRangeAddress; //m_cellRangeAddress } // the index will by written by the XlsxXmlWorksheetReader //m_context->m_chartExport->saveIndex(body); // write the embedded object file m_context->m_chartExport->saveContent(m_context->m_storeout, manifest); m_context = 0; return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL txPr KoFilter::ConversionStatus XlsxXmlChartReader::read_txPr() { READ_PROLOGUE while( !atEnd() ) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if ( isStartElement() ) if ( qualifiedName() == "a:p" ) read_p(); } READ_EPILOGUE return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL p KoFilter::ConversionStatus XlsxXmlChartReader::read_p() { //READ_PROLOGUE while( !atEnd() ) { readNext(); if ( isEndElement() && qualifiedName() == QLatin1String( "a:p") ) break; if ( isStartElement() ) if ( qualifiedName() == "a:pPr" ) read_pPr(); //TRY_READ_IF_NS(a,pPr); } //READ_EPILOGUE return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL pPr KoFilter::ConversionStatus XlsxXmlChartReader::read_pPr() { //READ_PROLOGUE while( !atEnd() ) { readNext(); if ( isEndElement() && qualifiedName() == QLatin1String( "a:pPr") ) break; if ( isStartElement() ) if ( qualifiedName() == "a:defRPr" ) read_defRPr(); } //READ_EPILOGUE return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL defRPr KoFilter::ConversionStatus XlsxXmlChartReader::read_defRPr() { //READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(sz); bool ok = false; const qreal size = sz.toDouble( &ok ); if ( ok ) { m_context->m_chart->m_textSize = size / 100.0; } while( !atEnd() ) { if ( isEndElement() && qualifiedName() == QLatin1String( "a:defRPr") ) break; readNext(); //BREAK_IF_END_OF(CURRENT_EL) } //READ_EPILOGUE return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL valAx KoFilter::ConversionStatus XlsxXmlChartReader::read_valAx() { READ_PROLOGUE // The logic here is that if the x-axis defines a category then it should be already // set above using the read_catAx else this read_valAx could be either a x-axis or // a y-axis. In that case we just look if there was already a x-axis defined in // which case we know if must be the y-axis or, if not, then it's the a-axis. // This sounds hacky (and it certainly is) but that's how OO.org does it too. bool xAxisAlreadyDefined = !m_context->m_chart->m_verticalCellRangeAddress.isEmpty(); if (!xAxisAlreadyDefined) { foreach(Charting::Axis* axis, m_context->m_chart->m_axes) { if (axis->m_type == Charting::Axis::HorizontalValueAxis) { xAxisAlreadyDefined = true; break; } } } Charting::Axis* axis = new Charting::Axis( xAxisAlreadyDefined ? Charting::Axis::VerticalValueAxis : Charting::Axis::HorizontalValueAxis ); m_context->m_chart->m_axes.push_back( axis ); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(axPos) ) ) { // const QXmlStreamAttributes attrs(attributes()); // TRY_READ_ATTR_WITHOUT_NS(val) // if ( val == QLatin1String( "b" ) ){ // axis->m_type = Charting::Axis::HorizontalValueAxis; // } // else if ( val == QLatin1String( "l" ) ){ // } // } } else if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(majorGridlines) ) ) { axis->m_majorGridlines = Charting::Axis::Gridline( Charting::LineFormat( Charting::LineFormat::Solid ) ); } else if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(numFmt) ) ) { const QXmlStreamAttributes attrs(attributes()); axis->m_numberFormat = attrs.value("formatCode").toString(); } ELSE_TRY_READ_IF(scaling) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL catAx KoFilter::ConversionStatus XlsxXmlChartReader::read_catAx() { READ_PROLOGUE // category-axis or date-axis are always x-axis. They are only defined for the case the // x-axis itself defines a category. If not then the x-axis will be defined via read_valAx. Charting::Axis* axis = new Charting::Axis( Charting::Axis::HorizontalValueAxis ); m_context->m_chart->m_axes.push_back( axis ); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(axPos) ) ) { // const QXmlStreamAttributes attrs(attributes()); // TRY_READ_ATTR_WITHOUT_NS(val) // if ( val == QLatin1String( "b" ) ){ // } // else if ( val == QLatin1String( "l" ) ){ // } } else if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(majorGridlines) ) ) { axis->m_majorGridlines = Charting::Axis::Gridline( Charting::LineFormat( Charting::LineFormat::Solid ) ); } ELSE_TRY_READ_IF(scaling) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL scaling KoFilter::ConversionStatus XlsxXmlChartReader::read_scaling() { READ_PROLOGUE Q_ASSERT(!m_context->m_chart->m_axes.isEmpty()); Charting::Axis* axis = m_context->m_chart->m_axes.last(); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { const QXmlStreamAttributes attrs(attributes()); if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(orientation) ) ) { TRY_READ_ATTR_WITHOUT_NS(val) axis->m_reversed = ( val == QLatin1String( "maxMin" ) ); } else if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(logBase) ) ) { TRY_READ_ATTR_WITHOUT_NS(val) axis->m_logarithmic = ( val.toDouble() >= 2. ); } else if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(max) ) ) { TRY_READ_ATTR_WITHOUT_NS(val) axis->m_maximum = val.toDouble(); axis->m_autoMaximum = false; } else if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(min) ) ) { TRY_READ_ATTR_WITHOUT_NS(val) axis->m_minimum = val.toDouble(); axis->m_autoMinimum = false; } } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL plotArea //! plotArea (Plot Area) /*! ECMA-376, 21.2.2.145, p.3828. Parent elements: - chart (§21.2.2.27) Child elements: - [Done]area3DChart (3D Area Charts) §21.2.2.4 - [Done]areaChart (Area Charts) §21.2.2.5 - [Done]bar3DChart (3D Bar Charts) §21.2.2.15 - [Done]barChart (Bar Charts) §21.2.2.16 - [Done]bubbleChart (Bubble Charts) §21.2.2.20 - [Done]catAx (Category Axis Data) §21.2.2.25 - dateAx (Date Axis) §21.2.2.39 - [Done]doughnutChart (Doughnut Charts) §21.2.2.50 - dTable (Data Table) §21.2.2.54 - extLst (Chart Extensibility) §21.2.2.64 - layout (Layout) §21.2.2.88 - [Done]line3DChart (3D Line Charts) §21.2.2.96 - [Done]lineChart (Line Charts) §21.2.2.97 - [Done]ofPieChart (Pie of Pie or Bar of Pie Charts) §21.2.2.126 - [Done]pie3DChart (3D Pie Charts) §21.2.2.140 - [Done]pieChart (Pie Charts) §21.2.2.141 - [Done]radarChart (Radar Charts) §21.2.2.153 - [Done]scatterChart (Scatter Charts) §21.2.2.161 - serAx (Series Axis) §21.2.2.175 - [Done]spPr (Shape Properties) §21.2.2.197 - [Done]stockChart (Stock Charts) §21.2.2.198 - [Done]surface3DChart (3D Surface Charts) §21.2.2.203 - [Done]surfaceChart (Surface Charts) §21.2.2.204 - [Done]valAx (Value Axis) §21.2.2.226 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_plotArea() { m_areaContext = PlotArea; if (!m_context->m_chart->m_plotArea) { m_context->m_chart->m_plotArea = new Charting::PlotArea(); } READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(spPr) ELSE_TRY_READ_IF(valAx) // x-axis or y-axis ELSE_TRY_READ_IF(catAx) // x-axis //ELSE_TRY_READ_IF(serAx) // z-axis ELSE_TRY_READ_IF(pieChart) ELSE_TRY_READ_IF(pie3DChart) ELSE_TRY_READ_IF(ofPieChart) ELSE_TRY_READ_IF(doughnutChart) ELSE_TRY_READ_IF(areaChart) ELSE_TRY_READ_IF(area3DChart) ELSE_TRY_READ_IF(barChart) ELSE_TRY_READ_IF(bar3DChart) ELSE_TRY_READ_IF(lineChart) ELSE_TRY_READ_IF(line3DChart) ELSE_TRY_READ_IF(scatterChart) ELSE_TRY_READ_IF(radarChart) ELSE_TRY_READ_IF(surfaceChart) ELSE_TRY_READ_IF(surface3DChart) ELSE_TRY_READ_IF(bubbleChart) ELSE_TRY_READ_IF(stockChart) SKIP_UNKNOWN } } READ_EPILOGUE m_areaContext = ChartArea; } #undef CURRENT_EL #define CURRENT_EL title /*! Read the horizontal value. */ KoFilter::ConversionStatus XlsxXmlChartReader::read_title() { m_readTxContext = Title; READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if(QUALIFIED_NAME_IS(tx)) { TRY_READ(chartText_Tx) } } } m_readTxContext = None; READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL val //! val (Values) /*! ECMA-376, 21.2.2.224, p.3867. Parent elements: - ser §21.2.2.168 - ser §21.2.2.170 - ser §21.2.2.174 - ser §21.2.2.171 - ser §21.2.2.172 - ser §21.2.2.169 - ser §21.2.2.167 - ser §21.2.2.173 Child elements: - numLit (Number Literal) §21.2.2.122 - [Done] numRef (Number Reference) §21.2.2.123 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_val() { READ_PROLOGUE d->m_currentNumRef = &d->m_currentVal->m_numRef; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(numRef) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL xVal //! xVal (X Values) /*! ECMA-376, 21.2.2.234, p.3872. Parent elements: - ser §21.2.2.174 - ser §21.2.2.167 Child elements: - multiLvlStrRef (Multi Level String Reference) §21.2.2.115 - numLit (Number Literal) §21.2.2.122 - [Done]numRef (Number Reference) §21.2.2.123 - strLit (String Literal) §21.2.2.200 - [Done]strRef (String Reference) §21.2.2.201 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_xVal() { READ_PROLOGUE d->m_currentNumRef = &d->m_currentXVal->m_numRef; d->m_currentStrRef = &d->m_currentXVal->m_strRef; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(numRef) ELSE_TRY_READ_IF(strRef) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL yVal //! yVal (Y Values) /*! ECMA-376, 21.2.2.237, p.3873. Parent elements: - ser §21.2.2.174 - ser §21.2.2.167 Child elements: - numLit (Number Literal) §21.2.2.122 - numRef (Number Reference) §21.2.2.123 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_yVal() { READ_PROLOGUE d->m_currentNumRef = &d->m_currentYVal->m_numRef; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(numRef) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL cat //! cat (Category Axis Data) /*! ECMA-376, 21.2.2.24, p.3766. Parent elements: - ser §21.2.2.168 - ser §21.2.2.170 - ser §21.2.2.174 - ser §21.2.2.171 - ser §21.2.2.172 - ser §21.2.2.169 - ser §21.2.2.167 - ser §21.2.2.173 Child elements: - multiLvlStrRef (Multi Level String Reference) §21.2.2.115 - numLit (Number Literal) §21.2.2.122 - [Done]numRef (Number Reference) §21.2.2.123 - strLit (String Literal) §21.2.2.200 - strRef (String Reference) §21.2.2.201 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_cat() { READ_PROLOGUE d->m_currentStrRef = &d->m_currentCat->m_strRef; d->m_currentNumRef = &d->m_currentCat->m_numRef; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(strRef) ELSE_TRY_READ_IF(multiLvlStrRef) ELSE_TRY_READ_IF(numRef) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL tx //! tx (Chart Text) /*! ECMA-376, 21.2.2.215, p.3863. Parent elements: - dispUnitsLbl (§21.2.2.46) - dLbl (§21.2.2.47) - title (§21.2.2.210) - trendlineLbl (§21.2.2.212) Child elements: - rich (Rich Text) §21.2.2.156 - strRef (String Reference) §21.2.2.201 */ /*! This element specifies text to use on a chart, including rich text formatting. */ KoFilter::ConversionStatus XlsxXmlChartReader::read_chartText_Tx() { READ_PROLOGUE2(chartText_Tx) enum { Start, InStrRef, InRichText } state; state = Start; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) switch(state) { case Start: if (qualifiedName() == QLatin1String(QUALIFIED_NAME(strRef))) state = isStartElement() ? InStrRef : Start; else if (qualifiedName() == QLatin1String(QUALIFIED_NAME(rich))) state = isStartElement() ? InRichText : Start; break; case InStrRef: // plaintext within a series // if (isStartElement() && !m_currentSeriesData->m_datasetValue.contains(Charting::Value::SeriesLegendOrTrendlineName)) { // if (qualifiedName() == QLatin1String(QUALIFIED_NAME(f))) { // Charting::Value* v = new Charting::Value(Charting::Value::SeriesLegendOrTrendlineName, Charting::Value::CellRange, readElementText()); // m_currentSeriesData->m_datasetValue[v->m_dataId] = v; // } else if (qualifiedName() == QLatin1String(QUALIFIED_NAME(v))) { // Charting::Value* v = new Charting::Value(Charting::Value::SeriesLegendOrTrendlineName, Charting::Value::TextOrValue, readElementText()); // m_currentSeriesData->m_datasetValue[v->m_dataId] = v; // } // } break; case InRichText: // richtext means the title text // we extract the text from the richtext cause we cannot handle the richtext formattings anyway QString result; enum { Rich, Paragraph, TextRun } s; s = Rich; while (!atEnd()) { readNext(); switch(s) { case Rich: if (isStartElement() && qualifiedName() == QLatin1String("a:p")) s = Paragraph; break; case Paragraph: if (qualifiedName() == QLatin1String("a:r")) // text run s = isStartElement() ? TextRun : Rich; break; case TextRun: if (qualifiedName() == QLatin1String("a:t")) { if(isStartElement()) { if(!result.isEmpty()) result += ' '; //concat multiple strings into one result const QString text = readElementText(); result += text; m_context->m_chart->m_title = text; } else s = Paragraph; } break; } BREAK_IF_END_OF(rich) } if(!result.isEmpty()) m_context->m_chart->m_texts << new Charting::Text(result); state = Start; break; } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL tx //! tx (Series Text) /*! ECMA-376, 21.2.2.215, p.3863. Parent elements: - ser §21.2.2.168 - ser §21.2.2.170 - ser §21.2.2.174 - ser §21.2.2.171 - ser §21.2.2.172 - ser §21.2.2.169 - ser §21.2.2.167 - ser §21.2.2.173 Child elements: - [Done]strRef (String Reference) §21.2.2.201 - v (Text Value) §21.2.2.223 */ /*! This element specifies text to use on a chart, including rich text formatting. */ KoFilter::ConversionStatus XlsxXmlChartReader::read_seriesText_Tx() { READ_PROLOGUE2(seriesText_Tx) d->m_currentStrRef = &d->m_currentTx->m_strRef; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(strRef) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL numCache //! numCache (Number Cache) /*! ECMA-376, 21.2.2.120, p.3813. Parent elements: - numRef (§21.2.2.123) Child elements: - extLst (Chart Extensibility) §21.2.2.64 - formatCode (Format Code) §21.2.2.71 - [Done]pt (Numeric Point) §21.2.2.150 - [Done]ptCount (Point Count) §21.2.2.152 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_numCache() { READ_PROLOGUE d->m_currentPtCount = &d->m_currentNumCache->m_ptCount; d->m_currentPtCache = &d->m_currentNumCache->m_cache; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(ptCount) ELSE_TRY_READ_IF(pt) ELSE_TRY_READ_IF(formatCode) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL formatCode //! formatCode (Format Code) /*! ECMA-376, 21.2.2.71, p.3802. Parent elements: - numCache (§21.2.2.120) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_formatCode() { READ_PROLOGUE const QString val = readElementText(); d->m_currentNumCache->formatCode = val; // while (!atEnd()) { // readNext(); // BREAK_IF_END_OF(CURRENT_EL) // // } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL legend KoFilter::ConversionStatus XlsxXmlChartReader::read_legend() { READ_PROLOGUE if (!m_context->m_chart->m_legend) { m_context->m_chart->m_legend = new Charting::Legend(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) //TODO } READ_EPILOGUE } void XlsxXmlChartReader::read_showDataLabel() { if ( m_currentSeries ) { const QXmlStreamAttributes attrs(attributes()); if ( qualifiedName() == "c:showVal" ) { m_currentSeries->m_showDataLabelValues = MSOOXML::Utils::convertBooleanAttr(attrs.value("val").toString(), true); } else if ( qualifiedName() == "c:showPercent" ) { m_currentSeries->m_showDataLabelPercent = MSOOXML::Utils::convertBooleanAttr(attrs.value("val").toString(), true); } else if ( qualifiedName() == "c:showCatName" ) { m_currentSeries->m_showDataLabelCategory = MSOOXML::Utils::convertBooleanAttr(attrs.value("val").toString(), true); } else if ( qualifiedName() == "c:showSerName" ) { m_currentSeries->m_showDataLabelSeries = MSOOXML::Utils::convertBooleanAttr(attrs.value("val").toString(), true); } } } #undef CURRENT_EL #define CURRENT_EL dLbl //! dLbl (Data Label) /*! ECMA-376, 21.2.2.47, p.3780. Parent elements: - dLbls (§21.2.2.49) - pivotFmt (§21.2.2.142) Child elements: - delete (Delete) §21.2.2.40 - dLblPos (Data Label Position) §21.2.2.48 - extLst (Chart Extensibility) §21.2.2.64 - idx (Index) §21.2.2.84 - layout (Layout) §21.2.2.88 - numFmt (Number Format) §21.2.2.121 - separator (Separator) §21.2.2.166 - showBubbleSize (Show Bubble Size) §21.2.2.178 - showCatName (Show Category Name) §21.2.2.179 - showLegendKey (Show Legend Key) §21.2.2.184 - showPercent (Show Percent) §21.2.2.187 - showSerName (Show Series Name) §21.2.2.188 - [Done]showVal (Show Value) §21.2.2.189 - spPr (Shape Properties) §21.2.2.197 - tx (Chart Text) §21.2.2.214 - txPr (Text Properties) §21.2.2.216 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_dLbl() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { read_showDataLabel(); } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL dLbls //! dLbls (Data Labels) /*! ECMA-376, 21.2.2.49, p.3781. Parent elements: - area3DChart (§21.2.2.4) - areaChart (§21.2.2.5) - bar3DChart (§21.2.2.15) - barChart (§21.2.2.16) - bubbleChart (§21.2.2.20) - doughnutChart (§21.2.2.50) - line3DChart (§21.2.2.96) - lineChart (§21.2.2.97) - ofPieChart (§21.2.2.126) - pie3DChart (§21.2.2.140) - pieChart (§21.2.2.141) - radarChart (§21.2.2.153) - scatterChart (§21.2.2.161) - ser (§21.2.2.168) - ser (§21.2.2.170) - ser (§21.2.2.174) - ser (§21.2.2.171) - ser (§21.2.2.172) - ser (§21.2.2.169) - ser (§21.2.2.167) - stockChart (§21.2.2.198) Child elements: - delete (Delete) §21.2.2.40 - [Done]dLbl (Data Label) §21.2.2.47 - dLblPos (Data Label Position) §21.2.2.48 - extLst (Chart Extensibility) §21.2.2.64 - leaderLines (Leader Lines) §21.2.2.92 - numFmt (Number Format) §21.2.2.121 - separator (Separator) §21.2.2.166 - showBubbleSize (Show Bubble Size) §21.2.2.178 - showCatName (Show Category Name) §21.2.2.179 - showLeaderLines (Show Leader Lines) §21.2.2.183 - showLegendKey (Show Legend Key) §21.2.2.184 - showPercent (Show Percent) §21.2.2.187 - showSerName (Show Series Name) §21.2.2.188 - [Done]showVal (Show Value) §21.2.2.189 - spPr (Shape Properties) §21.2.2.197 - txPr (Text Properties) §21.2.2.216 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_dLbls() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(dLbl) else if ( qualifiedName() == QLatin1String( QUALIFIED_NAME(numFmt) ) ) { const QXmlStreamAttributes attrs(attributes()); m_currentSeries->m_numberFormat = attrs.value("formatCode").toString(); } read_showDataLabel(); } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL spPr // Visual shape properties that can be applied to a shape. /*! * spPr Shape Properties * ECMA-376, 5.7.2.98, p.4108. * Parent Elements: * - backWall( §5.7.2.11 ) * - bandFmt( §5.7.2.13 ) * - catAx( §5.7.2.25 ) * - chartspace( §5.7.2.29 ) * - dataAx( §5.7.2.39 ) * - dispUnitsLbl( §5.7.2.46 ) * - dLbl( §5.7.2.47 ) * - dLbls( §5.7.2.49 ) * - downBars( §5.7.2.51) * - dPt( §5.7.2.52 ) * ... * * Child elements: * - blipFill( Picture Fill ) §5.1.10.14 * - customGeom( Custom Geometry ) §5.1.11.8 * - effectDag( Effect Container ) §5.1.10.25 * - effectLst( Effect Container ) §5.1.10.26 * - gradFill ( Gradient Fill ) §5.1.10.33 * - gradFill ( Group Fill ) §5.1.10.35 * - ln ( Outline ) §5.1.2.1.24 * - noFill( No Fill ) §5.1.10.44 * - pattFill( Pattern Fill ) §5.1.10.47 * - prstGeom( Preset geometry ) §5.1.11.18 * - scene3d ( 3D Scene Properties ) §5.1.4.1.26 * - solidFill( Solid Fill ) §5.1.10.54 * - sp3d ( Apply 3D shape properties ) §5.1.7.12 * - xrfm( 2D Transform for Individual Objects ) § 5.1.9.6 * * attributes: * - bwMode ( Black and White Mode ) §5.1.12.10 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_spPr() { enum State { Start, NoFill, InFill }; State state = Start; READ_PROLOGUE int level = 0; bool readingGradient = false; bool readingGradientStop = false; Charting::Gradient* gradient = NULL; Charting::Gradient::GradientStop currentStop; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if ( !m_currentShapeProperties ) continue; if(isStartElement()) ++level; else if(isEndElement()) --level; if (qualifiedName() == "a:solidFill" || qualifiedName() == "a:pattFill" || qualifiedName() == "a:gradFill") { if (level == 1) state = isStartElement() ? InFill : Start; } else if (qualifiedName() == "a:noFill") { m_currentShapeProperties->lineFill.setType( Charting::Fill::None ); if (level == 1) state = isStartElement() ? NoFill : Start; } else if ((state == NoFill || state == InFill) && qualifiedName() == "a:srgbClr") { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if(!val.isEmpty() && !m_context->m_chart->m_areaFormat) { if(!val.startsWith('#')) val.prepend('#'); if ( readingGradientStop ) { currentStop.knownColorValue = QColor( val ); } else { Charting::AreaFormat *areaFormat = new Charting::AreaFormat(QColor(val), QColor(), state == InFill); if ( m_areaContext == ChartArea ) m_context->m_chart->m_areaFormat = areaFormat; else m_context->m_chart->m_plotArea->m_areaFormat = areaFormat; } } state = Start; // job done } else if ( qualifiedName() == "a:srgbClr" ) { if ( isStartElement() ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if(!val.isEmpty() && !m_context->m_chart->m_areaFormat) { if(!val.startsWith('#')) val.prepend('#'); if ( readingGradientStop ) { currentStop.knownColorValue = QColor( val ); } else { Charting::AreaFormat *areaFormat = new Charting::AreaFormat(QColor(val), QColor(), state == InFill); if ( m_areaContext == ChartArea ) { m_context->m_chart->m_areaFormat = areaFormat; } else { m_context->m_chart->m_plotArea->m_areaFormat = areaFormat; } } } } } else if ( qualifiedName() == "a:alpha" ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if ( !val.isEmpty() ) { if ( readingGradientStop ) { currentStop.knownColorValue.setAlphaF( val.toDouble() / 100000.0 ); } else { if ( m_areaContext == ChartArea ) { if ( m_context->m_chart->m_areaFormat ) m_context->m_chart->m_areaFormat->m_foreground.setAlphaF( val.toDouble() / 100000.0 ); } else { if ( m_context->m_chart->m_plotArea->m_areaFormat ) m_context->m_chart->m_plotArea->m_areaFormat->m_foreground.setAlphaF( val.toDouble() / 100000.0 ); } } } } else if ( qualifiedName() == "a:gsLst" ) { if ( isStartElement() ) { readingGradient = true; gradient = new Charting::Gradient; } else if ( isEndElement() ) { readingGradient = false; switch ( m_areaContext ) { case( PlotArea ): m_context->m_chart->m_plotAreaFillGradient = gradient; break; case( ChartArea ): m_context->m_chart->m_fillGradient = gradient; break; } gradient = NULL; } } else if ( qualifiedName() == "a:gs" && readingGradient ) { if ( isStartElement() ) { readingGradientStop = true; const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(pos) if ( !pos.isEmpty() ) currentStop.position = pos.toDouble() / 1000.0; } else if ( isEndElement() ) { // append the current gradient stop gradient->gradientStops.append( currentStop ); readingGradientStop = false; currentStop.reset(); } } else if ( qualifiedName() == "a:schemeClr" && readingGradientStop ) { if ( isStartElement() ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if ( !val.isEmpty() ) currentStop.referenceColor = val; } else if ( isEndElement() ) { } } else if ( qualifiedName() == "a:tint" && readingGradientStop ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if ( !val.isEmpty() ) currentStop.tintVal = val.toDouble() / 1000.0; } else if ( qualifiedName() == "a:satMod" && readingGradientStop ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if ( !val.isEmpty() ) currentStop.satVal = val.toDouble() / 1000.0; } else if ( qualifiedName() == "a:lin" && readingGradient ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(ang) if ( !ang.isEmpty() ) gradient->angle = ang.toDouble() / 60000.0; } else if ( qualifiedName() == "a:noFill" ) { m_currentShapeProperties->lineFill.setType( Charting::Fill::None ); } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL pieChart //! pieChart (Pie Charts) /*! ECMA-376, 21.2.2.141, p.3826. Parent elements: - plotArea §21.2.2.145 Child elements: - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - [Done]firstSliceAng (First Slice Angle) §21.2.2.68 - [Done] ser (Pie Chart Series) §21.2.2.172 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_pieChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::PieImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(pieChart_Ser) } ELSE_TRY_READ_IF(firstSliceAng) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL pie3DChart //! pie3DChart (3D Pie Charts) /*! ECMA-376, 21.2.2.140, p.3826. Parent elements: - plotArea §21.2.2.145 Child elements: - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - [Done] ser (Pie Chart Series) §21.2.2.172 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_pie3DChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::PieImpl(); m_context->m_chart->m_is3d = true; } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(pieChart_Ser) } } } // // if there is only one c:ser, then c:tx can be chart title // if ((m_context->m_chart->m_title == "Chart Title") && (d->m_seriesData.size() == 1)) { // PieSeries * tempPieSeriesData = (PieSeries *)d->m_seriesData[0]; // if (tempPieSeriesData->m_tx.m_strRef.m_strCache.m_cache.size() == 1) { // m_context->m_chart->m_title = tempPieSeriesData->m_tx.m_strRef.m_strCache.m_cache[0]; // } // } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL ofPieChart //! ofPieChart (Pie of Pie or Bar of Pie Charts) /*! ECMA-376, §21.2.2.126, p.4057. Parent elements: - plotArea §21.2.2.145 Child elements: - custSplit (Custom Split) §5.7.2.35 - dLbls (Data Labels) §5.7.2.49 - extLst (Chart Extensibility) §5.7.2.64 - gapWidth (Gap Width) §5.7.2.75 - ofPieType (Pie of Pie or Bar of Pie Type) §5.7.2.128 - secondPieSize (Second Pie Size) §5.7.2.165 - ser (Pie Chart Series) §5.7.2.170 - serLines (Series Lines) §5.7.2.177 - splitPos (Split Position) §5.7.2.196 - splitType (Split Type) §5.7.2.197 - varyColors (Vary Colors by Point) §5.7.2.228 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_ofPieChart() { // KDChart used in the charting-plugin doesn't support pie-of-pie or bar-of-pie // charts nor does ODF. So, we do the same OO.org is doing and just translate // it to pie-chart what is better then nothing. if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::PieImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(pieChart_Ser) } } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL doughnutChart //! doughnutChart (Doughnut Charts) /*! ECMA-376, 21.2.2.50, p.3782. Parent elements: - plotArea §21.2.2.145 Child elements: - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - firstSliceAng (First Slice Angle) §21.2.2.68 - [Done]holeSize (Hole Size) §21.2.2.82 - [Done]ser (Pie Chart Series) §21.2.2.172 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_doughnutChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::RingImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(pieChart_Ser) } ELSE_TRY_READ_IF(holeSize) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL areaChart //! areaChart (Area Charts) /*! ECMA-376, 21.2.2.5, p.3757. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - dLbls (Data Labels) §21.2.2.49 - dropLines (Drop Lines) §21.2.2.53 - extLst (Chart Extensibility) §21.2.2.64 - [done] grouping (Grouping) §21.2.2.76 - [Done] ser (Area Chart Series) §21.2.2.168 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_areaChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::AreaImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(areaChart_Ser) } ELSE_TRY_READ_IF(grouping) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL area3DChart //! area3DChart (3D Area Charts) /*! ECMA-376, 21.2.2.4, p.3757. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - dLbls (Data Labels) §21.2.2.49 - dropLines (Drop Lines) §21.2.2.53 - extLst (Chart Extensibility) §21.2.2.64 - gapDepth (Gap Depth) §21.2.2.74 - [done] grouping (Grouping) §21.2.2.76 - [Done] ser (Area Chart Series) §21.2.2.168 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_area3DChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::AreaImpl(); m_context->m_chart->m_is3d = true; } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(areaChart_Ser) } ELSE_TRY_READ_IF(grouping) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL barChart //! barChart (Bar Charts) /*! ECMA-376, 21.2.2.16, p.3863. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - [Done]barDir (Bar Direction) §21.2.2.17 - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - gapWidth (Gap Width) §21.2.2.75 - [Done]grouping (Bar Grouping) §21.2.2.77 - overlap (Overlap) §21.2.2.131 - [Done]ser (Bar Chart Series) §21.2.2.170 - serLines (Series Lines) §21.2.2.176 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_barChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::BarImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(barChart_Ser) } ELSE_TRY_READ_IF(barDir) ELSE_TRY_READ_IF(grouping) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL bar3DChart //! bar3DChart (3D Bar Charts) /*! ECMA-376, 21.2.2.15, p.3862. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - [done]barDir (Bar Direction) §21.2.2.17 - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - gapDepth (Gap Depth) §21.2.2.74 - gapWidth (Gap Width) §21.2.2.75 - [Done]grouping (Bar Grouping) §21.2.2.77 - [Done]ser (Bar Chart Series) §21.2.2.170 - shape (Shape) §21.2.2.177 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_bar3DChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::BarImpl(); m_context->m_chart->m_is3d = true; } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(barChart_Ser) } ELSE_TRY_READ_IF(barDir) ELSE_TRY_READ_IF(grouping) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL lineChart //! lineChart (Line Charts) /*! ECMA-376, 21.2.2.97, p.3804. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - dLbls (Data Labels) §21.2.2.49 - dropLines (Drop Lines) §21.2.2.53 - extLst (Chart Extensibility) §21.2.2.64 - [Done]grouping (Grouping) §21.2.2.76 - hiLowLines (High Low Lines) §21.2.2.80 - marker (Show Marker) §21.2.2.105 - [Done]ser (Line Chart Series) §21.2.2.171 - smooth (Smoothing) §21.2.2.194 - upDownBars (Up/Down Bars) §21.2.2.218 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_lineChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::LineImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(lineChart_Ser) } ELSE_TRY_READ_IF(grouping) ELSE_TRY_READ_IF(marker) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL line3DChart //! line3DChart (3D Line Charts) /*! ECMA-376, 21.2.2.96, p.3803. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - dLbls (Data Labels) §21.2.2.49 - dropLines (Drop Lines) §21.2.2.53 - extLst (Chart Extensibility) §21.2.2.64 - gapDepth (Gap Depth) §21.2.2.74 - [Done]grouping (Grouping) §21.2.2.76 - [Done]ser (Line Chart Series) §21.2.2.171 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_line3DChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::LineImpl(); m_context->m_chart->m_is3d = true; } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(lineChart_Ser) } ELSE_TRY_READ_IF(grouping) } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL scatterChart //! scatterChart (Scatter Charts) /*! ECMA-376, 21.2.2.161, p.3836. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - scatterStyle (Scatter Style) §21.2.2.162 - [Done]ser (Scatter Chart Series) §21.2.2.167 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_scatterChart() { Charting::ScatterImpl* impl = dynamic_cast(m_context->m_chart->m_impl); if (!impl) { m_context->m_chart->m_impl = impl = new Charting::ScatterImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(scatterChart_Ser) } else if (QUALIFIED_NAME_IS(scatterStyle)) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val); if ( val == "none" ) impl->style = Charting::ScatterImpl::None; else if ( val == "line" ) impl->style = Charting::ScatterImpl::Line; else if ( val == "lineMarker" ) impl->style = Charting::ScatterImpl::LineMarker; else if ( val == "marker" ) impl->style = Charting::ScatterImpl::Marker; else if ( val == "smooth" ) impl->style = Charting::ScatterImpl::Smooth; else if ( val == "smoothMarker" ) impl->style = Charting::ScatterImpl::SmoothMarker; } } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL radarChart //! radarChart (Radar Charts) /*! ECMA-376, 21.2.2.153, p.3832. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - radarStyle (Radar Style) §21.2.2.154 - [Done]ser (Radar Chart Series) §21.2.2.169 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_radarChart() { Charting::RadarImpl* impl = dynamic_cast(m_context->m_chart->m_impl); if (!impl) { m_context->m_chart->m_impl = impl = new Charting::RadarImpl(false); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(radarStyle)) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if (val == "filled") impl->m_filled = true; } else if (QUALIFIED_NAME_IS(ser)) { TRY_READ(radarChart_Ser) } } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL surfaceChart //! surface3DChart (3D Surface Charts) /*! ECMA-376, 21.2.2.203, p.3858. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - bandFmts (Band Formats) §21.2.2.14 - extLst (Chart Extensibility) §21.2.2.64 - [Done]ser (Surface Chart Series) §21.2.2.173 - wireframe (Wireframe) §21.2.2.230 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_surfaceChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::SurfaceImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(surfaceChart_Ser) } } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL surface3DChart //! surfaceChart (Surface Charts) /*! ECMA-376, 21.2.2.204, p.3858. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - bandFmts (Band Formats) §21.2.2.14 - extLst (Chart Extensibility) §21.2.2.64 - ser (Surface Chart Series) §21.2.2.173 - wireframe (Wireframe) §21.2.2.230 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_surface3DChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::SurfaceImpl(); m_context->m_chart->m_is3d = true; } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(surfaceChart_Ser) } } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL bubbleChart //! bubbleChart (Bubble Charts) /*! ECMA-376, 21.2.2.20, p.3765. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - [Done]bubble3D (3D Bubble) §21.2.2.19 - [Done]bubbleScale (Bubble Scale) §21.2.2.21 - dLbls (Data Labels) §21.2.2.49 - extLst (Chart Extensibility) §21.2.2.64 - [Done]ser (Bubble Chart Series) §21.2.2.174 - showNegBubbles (Show Negative Bubbles) §21.2.2.185 - sizeRepresents (Size Represents) §21.2.2.193 - varyColors (Vary Colors by Point) §21.2.2.227 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_bubbleChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::BubbleImpl(); m_context->m_chart->m_is3d = true; } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(bubbleChart_Ser) } ELSE_TRY_READ_IF(bubbleScale) ELSE_TRY_READ_IF(bubble3D) } } // check if there are some c:strLit or c:numLit data and if yes then write them into internalTable // for (int i=0; im_seriesData.size(); i++ ){ // QString range = ((BubbleSeries *)d->m_seriesData[i])->m_bubbleSize.writeLitToInternalTable(this); // if (!range.isEmpty()) { // m_context->m_chart->m_series[i]->m_domainValuesCellRangeAddress.push_back(range); // } // } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL stockChart //! stockChart (Stock Charts) /*! ECMA-376, 21.2.2.199, p.3856. Parent elements: - plotArea §21.2.2.145 Child elements: - axId (Axis ID) §21.2.2.9 - dLbls (Data Labels) §21.2.2.49 - dropLines (Drop Lines) §21.2.2.53 - extLst (Chart Extensibility) §21.2.2.64 - hiLowLines (High Low Lines) §21.2.2.80 - [done]ser (Line Chart Series) §21.2.2.171 - upDownBars (Up/Down Bars) §21.2.2.218 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_stockChart() { if(!m_context->m_chart->m_impl) { m_context->m_chart->m_impl = new Charting::StockImpl(); } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(ser)) { TRY_READ(lineChart_Ser) } } } qDeleteAll(d->m_seriesData); d->m_seriesData.clear(); return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Pie Chart Series) /*! ECMA-376, 21.2.2.172, p.3842. Parent elements: - doughnutChart §21.2.2.50 - ofPieChart §21.2.2.126 - pie3DChart §21.2.2.140 - pieChart §21.2.2.141 Child elements: - cat (Category Axis Data) §21.2.2.24 - [Done]dLbls (Data Labels) §21.2.2.49 - dPt (Data Point) §21.2.2.52 - explosion (Explosion) §21.2.2.61 - extLst (Chart Extensibility) §21.2.2.64 - [Done]idx (Index) §21.2.2.84 - [Done] order (Order) §21.2.2.128 - spPr (Shape Properties) §21.2.2.197 - tx (Series Text) §21.2.2.215 - val (Values) §21.2.2.224 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_pieChart_Ser() { READ_PROLOGUE2(pieChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; PieSeries * tempPieSeriesData = new PieSeries(); d->m_seriesData << tempPieSeriesData; d->m_currentIdx = &tempPieSeriesData->m_idx; d->m_currentOrder = &tempPieSeriesData->m_order; d->m_currentTx = &tempPieSeriesData->m_tx; d->m_currentCat = &tempPieSeriesData->m_cat; d->m_currentVal = &tempPieSeriesData->m_val; d->m_currentExplosion = &tempPieSeriesData->m_explosion; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } ELSE_TRY_READ_IF(cat) ELSE_TRY_READ_IF(val) ELSE_TRY_READ_IF(explosion) ELSE_TRY_READ_IF(dLbls) } } // set data ranges and write data to internal table m_currentSeries->m_countYValues = tempPieSeriesData->m_val.m_numRef.m_numCache.m_ptCount; if ( !m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty() && m_context->m_chart->m_series.count() == 1 && !tempPieSeriesData->m_tx.m_strRef.m_strCache.m_cache.isEmpty() ) m_context->m_chart->m_title = tempPieSeriesData->m_tx.m_strRef.m_strCache.m_cache[ 0 ]; m_currentSeries->m_labelCell = tempPieSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_valuesCellRangeAddress = tempPieSeriesData->m_val.writeRefToInternalTable(this); m_context->m_chart->m_verticalCellRangeAddress = tempPieSeriesData->m_cat.writeRefToInternalTable(this); // set explosion if (tempPieSeriesData->m_explosion != 0) { if(Charting::PieImpl* pie = dynamic_cast(m_context->m_chart->m_impl)) { Q_UNUSED(pie); m_currentSeries->m_datasetFormat << new Charting::PieFormat(tempPieSeriesData->m_explosion); } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Bubble Chart Series) /*! ECMA-376, 21.2.2.174, p.3843. Parent elements: - ser (Bubble Chart Series) Child elements: - [Done]bubble3D (3D Bubble) §21.2.2.19 - [Done]bubbleSize (Bubble Size) §21.2.2.22 - [Done]dLbls (Data Labels) §21.2.2.49 - dPt (Data Point) §21.2.2.52 - errBars (Error Bars) §21.2.2.55 - extLst (Chart Extensibility) §21.2.2.64 - [Done]idx (Index) §21.2.2.84 - invertIfNegative (Invert if Negative) §21.2.2.86 - [Done]order (Order) §21.2.2.128 - spPr (Shape Properties) §21.2.2.197 - trendline (Trendlines) §21.2.2.211 - [Done]tx (Series Text) §21.2.2.215 - [Done]xVal (X Values) §21.2.2.234 - [Done]yVal (Y Values) §21.2.2.237 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_bubbleChart_Ser() { READ_PROLOGUE2(bubbleChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; BubbleSeries * tempBubbleSeriesData = new BubbleSeries(); d->m_seriesData << tempBubbleSeriesData; d->m_currentIdx = &tempBubbleSeriesData->m_idx; d->m_currentOrder = &tempBubbleSeriesData->m_order; d->m_currentTx = &tempBubbleSeriesData->m_tx; d->m_currentXVal = &tempBubbleSeriesData->m_xVal; d->m_currentYVal = &tempBubbleSeriesData->m_yVal; d->m_currentBubbleSize = &tempBubbleSeriesData->m_bubbleSize; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } ELSE_TRY_READ_IF(xVal) ELSE_TRY_READ_IF(yVal) ELSE_TRY_READ_IF(bubbleSize) ELSE_TRY_READ_IF(dLbls) ELSE_TRY_READ_IF(bubble3D) } } if ( !m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty() && m_context->m_chart->m_series.count() == 1 && !tempBubbleSeriesData->m_tx.m_strRef.m_strCache.m_cache.isEmpty() ) m_context->m_chart->m_title = tempBubbleSeriesData->m_tx.m_strRef.m_strCache.m_cache[ 0 ]; // set data ranges and write data to internal table m_currentSeries->m_labelCell = tempBubbleSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_countYValues = tempBubbleSeriesData->m_yVal.m_numRef.m_numCache.m_ptCount; m_currentSeries->m_domainValuesCellRangeAddress << tempBubbleSeriesData->m_yVal.writeRefToInternalTable(this); if ( tempBubbleSeriesData->m_bubbleSize.m_numRef.m_f.isEmpty() ) m_currentSeries->m_valuesCellRangeAddress = tempBubbleSeriesData->m_bubbleSize.writeLitToInternalTable(this); else m_currentSeries->m_valuesCellRangeAddress = tempBubbleSeriesData->m_bubbleSize.writeRefToInternalTable(this); // m_currentSeries->m_domainValuesCellRangeAddress.push_back(tempBubbleSeriesData->m_xVal.writeRefToInternalTable(this)); // // QString bubbleSizeRange = tempBubbleSeriesData->m_bubbleSize.writeRefToInternalTable(this); // if (!bubbleSizeRange.isEmpty()) { // m_currentSeries->m_domainValuesCellRangeAddress.push_back(tempBubbleSeriesData->m_bubbleSize.writeRefToInternalTable(this)); // } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Scatter Chart Series) /*! ECMA-376, 21.2.2.167, p.3838. Parent elements: - scatterChart (§21.2.2.161) Child elements: - [Done]dLbls (Data Labels) §21.2.2.49 - dPt (Data Point) §21.2.2.52 - errBars (Error Bars) §21.2.2.55 - extLst (Chart Extensibility) §21.2.2.64 - [done] idx (Index) §21.2.2.84 - marker (Marker) §21.2.2.106 - [Done] order (Order) §21.2.2.128 - smooth (Smoothing) §21.2.2.194 - spPr (Shape Properties) §21.2.2.197 - trendline (Trendlines) §21.2.2.211 - [Done] tx (Series Text) §21.2.2.215 - [Done] xVal (X Values) §21.2.2.234 - [Done] yVal (Y Values) §21.2.2.237 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_scatterChart_Ser() { READ_PROLOGUE2(scatterChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; ScatterSeries * tempScatterSeriesData = new ScatterSeries(); d->m_seriesData << tempScatterSeriesData; d->m_currentIdx = &tempScatterSeriesData->m_idx; d->m_currentOrder = &tempScatterSeriesData->m_order; d->m_currentTx = &tempScatterSeriesData->m_tx; d->m_currentXVal = &tempScatterSeriesData->m_xVal; d->m_currentYVal = &tempScatterSeriesData->m_yVal; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (QUALIFIED_NAME_IS(spPr) ) { m_currentSeries->spPr = new Charting::ShapeProperties; m_currentShapeProperties = m_currentSeries->spPr; } ELSE_TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } ELSE_TRY_READ_IF(xVal) ELSE_TRY_READ_IF(yVal) ELSE_TRY_READ_IF(dLbls) ELSE_TRY_READ_IF(spPr) // ELSE_TRY_READ_IF(spPr) } } if ( !m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty() && m_context->m_chart->m_series.count() == 1 && !tempScatterSeriesData->m_tx.m_strRef.m_strCache.m_cache.isEmpty() ) m_context->m_chart->m_title = tempScatterSeriesData->m_tx.m_strRef.m_strCache.m_cache[ 0 ]; // set data ranges and write data to internal table m_currentSeries->m_labelCell = tempScatterSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_countXValues = tempScatterSeriesData->m_xVal.m_numLit.m_ptCount; if (m_currentSeries->m_countXValues == 0 ) { m_currentSeries->m_countXValues = tempScatterSeriesData->m_xVal.m_strRef.m_strCache.m_ptCount; m_currentSeries->m_domainValuesCellRangeAddress << tempScatterSeriesData->m_xVal.writeRefToInternalTable(this); } else { m_currentSeries->m_domainValuesCellRangeAddress << tempScatterSeriesData->m_xVal.writeLitToInternalTable(this); } m_currentSeries->m_countYValues = tempScatterSeriesData->m_yVal.m_numRef.m_numCache.m_ptCount; m_currentSeries->m_valuesCellRangeAddress = tempScatterSeriesData->m_yVal.writeRefToInternalTable(this); //m_currentSeries->m_domainValuesCellRangeAddress.push_back(tempScatterSeriesData->m_xVal.writeRefToInternalTable(this)); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Bar Chart Series) /*! ECMA-376, 21.2.2.167, p.3840. Parent elements: - bar3DChart (§21.2.2.15) - barChart (§21.2.2.16) Child elements: - [Done]cat (Category Axis Data) §21.2.2.24 - [Done]dLbls (Data Labels) §21.2.2.49 - dPt (Data Point) §21.2.2.52 - errBars (Error Bars) §21.2.2.55 - extLst (Chart Extensibility) §21.2.2.64 - idx (Index) §21.2.2.84 - invertIfNegative (Invert if Negative) §21.2.2.86 - order (Order) §21.2.2.128 - pictureOptions (Picture Options) §21.2.2.138 - shape (Shape) §21.2.2.177 - spPr (Shape Properties) §21.2.2.197 - trendline (Trendlines) §21.2.2.211 - [Done]tx (Series Text) §21.2.2.215 - [Done]val (Values) §21.2.2.224 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_barChart_Ser() { READ_PROLOGUE2(barChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; BarSeries * tempBarSeriesData = new BarSeries(); d->m_seriesData << tempBarSeriesData; d->m_currentIdx = &tempBarSeriesData->m_idx; d->m_currentOrder = &tempBarSeriesData->m_order; d->m_currentTx = &tempBarSeriesData->m_tx; d->m_currentCat = &tempBarSeriesData->m_cat; d->m_currentVal = &tempBarSeriesData->m_val; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) else if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } ELSE_TRY_READ_IF(cat) ELSE_TRY_READ_IF(val) ELSE_TRY_READ_IF(dLbls) } } if ( !m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty() && m_context->m_chart->m_series.count() == 1 && !tempBarSeriesData->m_tx.m_strRef.m_strCache.m_cache.isEmpty() ) m_context->m_chart->m_title = tempBarSeriesData->m_tx.m_strRef.m_strCache.m_cache[ 0 ]; // set data ranges and write data to internal table m_currentSeries->m_countYValues = tempBarSeriesData->m_val.m_numRef.m_numCache.m_ptCount; m_currentSeries->m_labelCell = tempBarSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_valuesCellRangeAddress = tempBarSeriesData->m_val.writeRefToInternalTable(this); m_context->m_chart->m_verticalCellRangeAddress = tempBarSeriesData->m_cat.writeRefToInternalTable(this); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Area Chart Series) /*! ECMA-376, 21.2.2.168, p.3839. Parent elements: - area3DChart (§21.2.2.4) - areaChart (§21.2.2.5) Child elements: - cat (Category Axis Data) §21.2.2.24 - [Done]dLbls (Data Labels) §21.2.2.49 - dPt (Data Point) §21.2.2.52 - errBars (Error Bars) §21.2.2.55 - extLst (Chart Extensibility) §21.2.2.64 - idx (Index) §21.2.2.84 - order (Order) §21.2.2.128 - pictureOptions (Picture Options) §21.2.2.138 - spPr (Shape Properties) §21.2.2.197 - trendline (Trendlines) §21.2.2.211 - tx (Series Text) §21.2.2.215 - val (Values) §21.2.2.224 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_areaChart_Ser() { READ_PROLOGUE2(areaChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; AreaSeries * tempAreaSeriesData = new AreaSeries(); d->m_seriesData << tempAreaSeriesData; d->m_currentIdx = &tempAreaSeriesData->m_idx; d->m_currentOrder = &tempAreaSeriesData->m_order; d->m_currentTx = &tempAreaSeriesData->m_tx; d->m_currentCat = &tempAreaSeriesData->m_cat; d->m_currentVal = &tempAreaSeriesData->m_val; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) else if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } ELSE_TRY_READ_IF(cat) ELSE_TRY_READ_IF(val) ELSE_TRY_READ_IF(dLbls) } } if ( !m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty() && m_context->m_chart->m_series.count() == 1 && !tempAreaSeriesData->m_tx.m_strRef.m_strCache.m_cache.isEmpty() ) m_context->m_chart->m_title = tempAreaSeriesData->m_tx.m_strRef.m_strCache.m_cache[ 0 ]; // set data ranges and write data to internal table m_currentSeries->m_countYValues = tempAreaSeriesData->m_val.m_numRef.m_numCache.m_ptCount; m_currentSeries->m_labelCell = tempAreaSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_valuesCellRangeAddress = tempAreaSeriesData->m_val.writeRefToInternalTable(this); m_context->m_chart->m_verticalCellRangeAddress = tempAreaSeriesData->m_cat.writeRefToInternalTable(this); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Radar Chart Series) /*! ECMA-376, 21.2.2.169, p.3840. Parent elements: - radarChart (§21.2.2.153) Child elements: - [Done]cat (Category Axis Data) §21.2.2.24 - [Done]dLbls (Data Labels) §21.2.2.49 - dPt (Data Point) §21.2.2.52 - extLst (Chart Extensibility) §21.2.2.64 - [Done]idx (Index) §21.2.2.84 - marker (Marker) §21.2.2.106 - [Done]order (Order) §21.2.2.128 - spPr (Shape Properties) §21.2.2.197 - [Done]tx (Series Text) §21.2.2.215 - [Done]val (Values) §21.2.2.224 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_radarChart_Ser() { READ_PROLOGUE2(radarChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; RadarSeries * tempRadarSeriesData = new RadarSeries(); d->m_seriesData << tempRadarSeriesData; d->m_currentIdx = &tempRadarSeriesData->m_idx; d->m_currentOrder = &tempRadarSeriesData->m_order; d->m_currentTx = &tempRadarSeriesData->m_tx; d->m_currentCat = &tempRadarSeriesData->m_cat; d->m_currentVal = &tempRadarSeriesData->m_val; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) else if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } ELSE_TRY_READ_IF(cat) ELSE_TRY_READ_IF(val) ELSE_TRY_READ_IF(dLbls) } } if ( !m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty() && m_context->m_chart->m_series.count() == 1 && !tempRadarSeriesData->m_tx.m_strRef.m_strCache.m_cache.isEmpty() ) m_context->m_chart->m_title = tempRadarSeriesData->m_tx.m_strRef.m_strCache.m_cache[ 0 ]; // set data ranges and write data to internal table m_currentSeries->m_countYValues = tempRadarSeriesData->m_val.m_numRef.m_numCache.m_ptCount; m_currentSeries->m_labelCell = tempRadarSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_valuesCellRangeAddress = tempRadarSeriesData->m_val.writeRefToInternalTable(this); m_context->m_chart->m_verticalCellRangeAddress = tempRadarSeriesData->m_cat.writeRefToInternalTable(this); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Line Chart Series) /*! ECMA-376, 21.2.2.168, p.3839. Parent elements: - line3DChart (§21.2.2.96) - lineChart (§21.2.2.97) - stockChart (§21.2.2.198) Child elements: - [Done]cat (Category Axis Data) §21.2.2.24 - [Done]dLbls (Data Labels) §21.2.2.49 - dPt (Data Point) §21.2.2.52 - errBars (Error Bars) §21.2.2.55 - extLst (Chart Extensibility) §21.2.2.64 - [Done]idx (Index) §21.2.2.84 - marker (Marker) §21.2.2.106 - [Done]order (Order) §21.2.2.128 - smooth (Smoothing) §21.2.2.194 - spPr (Shape Properties) §21.2.2.197 - trendline (Trendlines) §21.2.2.211 - [Done]tx (Series Text) §21.2.2.215 - [Done]val (Values) §21.2.2.224 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_lineChart_Ser() { READ_PROLOGUE2(lineChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; LineSeries * tempLineSeriesData = new LineSeries(); d->m_seriesData << tempLineSeriesData; d->m_currentIdx = &tempLineSeriesData->m_idx; d->m_currentOrder = &tempLineSeriesData->m_order; d->m_currentTx = &tempLineSeriesData->m_tx; d->m_currentCat = &tempLineSeriesData->m_cat; d->m_currentVal = &tempLineSeriesData->m_val; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) else if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } else if (QUALIFIED_NAME_IS(marker)) { TRY_READ(serMarker) } ELSE_TRY_READ_IF(cat) ELSE_TRY_READ_IF(val) ELSE_TRY_READ_IF(dLbls) } } if ( !m_autoTitleDeleted && m_context->m_chart->m_title.isEmpty() && m_context->m_chart->m_series.count() == 1 && !tempLineSeriesData->m_tx.m_strRef.m_strCache.m_cache.isEmpty() ) m_context->m_chart->m_title = tempLineSeriesData->m_tx.m_strRef.m_strCache.m_cache[ 0 ]; // set data ranges and write data to internal table m_currentSeries->m_countYValues = tempLineSeriesData->m_val.m_numRef.m_numCache.m_ptCount; m_currentSeries->m_labelCell = tempLineSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_valuesCellRangeAddress = tempLineSeriesData->m_val.writeRefToInternalTable(this); m_context->m_chart->m_verticalCellRangeAddress = tempLineSeriesData->m_cat.writeRefToInternalTable(this); READ_EPILOGUE } Charting::MarkerType markerType(const QString &_val) { const QString val = _val.toLower(); if ( val == "star" ) return Charting::StarMarker; if ( val == "dash" ) return Charting::DashMarker; if ( val == "dot" ) return Charting::DotMarker; if ( val == "plus" ) return Charting::PlusMarker; if ( val == "circle" ) return Charting::CircleMarker; if ( val == "x" ) return Charting::SymbolXMarker; if ( val == "triangle" ) return Charting::TriangleMarker; if ( val == "squre" ) return Charting::SquareMarker; if ( val == "diamond" ) return Charting::DiamondMarker; return Charting::NoMarker; } #undef CURRENT_EL #define CURRENT_EL marker KoFilter::ConversionStatus XlsxXmlChartReader::read_marker() { READ_PROLOGUE bool gotSymbol = m_serMarkerDefined; const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if ( !gotSymbol && qualifiedName() == "c:symbol" ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val); m_context->m_chart->m_markerType = markerType(val); gotSymbol = true; } } } if (!gotSymbol) if (MSOOXML::Utils::convertBooleanAttr(val, true)) m_context->m_chart->m_markerType = Charting::AutoMarker; READ_EPILOGUE return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL marker KoFilter::ConversionStatus XlsxXmlChartReader::read_serMarker() { READ_PROLOGUE2( serMarker ) m_serMarkerDefined = true; bool gotSymbol = false; const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if ( qualifiedName() == "c:symbol" ) { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val); m_currentSeries->m_markerType = markerType(val); gotSymbol = true; } } } if (!gotSymbol) if (MSOOXML::Utils::convertBooleanAttr(val, true)) m_currentSeries->m_markerType = Charting::AutoMarker; READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ser //! ser (Surface Chart Series) /*! ECMA-376, 21.2.2.169, p.3840. Parent elements: - surface3DChart (§21.2.2.203) - surfaceChart (§21.2.2.204) Child elements: - [Done]cat (Category Axis Data) §21.2.2.24 - extLst (Chart Extensibility) §21.2.2.64 - [Done]idx (Index) §21.2.2.84 - marker (Marker) §21.2.2.106 - [Done]order (Order) §21.2.2.128 - spPr (Shape Properties) §21.2.2.197 - [Done]tx (Series Text) §21.2.2.215 - [Done]val (Values) §21.2.2.224 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_surfaceChart_Ser() { READ_PROLOGUE2(surfaceChart_Ser) m_currentSeries = new Charting::Series(); m_context->m_chart->m_series << m_currentSeries; SurfaceSeries * tempSurfaceSeriesData = new SurfaceSeries(); d->m_seriesData << tempSurfaceSeriesData; d->m_currentIdx = &tempSurfaceSeriesData->m_idx; d->m_currentOrder = &tempSurfaceSeriesData->m_order; d->m_currentTx = &tempSurfaceSeriesData->m_tx; d->m_currentCat = &tempSurfaceSeriesData->m_cat; d->m_currentVal = &tempSurfaceSeriesData->m_val; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(order) ELSE_TRY_READ_IF(idx) else if (QUALIFIED_NAME_IS(tx)) { TRY_READ(seriesText_Tx) } ELSE_TRY_READ_IF(cat) ELSE_TRY_READ_IF(val) } } // set data ranges and write data to internal table m_currentSeries->m_countYValues = tempSurfaceSeriesData->m_val.m_numRef.m_numCache.m_ptCount; m_currentSeries->m_labelCell = tempSurfaceSeriesData->m_tx.writeRefToInternalTable(this); m_currentSeries->m_valuesCellRangeAddress = tempSurfaceSeriesData->m_val.writeRefToInternalTable(this); m_context->m_chart->m_verticalCellRangeAddress = tempSurfaceSeriesData->m_cat.writeRefToInternalTable(this); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL barDir //! barDir (Bar Direction) /*! ECMA-376, 21.2.2.17, p.3763. Parent elements: - bar3DChart (§21.2.2.15) - barChart (§21.2.2.16) Attributes: - [Done] val (Bar Direction Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_barDir() { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) m_context->m_chart->m_transpose = (val == "bar"); // "bar" or "col" while (!atEnd()) { BREAK_IF_END_OF(CURRENT_EL) readNext(); } return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL grouping //! grouping (Bar Grouping) /*! ECMA-376, 21.2.2.77, p.3794. Parent elements: - bar3DChart (§21.2.2.15) - barChart (§21.2.2.16) Attributes: - [Done] val (Bar Grouping Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_grouping() { const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) if(val == "stacked") { m_context->m_chart->m_stacked = true; } else if(val == "percentStacked") { m_context->m_chart->m_stacked = true; m_context->m_chart->m_f100 = true; } else if(val == "clustered") { //TODO } // else if(val == "standard") is not needed cause that's the default anyway while (!atEnd()) { BREAK_IF_END_OF(CURRENT_EL) readNext(); } return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL firstSliceAng //! firstSliceAng (First Slice Angle) /*! ECMA-376, 21.2.2.68, p.3790. Parent elements: - doughnutChart (§21.2.2.50) - pieChart (§21.2.2.141) Child elements: - val (First Slice Angle Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_firstSliceAng() { if(Charting::PieImpl* pie = dynamic_cast(m_context->m_chart->m_impl)) { const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); pie->m_anStart = val.toInt(); // default value is zero } while (!atEnd()) { BREAK_IF_END_OF(CURRENT_EL) readNext(); } return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL holeSize //! holeSize (Hole Size) /*! ECMA-376, 21.2.2.82, p.3797. Parent elements: - doughnutChart (§21.2.2.50) Child elements: - val (Hole Size Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_holeSize() { if(Charting::RingImpl* ring = dynamic_cast(m_context->m_chart->m_impl)) { const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); ring->m_pcDonut = val.toInt(); // default value is zero } while (!atEnd()) { BREAK_IF_END_OF(CURRENT_EL) readNext(); } return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL bubbleSize //! bubbleSize (Bubble Size) /*! ECMA-376, 21.2.2.22, p.3876. Parent elements: - ser §21.2.2.174 Child elements: - [done] numLit (Number Literal) §21.2.2.122 - [done] numRef (Number Reference) §21.2.2.123 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_bubbleSize() { READ_PROLOGUE d->m_currentNumRef = &d->m_currentBubbleSize->m_numRef; d->m_currentNumLit = &d->m_currentBubbleSize->m_numLit; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(numRef) ELSE_TRY_READ_IF(numLit) ELSE_WRONG_FORMAT } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL bubbleScale //! bubbleScale (Bubble Scale) /*! ECMA-376, 21.2.2.21, p.3765. Parent elements: - bubbleChart (§21.2.2.20) Attributes: - [Done] val (Bubble Scale Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_bubbleScale() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); if(Charting::BubbleImpl* bubble = dynamic_cast(m_context->m_chart->m_impl)) { bool ok; const int i = val.toInt(&ok); if(ok) bubble->m_sizeRatio = i; } readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL bubble3D //! bubble3D (3D Bubble) /*! ECMA-376, 21.2.2.21, p.3765. Parent elements: - bubbleChart (§21.2.2.20) - dPt (§21.2.2.52) - ser (§21.2.2.174) Attributes: - [Done] val (Boolean Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_bubble3D() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); m_context->m_chart->m_is3d = val.toInt(); readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL numLit //! numLit (Number Literal) /*! ECMA-376, 21.2.2.122, p.3815. Parent elements: - bubbleSize (§21.2.2.22) - cat (§21.2.2.24) - minus (§21.2.2.113) - plus (§21.2.2.147) - val (§21.2.2.224) - xVal(§21.2.2.234) - yVal (§21.2.2.237) Child elements: - extLst (Chart Extensibility) §21.2.2.64 - formatCode (Format Code) §21.2.2.71 - [Done]pt (Numeric Point) §21.2.2.150 - [Done]ptCount (Point Count) §21.2.2.152 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_numLit() { READ_PROLOGUE d->m_currentPtCount = &d->m_currentNumLit->m_ptCount; d->m_currentPtCache = &d->m_currentNumLit->m_cache; while ( !atEnd() ) { readNext(); BREAK_IF_END_OF( CURRENT_EL ) if ( isStartElement() ) { TRY_READ_IF(ptCount) ELSE_TRY_READ_IF(pt) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL pt //! pt (String Point) /*! ECMA-376, 21.2.2.151, p.3831. Parent elements: - lvl (§21.2.2.99) - strCache (§21.2.2.199) - strLit (§21.2.2.200) Child elements: - [Done]v (Text Value) Attributes: - idx (Index) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_pt() { READ_PROLOGUE while ( !atEnd() ) { readNext(); BREAK_IF_END_OF( CURRENT_EL ) if ( isStartElement() ) { if ( qualifiedName() == QLatin1String( QUALIFIED_NAME( v ) ) ) { d->m_currentPtCache->append(readElementText()); } } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL order //! order (Order) /*! ECMA-376, 21.2.2.128, p.3817. Parent elements: - ser §21.2.2.168 - ser §21.2.2.170 - ser §21.2.2.174 - ser §21.2.2.171 - ser §21.2.2.172 - ser §21.2.2.169 - ser §21.2.2.167 - ser §21.2.2.173 Attributes: - [Done] val (Integer Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_order() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); *d->m_currentOrder = val.toInt(); readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL idx //! idx (Index) /*! ECMA-376, 21.2.2.84, p.3798. Parent elements: - bandFmt (§21.2.2.13) - dLbl (§21.2.2.47) - dPt (§21.2.2.52) - legendEntry (§21.2.2.94) - pivotFmt (§21.2.2.142) - ser §21.2.2.168 - ser §21.2.2.170 - ser §21.2.2.174 - ser §21.2.2.171 - ser §21.2.2.172 - ser §21.2.2.169 - ser §21.2.2.167 - ser §21.2.2.173 Attributes: - [Done] val (Integer Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_idx() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); *d->m_currentIdx = val.toInt(); readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL explosion //! explosion (Explosion) /*! ECMA-376, 21.2.2.61, p.3787. Parent elements: - dPt (§21.2.2.52) - ser (§21.2.2.172) Attributes: - [Done] val (Integer Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_explosion() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); *d->m_currentExplosion = val.toInt(); readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL strRef //! strRef (String Reference) /*! ECMA-376, 21.2.2.201, p.3857. Parent elements: - cat (§21.2.2.24) - tx (§21.2.2.215) - tx (§21.2.2.214) - xVal (§21.2.2.234) Child elements: - extLst (Chart Extensibility) §21.2.2.64 - [Done] f (Formula) §21.2.2.65 - [Done] strCache (String Cache) §21.2.2.199 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_strRef() { READ_PROLOGUE d->m_currentF = &d->m_currentStrRef->m_f; d->m_currentStrCache = &d->m_currentStrRef->m_strCache; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(f) ELSE_TRY_READ_IF(strCache) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL multiLvlStrRef //! multiLvlStrRef (Multi Level String Reference) /*! ECMA-376, 5.7.2.116, p.4060 Parent Elements: - cat (§5.7.2.24); xVal (§5.7.2.235) Child Elements: - extLst (Chart Extensibility) §5.7.2.64 - f (Formula) §5.7.2.65 - multiLvlStrCache (Multi Level String Cache) §5.7.2.115 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_multiLvlStrRef() { READ_PROLOGUE d->m_currentF = &d->m_currentStrRef->m_f; d->m_currentStrCache = &d->m_currentStrRef->m_strCache; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(f) ELSE_TRY_READ_IF(multiLvlStrCache) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL multiLvlStrCache KoFilter::ConversionStatus XlsxXmlChartReader::read_multiLvlStrCache() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(lvl) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL lvl KoFilter::ConversionStatus XlsxXmlChartReader::read_lvl() { READ_PROLOGUE d->m_currentPtCount = &d->m_currentStrCache->m_ptCount; d->m_currentPtCache = &d->m_currentStrCache->m_cache; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(ptCount) ELSE_TRY_READ_IF(pt) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL numRef //! numRef (String Reference) /*! ECMA-376, 21.2.2.123, p.3815. Parent elements: - bubbleSize (§21.2.2.22) - cat (§21.2.2.24) - minus (§21.2.2.113) - plus (§21.2.2.147) - val (§21.2.2.224) - xVal (§21.2.2.234) - yVal (§21.2.2.237) Child elements: - extLst (Chart Extensibility) §21.2.2.64 - [Done]f (Formula) §21.2.2.65 - [Done]numCache (Number Cache) §21.2.2.120 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_numRef() { READ_PROLOGUE d->m_currentF = &d->m_currentNumRef->m_f; d->m_currentNumCache = &d->m_currentNumRef->m_numCache; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(f) ELSE_TRY_READ_IF(numCache) } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL f //! f (Formula) /*! ECMA-376, 21.2.2.65, p.3789. Parent elements: - multiLvlStrRef (§21.2.2.115) - numRef (§21.2.2.123) - strRef (§21.2.2.201) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_f() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); *d->m_currentF = readElementText(); while (!atEnd()) { BREAK_IF_END_OF(CURRENT_EL) readNext(); } if (d->m_currentF->size() != 0) { QPair result = splitCellRange( *d->m_currentF ); m_context->m_chart->addRange( result.second ); } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL ptCount //! ptCount (Point Count) /*! ECMA-376, 21.2.2.152, p.3832. Parent elements: - multiLvlStrCache (§21.2.2.114) - numCache (§21.2.2.120) - numLit (§21.2.2.122) - strCache (§21.2.2.199) - strLit (§21.2.2.200) Attributes: - [Done] val (Integer Value) */ KoFilter::ConversionStatus XlsxXmlChartReader::read_ptCount() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString val(attrs.value("val").toString()); *d->m_currentPtCount = val.toInt(); readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL strCache //! strCache (String Cache) /*! ECMA-376, 21.2.2.199, p.3856. Parent elements: - strRef (§21.2.2.201) Child elements: - extLst (Chart Extensibility) §21.2.2.64 - [Done]pt (String Point) §21.2.2.151 - [Done]ptCount (Point Count) §21.2.2.152 */ KoFilter::ConversionStatus XlsxXmlChartReader::read_strCache() { READ_PROLOGUE d->m_currentPtCount = &d->m_currentStrCache->m_ptCount; d->m_currentPtCache = &d->m_currentStrCache->m_cache; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(ptCount) ELSE_TRY_READ_IF(pt) } } READ_EPILOGUE } int charToInt( const QString& string ) { if (string.isEmpty()) { return -1; } int ret = 0; int multiplier = 1; for(int i=string.size()-1; i>-1; i--,multiplier = multiplier*26) { - char val = string[i].toAscii(); + const char val = string[i].toLatin1(); if ( val >= 65 && val <= 90 ) { ret = ret + (val - 64)*multiplier; } else { ret = -1; break; } } return ret; } QString XlsxXmlChartReader::AlocateAndWriteIntoInternalTable(QVector< QString > &buffer, KoGenStyle::Type formatType) { if (buffer.size() == 0) return QString(); //create range where to place the data QString range("local"); Charting::InternalTable *internalTable = &m_context->m_chart->m_internalTable; range += "!$" + columnName(internalTable->maxColumn()+1) +"$" + "1" + ":$" + columnName(internalTable->maxColumn()+1) + "$" + QString::number(buffer.size()); WriteIntoInternalTable(range, buffer, formatType); return range; } QString convertToFormat( KoGenStyle::Type formatType ) { switch (formatType) { case KoGenStyle::NumericDateStyle: return "date"; case KoGenStyle::NumericTimeStyle: return "time"; case KoGenStyle::NumericPercentageStyle: return "percentage"; case KoGenStyle::NumericCurrencyStyle: return "currency"; case KoGenStyle::NumericTextStyle: return "string"; case KoGenStyle::NumericBooleanStyle: return "boolean"; case KoGenStyle::NumericNumberStyle: case KoGenStyle::NumericFractionStyle: case KoGenStyle::NumericScientificStyle: return "float"; default: kWarning() << "Unhandled format-type=" << formatType; break; } return "string"; } QString convertToFormat( KoGenStyle::Type formatType, const QString& formatString, const QString& value ) { switch (formatType) { case KoGenStyle::NumericDateStyle: { QString f = formatString; f.replace( QRegExp( "[m{1}]" ), "M" ); QDateTime dt( QDate( 1899, 12, 30 ) ); return dt.addDays( value.toInt() ).toString( f ); } case KoGenStyle::NumericTimeStyle: { QTime t(0,0,0,0); t.addSecs( value.toInt() ); return t.toString( Qt::ISODate ); } case KoGenStyle::NumericPercentageStyle: { return value + '%'; } /*TODO case KoGenStyle::NumericCurrencyStyle: case KoGenStyle::NumericBooleanStyle: case KoGenStyle::NumericFractionStyle: case KoGenStyle::NumericScientificStyle: */ case KoGenStyle::NumericNumberStyle: case KoGenStyle::NumericTextStyle: return value; default: kWarning() << "Unhandled format-type=" << formatType; break; } return value; } void XlsxXmlChartReader::WriteIntoInternalTable(QString &range, QVector< QString > &buffer, KoGenStyle::Type formatType, const QString& formatString) { if(range.isEmpty()) { return; } const QString sheet = range.section( '!', 0, 0 ); const QString cellRange = range.section( '!', 1, -1 ); const QStringList& res = cellRange.split( QRegExp( "[$:]" ), QString::SkipEmptyParts ); if (res.count() <= 1) { return; } int startColumn = charToInt( res[ 0 ] ); int startRow = res[ 1 ].toInt(); int endColumn = 0; int endRow = 0; if (res.size() >= 4) { endColumn = charToInt( res[ 2 ] ); endRow = res[ 3 ].toInt(); } else { endColumn = startColumn ; endRow = startRow; } // kDebug()<<"range " << range; // kDebug()<<"sheet " << sheet; // kDebug()<<"cellRange " << cellRange; // kDebug()<<"startColumn " << startColumn; // kDebug()<<"startRow " << startRow; // kDebug()<<"endColumn " << endColumn; // kDebug()<<"endRow " << endRow; // // kDebug()<<"buffer.size() " << buffer.size(); Charting::InternalTable *internalTable = &m_context->m_chart->m_internalTable; if (startColumn < endColumn) { if ((endColumn - startColumn +1) == buffer.size()) { int bufferIndex = 0; for(int i = startColumn; i <=endColumn; i++,bufferIndex++) { Charting::Cell *cell = internalTable->cell(i,startRow,true); cell->m_valueType = convertToFormat( formatType ); cell->m_value = convertToFormat( formatType, formatString, buffer[bufferIndex] ); // kDebug()<<"m_value " << format; // kDebug()<<"buffer[bufferIndex] " << buffer[bufferIndex]; // kDebug()<<"cell row" << startRow; // kDebug()<<"cell column " << i; } } } else if (startRow < endRow){ if ((endRow - startRow +1) == buffer.size()) { int bufferIndex = 0; for(int i = startRow; i <=endRow; i++,bufferIndex++) { Charting::Cell *cell = internalTable->cell(startColumn,i,true); cell->m_valueType = convertToFormat( formatType ); cell->m_value = convertToFormat( formatType, formatString, buffer[bufferIndex] ); // kDebug()<<"m_value " << format; // kDebug()<<"buffer[bufferIndex] " << buffer[bufferIndex]; // kDebug()<<"cell row" << i; // kDebug()<<"cell column " << startColumn; } } } else { if (buffer.size() != 0) { Charting::Cell *cell = internalTable->cell(startColumn,startRow,true); cell->m_valueType = convertToFormat( formatType ); cell->m_value = convertToFormat( formatType, formatString, buffer[ 0 ] ); // kDebug()<<"m_value " << format; // kDebug()<<"buffer[bufferIndex] " << buffer[0]; // kDebug()<<"cell row" << startRow; // kDebug()<<"cell column " << startColumn; } } } diff --git a/filters/sheets/xlsx/XlsxXmlWorksheetReader.cpp b/filters/sheets/xlsx/XlsxXmlWorksheetReader.cpp index 99c3eb59002..d4f74ac3012 100644 --- a/filters/sheets/xlsx/XlsxXmlWorksheetReader.cpp +++ b/filters/sheets/xlsx/XlsxXmlWorksheetReader.cpp @@ -1,2321 +1,2321 @@ /* * This file is part of Office 2007 Filters for Calligra * * Copyright (C) 2010 Sebastian Sauer * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Suresh Chande suresh.chande@nokia.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "XlsxXmlWorksheetReader.h" #include "XlsxXmlCommentsReader.h" #include "XlsxXmlStylesReader.h" #include "XlsxXmlDocumentReader.h" #include "XlsxXmlDrawingReader.h" #include "XlsxXmlChartReader.h" #include "XlsxXmlTableReader.h" #include "XlsxImport.h" #include "Charting.h" #include "ChartExport.h" #include "FormulaParser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "NumberFormatParser.h" #define XLSXXMLWORKSHEETREADER_CPP #define UNICODE_EUR 0x20AC #define UNICODE_GBP 0x00A3 #define UNICODE_JPY 0x00A5 #undef MSOOXML_CURRENT_NS // tags without namespace #define MSOOXML_CURRENT_CLASS XlsxXmlWorksheetReader #define BIND_READ_CLASS MSOOXML_CURRENT_CLASS #include #include // ---------------------------------------------------------------- // Include implementation of common tags #include // this adds p, pPr, t, r, etc. #undef MSOOXML_CURRENT_NS // tags without namespace #define MSOOXML_CURRENT_NS // ---------------------------------------------------------------- #define NO_DRAWINGML_NS #define NO_DRAWINGML_PIC_NS // DrawingML/Picture #include // this adds pic, etc. #include "XlsxXmlWorksheetReader_p.h" XlsxXmlWorksheetReaderContext::XlsxXmlWorksheetReaderContext( uint _worksheetNumber, uint _numberOfWorkSheets, const QString& _worksheetName, const QString& _state, const QString _path, const QString _file, MSOOXML::DrawingMLTheme*& _themes, const QVector& _sharedStrings, const XlsxComments& _comments, const XlsxStyles& _styles, MSOOXML::MsooXmlRelationships& _relationships, XlsxImport* _import, QMap _oleReplacements, QMap _oleBeginFrames, QVector& autoFilters) : MSOOXML::MsooXmlReaderContext(&_relationships) , sheet(new Sheet(_worksheetName)) , worksheetNumber(_worksheetNumber) , numberOfWorkSheets(_numberOfWorkSheets) , worksheetName(_worksheetName) , state(_state) , themes(_themes) , sharedStrings(&_sharedStrings) , comments(&_comments) , styles(&_styles) , import(_import) , path(_path) , file(_file) , oleReplacements(_oleReplacements) , oleFrameBegins(_oleBeginFrames) , autoFilters(autoFilters) { } XlsxXmlWorksheetReaderContext::~XlsxXmlWorksheetReaderContext() { delete sheet; } static void splitToRowAndColumn(const QString source, QString& row, int& column) { // Checking whether the 2nd char is a number - char second = source.at(1).toAscii(); + const char second = source.at(1).toLatin1(); if (second < 65) { row = source.at(0); column = source.mid(1).toInt(); } else { row = source.left(2); column = source.mid(2).toInt(); } } //! @return value @a cm with cm suffix static QString printCm(double cm) { QString string; string.sprintf("%3.3fcm", cm); return string; } QList > XlsxXmlWorksheetReaderContext::conditionalStyleForPosition(const QString& positionLetter, int positionNumber) { QString startLetter, endLetter; int startNumber, endNumber; QList > returnMaps; // Known positions which are hits/misses for this // purpose is to optimize this code part for a large set of conditions QList cachedHits, cachedMisses; // We do not wish to add the same condition twice QList addedConditions; int index = 0; while (index < conditionalStyles.size()) { QString range = conditionalStyles.at(index).first; if (cachedHits.contains(range)) { if (!addedConditions.contains(conditionalStyles.at(index).second.value("style:condition"))) { returnMaps.push_back(conditionalStyles.at(index).second); addedConditions.push_back(conditionalStyles.at(index).second.value("style:condition")); } ++index; continue; } if (cachedMisses.contains(range)) { ++index; continue; } int columnIndex = range.indexOf(':'); if (columnIndex < 0) { splitToRowAndColumn(range, startLetter, startNumber); endLetter.clear(); } else { splitToRowAndColumn(range.left(columnIndex), startLetter, startNumber); splitToRowAndColumn(range.mid(columnIndex + 1), endLetter, endNumber); } if ((positionLetter == startLetter && positionNumber == startNumber && endLetter.isEmpty()) || (positionLetter >= startLetter && positionNumber >= startNumber && positionLetter <= endLetter && positionNumber <= endNumber)) { if (!addedConditions.contains(conditionalStyles.at(index).second.value("style:condition"))) { returnMaps.push_back(conditionalStyles.at(index).second); addedConditions.push_back(conditionalStyles.at(index).second.value("style:condition")); } cachedHits.push_back(range); ++index; continue; } else { cachedMisses.push_back(range); ++index; continue; } ++index; } return returnMaps; } const char* XlsxXmlWorksheetReader::officeValue = "office:value"; const char* XlsxXmlWorksheetReader::officeDateValue = "office:date-value"; const char* XlsxXmlWorksheetReader::officeStringValue = "office:string-value"; const char* XlsxXmlWorksheetReader::officeTimeValue = "office:time-value"; const char* XlsxXmlWorksheetReader::officeBooleanValue = "office:boolean-value"; class XlsxXmlWorksheetReader::Private { public: Private( XlsxXmlWorksheetReader* qq ) : q( qq ), warningAboutWorksheetSizeDisplayed(false), drawingNumber(0) { } //~Private(){ qDeleteAll( savedStyles ); } XlsxXmlWorksheetReader* const q; bool warningAboutWorksheetSizeDisplayed; int drawingNumber; QHash sharedFormulas; QHash savedStyles; }; XlsxXmlWorksheetReader::XlsxXmlWorksheetReader(KoOdfWriters *writers) : MSOOXML::MsooXmlCommonReader(writers) , m_context(0) , d(new Private( this ) ) { init(); } XlsxXmlWorksheetReader::~XlsxXmlWorksheetReader() { delete d; } void XlsxXmlWorksheetReader::init() { initInternal(); // MsooXmlCommonReaderImpl.h initDrawingML(); m_defaultNamespace = ""; m_columnCount = 0; m_currentRow = 0; m_currentColumn = 0; } KoFilter::ConversionStatus XlsxXmlWorksheetReader::read(MSOOXML::MsooXmlReaderContext* context) { m_context = dynamic_cast(context); Q_ASSERT(m_context); const KoFilter::ConversionStatus result = readInternal(); m_context = 0; return result; } KoFilter::ConversionStatus XlsxXmlWorksheetReader::readInternal() { kDebug() << "============================="; Q_ASSERT(m_context); readNext(); if (!isStartDocument()) { return KoFilter::WrongFormat; } // worksheet readNext(); //kDebug() << *this << namespaceUri(); if (name() != "worksheet" && name() != "dialogsheet" && name() != "chartsheet") { return KoFilter::WrongFormat; } if (!expectNS(MSOOXML::Schemas::spreadsheetml)) { return KoFilter::WrongFormat; } m_context->sheet->setVisible( m_context->state.toLower() != "hidden" ); QXmlStreamNamespaceDeclarations namespaces(namespaceDeclarations()); for (int i = 0; i < namespaces.count(); i++) { kDebug() << "NS prefix:" << namespaces[i].prefix() << "uri:" << namespaces[i].namespaceUri(); } //! @todo find out whether the namespace returned by namespaceUri() //! is exactly the same ref as the element of namespaceDeclarations() if (!namespaces.contains(QXmlStreamNamespaceDeclaration("", MSOOXML::Schemas::spreadsheetml))) { raiseError(i18n("Namespace \"%1\" not found", MSOOXML::Schemas::spreadsheetml)); return KoFilter::WrongFormat; } //! @todo expect other namespaces too... if (name() == "worksheet") { TRY_READ(worksheet) } else if (name() == "dialogsheet") { TRY_READ(dialogsheet) } kDebug() << "===========finished============"; return KoFilter::OK; } QString XlsxXmlWorksheetReader::computeColumnWidth(qreal widthNumber) const { //! CASE #S3300 //! Column width measured as the number of characters of the maximum digit width of the //! numbers 0, 1, 2, …, 9 as rendered in the normal style's font. There are 4 pixels of margin //! padding (two on each side), plus 1 pixel padding for the gridlines. //! For explanation of width, see p. 1778 //simplified: //! @todo hardcoded, not 100% accurate kDebug() << "PT_TO_PX(11.0):" << PT_TO_PX(11.0); const double realSize = round(PT_TO_PX(11.0)) * 0.75; kDebug() << "realSize:" << realSize; const double averageDigitWidth = realSize * 2.0 / 3.0; kDebug() << "averageDigitWidth:" << averageDigitWidth; QString result; if (averageDigitWidth * widthNumber == 0) { result = QLatin1String("0cm"); } else { result = printCm(PX_TO_CM(averageDigitWidth * widthNumber)); } return result; } void XlsxXmlWorksheetReader::showWarningAboutWorksheetSize() { if (d->warningAboutWorksheetSizeDisplayed) return; d->warningAboutWorksheetSizeDisplayed = true; kWarning() << i18n("The data could not be loaded completely because the maximum size of " "sheet was exceeded."); } inline static QString encodeLabelText(int col, int row) { return Calligra::Sheets::Util::encodeColumnLabelText(col) + QString::number(row); } void XlsxXmlWorksheetReader::saveAnnotation(int col, int row) { QString ref(encodeLabelText(col + 1, row + 1)); kDebug() << ref; XlsxComment *comment = m_context->comments->value(ref); if (!comment) return; //kDebug() << "Saving annotation for cell" << ref; body->startElement("office:annotation"); body->startElement("dc:creator"); body->addTextNode(comment->author(m_context->comments)); body->endElement(); // dc:creator //! @todo support dc:date body->startElement("text:p"); body->addCompleteElement(comment->texts.toUtf8()); body->endElement(); // text:p body->endElement(); // office:annotation } #undef CURRENT_EL #define CURRENT_EL chartsheet KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_chartsheet() { READ_PROLOGUE return read_sheetHelper("chartsheet"); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL dialogsheet KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_dialogsheet() { READ_PROLOGUE return read_sheetHelper("dialogsheet"); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL worksheet //! worksheet handler (Worksheet) /*! ECMA-376, 18.3.1.99, p. 1894. Root element of Worksheet parts within a SpreadsheetML document. Child elements: - [done] autoFilter (AutoFilter Settings) §18.3.1.2 - cellWatches (Cell Watch Items) §18.3.1.9 - colBreaks (Vertical Page Breaks) §18.3.1.14 - [done] cols (Column Information) §18.3.1.17 - [done] conditionalFormatting (Conditional Formatting) §18.3.1.18 - [done] controls (Embedded Controls) §18.3.1.21 - customProperties (Custom Properties) §18.3.1.23 - customSheetViews (Custom Sheet Views) §18.3.1.27 - dataConsolidate (Data Consolidate) §18.3.1.29 - dataValidations (Data Validations) §18.3.1.33 - dimension (Worksheet Dimensions) §18.3.1.35 - [done] drawing (Drawing) §18.3.1.36 - drawingHF (Drawing Reference in Header Footer) §18.3.1.37 - extLst (Future Feature Data Storage Area) §18.2.10 - headerFooter (Header Footer Settings) §18.3.1.46 - [done] hyperlinks (Hyperlinks) §18.3.1.48 - ignoredErrors (Ignored Errors) §18.3.1.51 - [done] mergeCells (Merge Cells) §18.3.1.55 - [done] oleObjects (Embedded Objects) §18.3.1.60 - pageMargins (Page Margins) §18.3.1.62 - pageSetup (Page Setup Settings) §18.3.1.63 - phoneticPr (Phonetic Properties) §18.4.3 - [done] picture (Background Image) §18.3.1.67 - printOptions (Print Options) §18.3.1.70 - protectedRanges (Protected Ranges) §18.3.1.72 - rowBreaks (Horizontal Page Breaks (Row)) §18.3.1.74 - scenarios (Scenarios) §18.3.1.76 - sheetCalcPr (Sheet Calculation Properties) §18.3.1.79 - [done] sheetData (Sheet Data) §18.3.1.80 - sheetFormatPr (Sheet Format Properties) §18.3.1.81 - sheetPr (Sheet Properties) §18.3.1.82 - sheetProtection (Sheet Protection Options) §18.3.1.85 - sheetViews (Sheet Views) §18.3.1.88 - smartTags (Smart Tags) §18.3.1.90 - sortState (Sort State) §18.3.1.92 - [done] tableParts (Table Parts) §18.3.1.95 - webPublishItems (Web Publishing Items) §18.3.1.98 @todo support all child elements */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_worksheet() { READ_PROLOGUE return read_sheetHelper("worksheet"); READ_EPILOGUE } KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_sheetHelper(const QString& type) { // In the first round we do not wish to output anything QBuffer fakeBuffer; KoXmlWriter fakeBody(&fakeBuffer); KoXmlWriter *oldBody = body; if (m_context->firstRoundOfReading) { body = &fakeBody; } body->startElement("table:table"); //! @todo implement CASE #S202 for fixing the name body->addAttribute("table:name", m_context->worksheetName); m_tableStyle = KoGenStyle(KoGenStyle::TableAutoStyle, "table"); //! @todo hardcoded master page name m_tableStyle.addAttribute("style:master-page-name", QString("PageStyle_5f_Test_20_sheet_20__5f_%1").arg(m_context->worksheetNumber)); m_tableStyle.addProperty("table:display", m_context->sheet->visible()); //The style might be changed depending on what elements we find, //hold the body writer so that we can set the proper style KoXmlWriter* heldBody = body; QBuffer bodyBuffer; bodyBuffer.open(QIODevice::ReadWrite); body = new KoXmlWriter(&bodyBuffer); QBuffer drawingBuffer; drawingBuffer.open(QIODevice::ReadWrite); while (!atEnd()) { readNext(); kDebug() << *this; if (isEndElement() && name() == type) { break; } if (isStartElement() && !m_context->firstRoundOfReading) { TRY_READ_IF(sheetFormatPr) ELSE_TRY_READ_IF(cols) ELSE_TRY_READ_IF(sheetData) // does fill the m_context->sheet ELSE_TRY_READ_IF(mergeCells) else if (name() == "drawing") { KoXmlWriter *tempBodyHolder = body; body = new KoXmlWriter(&drawingBuffer); TRY_READ(drawing) delete body; body = tempBodyHolder; } ELSE_TRY_READ_IF(legacyDrawing) ELSE_TRY_READ_IF(hyperlinks) ELSE_TRY_READ_IF(picture) ELSE_TRY_READ_IF(oleObjects) else if (name() == "controls") { KoXmlWriter *tempBodyHolder = body; body = new KoXmlWriter(&drawingBuffer); TRY_READ(controls) delete body; body = tempBodyHolder; } ELSE_TRY_READ_IF(autoFilter) SKIP_UNKNOWN } else if (isStartElement() && m_context->firstRoundOfReading) { TRY_READ_IF(conditionalFormatting) ELSE_TRY_READ_IF(tableParts) SKIP_UNKNOWN } } if (m_context->firstRoundOfReading) { // Sorting conditional styles according to the priority typedef QPair > Condition; // Transforming to a list for easier handling QList >, int> > diffFormulasList; QMapIterator > i(m_conditionalStyles); while (i.hasNext()) { i.next(); int index = 0; QList conditions = i.value(); QPair > innerPair; QPair >, int> outerPair; while (index < conditions.size()) { innerPair.first = i.key(); innerPair.second = conditions.at(index).second; outerPair.first = innerPair; outerPair.second = conditions.at(index).first; diffFormulasList.push_back(outerPair); ++index; } } QList > priorityActualIndex; int index = 0; while (index < diffFormulasList.size()) { priorityActualIndex.push_back(QPair(diffFormulasList.at(index).second, index)); ++index; } qSort(priorityActualIndex); // Finally we have the list sorted and we can store the conditions in right priority order index = 0; while (index < priorityActualIndex.size()) { QPair > odfValue; odfValue.first = diffFormulasList.at(priorityActualIndex.at(index).second).first.first; odfValue.second = diffFormulasList.at(priorityActualIndex.at(index).second).first.second; m_context->conditionalStyles.push_back(odfValue); ++index; } } if( !m_context->sheet->pictureBackgroundPath().isNull() ) { QBuffer buffer; buffer.open(QIODevice::WriteOnly); KoXmlWriter writer(&buffer); writer.startElement("style:background-image"); writer.addAttribute("xlink:href", m_context->sheet->pictureBackgroundPath()); writer.addAttribute("xlink:type", "simple"); writer.addAttribute("xlink:show", "embed"); writer.addAttribute("xlink:actuate", "onLoad"); writer.endElement(); buffer.close(); m_tableStyle.addChildElement("style:background-image", QString::fromUtf8(buffer.buffer(), buffer.buffer().size())); } const QString currentTableStyleName(mainStyles->insert(m_tableStyle, "ta")); heldBody->addAttribute("table:style-name", currentTableStyleName); heldBody->addCompleteElement(&bodyBuffer); delete body; body = heldBody; // Adding drawings, if there are any if (drawingBuffer.size() > 0) { body->startElement("table:shapes"); body->addCompleteElement(&drawingBuffer); body->endElement(); // table:shapes } // now we have everything to start writing the actual cells int c = 0; while (c <= m_context->sheet->maxColumn()) { body->startElement("table:table-column"); int repeatedColumns = 1; bool currentColumnHidden = false; Column* column = m_context->sheet->column(c, false); if (column) { if (!column->styleName.isEmpty()) { body->addAttribute("table:style-name", column->styleName); } else { if (m_context->sheet->m_defaultColWidth != -1.0) { saveColumnStyle( computeColumnWidth( m_context->sheet->m_defaultColWidth ) ); } } } if (column && column->hidden) { body->addAttribute("table:visibility", "collapse"); currentColumnHidden = true; } ++c; while (c <= m_context->sheet->maxColumn()) { column = m_context->sheet->column(c, false); if (column && column->hidden ) { if (currentColumnHidden) { ++repeatedColumns; } else { break; } } else { if (!currentColumnHidden) { ++repeatedColumns; } else { break; } } ++c; } if (repeatedColumns > 1) { body->addAttribute("table:number-columns-repeated", repeatedColumns); } body->endElement(); // table:table-column } const int rowCount = m_context->sheet->maxRow(); for(int r = 0; r <= rowCount; ++r) { const int columnCount = m_context->sheet->maxCellsInRow(r); Row* row = m_context->sheet->row(r, false); body->startElement("table:table-row"); if (row) { if (!row->styleName.isEmpty()) { body->addAttribute("table:style-name", row->styleName); } else if (m_context->sheet->m_defaultRowHeight != -1.0) { QString styleName = processRowStyle(m_context->sheet->m_defaultRowHeight); // in pt body->addAttribute("table:style-name", styleName); } if (row->hidden) { body->addAttribute("table:visibility", "collapse"); } //body->addAttribute("table:number-rows-repeated", QByteArray::number(row->repeated)); for(int c = 0; c <= columnCount; ++c) { body->startElement("table:table-cell"); if (Cell* cell = m_context->sheet->cell(c, r, false)) { const bool hasHyperlink = ! cell->hyperlink().isEmpty(); if (!cell->styleName.isEmpty()) { body->addAttribute("table:style-name", cell->styleName); } //body->addAttribute("table:number-columns-repeated", QByteArray::number(cell->repeated)); if (!hasHyperlink) { switch(cell->valueType) { case Cell::ConstNone: break; case Cell::ConstString: body->addAttribute("office:value-type", MsooXmlReader::constString); break; case Cell::ConstBoolean: body->addAttribute("office:value-type", MsooXmlReader::constBoolean); break; case Cell::ConstDate: body->addAttribute("office:value-type", MsooXmlReader::constDate); break; case Cell::ConstFloat: body->addAttribute("office:value-type", MsooXmlReader::constFloat); break; } } if (cell->valueAttrValue) { switch(cell->valueAttr) { case Cell::OfficeNone: break; case Cell::OfficeValue: body->addAttribute(XlsxXmlWorksheetReader::officeValue, *cell->valueAttrValue); break; case Cell::OfficeStringValue: body->addAttribute(XlsxXmlWorksheetReader::officeStringValue, *cell->valueAttrValue); break; case Cell::OfficeBooleanValue: // Treat boolean values specially (ODF1.1 chapter 6.7.1) //! @todo This breaks down if the value is a formula and not constant. body->addAttribute(XlsxXmlWorksheetReader::officeBooleanValue, *cell->valueAttrValue == "0" ? "false" : "true"); break; case Cell::OfficeDateValue: body->addAttribute(XlsxXmlWorksheetReader::officeDateValue, *cell->valueAttrValue); break; } } if (cell->formula) { QString formula; if (cell->formula->isShared()) { Cell *referencedCell = static_cast(cell->formula)->m_referencedCell; Q_ASSERT(referencedCell); formula = MSOOXML::convertFormulaReference(referencedCell, cell); } else { formula = static_cast(cell->formula)->m_formula; } if (!formula.isEmpty()) { body->addAttribute("table:formula", formula); } } if (cell->rowsMerged > 1) { body->addAttribute("table:number-rows-spanned", cell->rowsMerged); } if (cell->columnsMerged > 1) { body->addAttribute("table:number-columns-spanned", cell->columnsMerged); } saveAnnotation(c, r); if (!cell->text.isEmpty() || !cell->charStyleName.isEmpty() || hasHyperlink) { body->startElement("text:p", false); if (!cell->charStyleName.isEmpty()) { body->startElement( "text:span" ); body->addAttribute( "text:style-name", cell->charStyleName); } if (hasHyperlink) { body->startElement("text:a"); body->addAttribute("xlink:href", cell->hyperlink()); body->addAttribute("xlink:type", "simple"); //body->addAttribute("office:target-frame-name", targetFrameName); if(cell->text.isEmpty()) { body->addTextNode(cell->hyperlink()); } else { body->addCompleteElement(cell->text.toUtf8()); } body->endElement(); // text:a } else if (!cell->text.isEmpty()) { body->addCompleteElement(cell->text.toUtf8()); } if (!cell->charStyleName.isEmpty()) { body->endElement(); // text:span } body->endElement(); // text:p } // handle drawing objects like e.g. charts, diagrams and pictures if ( cell->embedded ) { foreach(XlsxDrawingObject* drawing, cell->embedded->drawings) { drawing->save(body); } QPair oleObject; int listIndex = 0; foreach( oleObject, cell->embedded->oleObjects ) { const QString olePath = oleObject.first; const QString previewPath = oleObject.second; body->addCompleteElement(cell->embedded->oleFrameBegins.at(listIndex).toUtf8()); ++listIndex; body->startElement("draw:object-ole"); body->addAttribute("xlink:href", olePath); body->addAttribute("xlink:type", "simple"); body->addAttribute("xlink:show", "embed"); body->addAttribute("xlink:actuate", "onLoad"); body->endElement(); // draw:object-ole body->startElement("draw:image"); body->addAttribute("xlink:href", previewPath); body->addAttribute("xlink:type", "simple"); body->addAttribute("xlink:show", "embed"); body->addAttribute("xlink:actuate", "onLoad"); body->endElement(); // draw:image body->addCompleteElement(""); } } } body->endElement(); // table:table-cell } } if (!row || columnCount <= 0) { // element table:table-row may not be empty body->startElement("table:table-cell"); body->endElement(); // table:table-cell } body->endElement(); // table:table-row } body->endElement(); // table:table if (m_context->firstRoundOfReading) { body = oldBody; } return KoFilter::OK; } #undef CURRENT_EL #define CURRENT_EL conditionalFormatting /* Parent elements: - [done] worksheet (§18.3.1.99) Child elements: - [done] cfRule (Conditional Formatting Rule) §18.3.1.10 - extLst (Future Feature Data Storage Area) §18.2.10 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_conditionalFormatting() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(sqref) // Getting rid of previously handled conditions m_conditionalIndices.clear(); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(cfRule) SKIP_UNKNOWN } } QList areas; while (sqref.indexOf(' ') > 0) { QString conditionArea = sqref.left(sqref.indexOf(' ')); sqref = sqref.mid(conditionArea.length() + 1); areas.push_back(conditionArea); } areas.push_back(sqref); typedef QPair > Condition; // Adding conditions to list of conditions and making sure that only the one with highest priority // remains if there are multiple conditions with same area & condition // This is done because some ooxml files have same condition for some area listed multiple times but // with different priorities int index = 0; while (index < m_conditionalIndices.size()) { QString conditionalArea; Condition examinedCondition = m_conditionalIndices.at(index); QString sqrefOriginal = sqref; int areaIndex = 0; Condition previousCond; while (areaIndex < areas.size()) { conditionalArea = areas.at(areaIndex); QList previousConditions = m_conditionalStyles.value(conditionalArea); if (previousConditions.isEmpty()) { previousConditions.push_back(examinedCondition); m_conditionalStyles[conditionalArea] = previousConditions; } else { int conditionIndex = 0; bool hasTheSameCondition = false; while (conditionIndex < previousConditions.size()) { // When comparing we only care about the condition, not the style if (previousConditions.at(conditionIndex).second.value("style:condition") == examinedCondition.second.value("style:condition")) { hasTheSameCondition = true; previousCond = previousConditions.at(conditionIndex); if (previousCond.first > examinedCondition.first) { previousConditions.replace(conditionIndex, examinedCondition); m_conditionalStyles[conditionalArea] = previousConditions; } break; } ++conditionIndex; } if (!hasTheSameCondition) { previousConditions.push_back(examinedCondition); m_conditionalStyles[conditionalArea] = previousConditions; } } ++areaIndex; } ++index; } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL cfRule /* Parent elements: - [done] conditionalFormatting (§18.3.1.18) Child elements: - colorScale (Color Scale) §18.3.1.16 - dataBar (Data Bar) §18.3.1.28 - extLst (Future Feature Data Storage Area) §18.2.10 - [done] formula (Formula) §18.3.1.43 - iconSet (Icon Set) §18.3.1.49 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_cfRule() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(type) TRY_READ_ATTR_WITHOUT_NS(dxfId) TRY_READ_ATTR_WITHOUT_NS(priority) QString op = attrs.value("operator").toString(); QList formulas; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (name() == "formula") { TRY_READ(formula) formulas.push_back(m_formula); } SKIP_UNKNOWN } } QMap odf; // TODO, use attributes to really interpret this // The default one here is valid for type="cellIs" operator="equal" if (op == "equal") { odf["style:condition"] = QString("cell-content()=%1").arg(m_formula); } else if (op == "lessThan") { odf["style:condition"] = QString("cell-content()<%1").arg(m_formula); } else if (op == "greaterThan") { odf["style:condition"] = QString("cell-content()>%1").arg(m_formula); } else if (op == "between") { odf["style:condition"] = QString("cell-content-is-between(%1, %2)").arg(formulas.at(0)).arg(formulas.at(1)); } odf["style:apply-style-name"] = m_context->styles->conditionalStyle(dxfId.toInt() + 1); m_conditionalIndices.push_back(QPair >(priority.toInt(), odf)); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL formula /* Parent elements: - [done] cfRule (§18.3.1.10) - rdn (§18.11.1.13) Child elements: - none */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_formula() { READ_PROLOGUE READ_PROLOGUE while (!atEnd()) { readNext(); if (isCharacters()) { m_formula = text().toString(); } BREAK_IF_END_OF(CURRENT_EL) } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL sheetFormatPr //! sheetFormatPr handler (Sheet Format Properties) /*! ECMA-376, 18.3.1.81, p. 1866. Sheet formatting properties. No child elements. Parent elements: - dialogsheet (§18.3.1.34) - [done] worksheet (§18.3.1.99) @todo support all attributes and elements */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_sheetFormatPr() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(defaultRowHeight) // in pt TRY_READ_ATTR_WITHOUT_NS(defaultColWidth) TRY_READ_ATTR_WITHOUT_NS(baseColWidth) bool ok; const double drh = defaultRowHeight.toDouble(&ok); if (ok) { m_context->sheet->m_defaultRowHeight = drh; } const double dcw = defaultColWidth.toDouble(&ok); if (ok) { m_context->sheet->m_defaultColWidth = dcw; } const double bcw = baseColWidth.toDouble(&ok); if (ok) { m_context->sheet->m_baseColWidth = bcw; } readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL cols //! cols handler (Column Information) /*! ECMA-376, 18.3.1.17, p. 1782. Information about whole columns of the worksheet. Child elements: - [done] col (Column Width & Formatting) §18.3.1.13 Parent elements: - [done] worksheet (§18.3.1.99) */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_cols() { READ_PROLOGUE while (!atEnd()) { readNext(); kDebug() << *this; BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(col) ELSE_WRONG_FORMAT } } READ_EPILOGUE_WITHOUT_RETURN // append remaining empty columns appendTableColumns(MSOOXML::maximumSpreadsheetColumns() - m_columnCount); return KoFilter::OK; } //! Saves information about column style void XlsxXmlWorksheetReader::saveColumnStyle(const QString& widthString) { if ( !d->savedStyles.contains( widthString ) ) { KoGenStyle tableColumnStyle(KoGenStyle::TableColumnAutoStyle, "table-column"); tableColumnStyle.addProperty("style:column-width", widthString); tableColumnStyle.addProperty("fo:break-before", "auto"); const QString currentTableColumnStyleName(mainStyles->insert(tableColumnStyle, "co")); body->addAttribute("table:style-name", currentTableColumnStyleName); d->savedStyles[widthString] = currentTableColumnStyleName; } else { const QString currentTableColumnStyleName(d->savedStyles[widthString]); body->addAttribute("table:style-name", currentTableColumnStyleName); } } void XlsxXmlWorksheetReader::appendTableColumns(int columns, const QString& width) { kDebug() << "columns:" << columns; if (columns <= 0) return; body->startElement("table:table-column"); if (columns > 1) body->addAttribute("table:number-columns-repeated", QByteArray::number(columns)); //! @todo hardcoded table:default-cell-style-name body->addAttribute("table:default-cell-style-name", "Excel_20_Built-in_20_Normal"); //! @todo hardcoded default style:column-width saveColumnStyle(width.isEmpty() ? QLatin1String("1.707cm") : width); body->endElement(); // table:table-column } #undef CURRENT_EL #define CURRENT_EL col //! col handler (Column Width & Formatting) /*! ECMA-376, 18.3.1.13, p. 1777. Defines column width and column formatting for one or more columns of the worksheet. No child elements. Parent elements: - [done] cols (§18.3.1.17) @todo support more attributes */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_col() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); Column* column = m_context->sheet->column(m_columnCount, true); ++m_columnCount; //moved body->startElement("table:table-column"); // CASE #S2500? int minCol = m_columnCount; int maxCol = m_columnCount; QString minStr, maxStr; TRY_READ_ATTR_WITHOUT_NS_INTO(min, minStr) STRING_TO_INT(minStr, minCol, "col@min") TRY_READ_ATTR_WITHOUT_NS_INTO(max, maxStr) STRING_TO_INT(maxStr, maxCol, "col@min") if (minCol > maxCol) qSwap(minCol, maxCol); if (m_columnCount < minCol) { appendTableColumns(minCol - m_columnCount); m_columnCount = minCol; } TRY_READ_ATTR_WITHOUT_NS(width) QString realWidthString; if (!width.isEmpty()) { bool ok; double widthNumber = width.toDouble(&ok); if (!ok) return KoFilter::WrongFormat; realWidthString = computeColumnWidth(widthNumber); kDebug() << "realWidthString:" << realWidthString; //moved saveColumnStyle(realWidthString); //! @todo hardcoded table:default-cell-style-name //moved body->addAttribute("table:default-cell-style-name", "Excel_20_Built-in_20_Normal"); } // we apparently don't need "customWidth" attr TRY_READ_ATTR_WITHOUT_NS(hidden) if (!hidden.isEmpty()) { column->hidden = hidden.toInt() > 0; } //moved body->endElement(); // table:table-column appendTableColumns(maxCol - minCol + 1, realWidthString); if (d->savedStyles.contains(realWidthString)) { column->styleName = d->savedStyles.value(realWidthString); } m_columnCount += (maxCol - minCol); if (m_columnCount > (int)MSOOXML::maximumSpreadsheetColumns()) { showWarningAboutWorksheetSize(); } readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL sheetData //! sheetData handler (Sheet Data) /*! ECMA-376, 18.3.1.80, p. 1866. This collection represents the cell table itself. This collection expresses information about each cell, grouped together by rows in the worksheet. Child elements: - [done] row (Row) §18.3.1.73 Parent elements: - [done] worksheet (§18.3.1.99) */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_sheetData() { READ_PROLOGUE m_currentRow = 0; while (!atEnd()) { readNext(); kDebug() << *this; BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(row) ELSE_WRONG_FORMAT } } READ_EPILOGUE } QString XlsxXmlWorksheetReader::processRowStyle(qreal height) { if (height == -1.0) { height = m_context->sheet->m_defaultRowHeight; } KoGenStyle tableRowStyle(KoGenStyle::TableRowAutoStyle, "table-row"); //! @todo alter fo:break-before? tableRowStyle.addProperty("fo:break-before", MsooXmlReader::constAuto); //! @todo alter style:use-optimal-row-height? tableRowStyle.addProperty("style:use-optimal-row-height", MsooXmlReader::constFalse); if (height >= 0.0) { tableRowStyle.addProperty("style:row-height", printCm(POINT_TO_CM(height))); } const QString currentTableRowStyleName(mainStyles->insert(tableRowStyle, "ro")); return currentTableRowStyleName; } void XlsxXmlWorksheetReader::appendTableCells(int cells) { if (cells <= 0) return; body->startElement("table:table-cell"); if (cells > 1) body->addAttribute("table:number-columns-repeated", QByteArray::number(cells)); body->endElement(); // table:table-cell } #undef CURRENT_EL #define CURRENT_EL row //! row handler (Row) /*! ECMA-376, 18.3.1.73, p. 1855. The element expresses information about an entire row of a worksheet, and contains all cell definitions for a particular row in the worksheet. Child elements: - [done] c (Cell) §18.3.1.4 - extLst (Future Feature Data Storage Area) §18.2.10 Parent elements: - [done] sheetData (§18.3.1.80) @todo support all child elements */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_row() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(r) //TRY_READ_ATTR_WITHOUT_NS(spans) // spans are only an optional help TRY_READ_ATTR_WITHOUT_NS(ht) //TRY_READ_ATTR_WITHOUT_NS(customHeight) not used atm TRY_READ_ATTR_WITHOUT_NS(hidden) if (!r.isEmpty()) { bool ok; m_currentRow = r.toInt(&ok) - 1; if (!ok || m_currentRow < 0) return KoFilter::WrongFormat; } if (m_currentRow > (int)MSOOXML::maximumSpreadsheetRows()) { showWarningAboutWorksheetSize(); } m_currentColumn = 0; Row* row = m_context->sheet->row(m_currentRow, true); if (!ht.isEmpty()) { bool ok; qreal height = ht.toDouble(&ok); if (ok) { row->styleName = processRowStyle(height); } } if (!hidden.isEmpty()) { row->hidden = hidden.toInt() > 0; } qreal range = (55.0/m_context->numberOfWorkSheets); int counter = 0; while (!atEnd()) { readNext(); kDebug() << *this; BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (counter == 40) { // set the progress by the position of what was read qreal progress = 45 + range * (m_context->worksheetNumber - 1) + range * device()->pos() / device()->size(); m_context->import->reportProgress(progress); counter = 0; } ++counter; TRY_READ_IF(c) // modifies m_currentColumn SKIP_UNKNOWN } } ++m_currentRow; // This row is done now. Select the next row. READ_EPILOGUE } //! @return true if @a v represents an integer or floating-point number static bool valueIsNumeric(const QString& v) { bool ok; v.toDouble(&ok); return ok; } #undef CURRENT_EL #define CURRENT_EL c //! c handler (Cell) /*! ECMA-376, 18.3.1.4, p. 1767. This collection represents a cell in the worksheet. Information about the cell's location (reference), value, data type, formatting, and formula is expressed here. Child elements: - extLst (Future Feature Data Storage Area) §18.2.10 - [done] f (Formula) §18.3.1.40 - is (Rich Text Inline) §18.3.1.53 - [done] v (Cell Value) §18.3.1.96 Parent elements: - [done] row (§18.3.1.73) @todo support all child elements */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_c() { Row* row = m_context->sheet->row(m_currentRow, false); Q_ASSERT(row); Q_UNUSED(row); READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(r) if (!r.isEmpty()) { m_currentColumn = Calligra::Sheets::Util::decodeColumnLabelText(r) - 1; if (m_currentColumn < 0) return KoFilter::WrongFormat; } TRY_READ_ATTR_WITHOUT_NS(s) TRY_READ_ATTR_WITHOUT_NS(t) m_value.clear(); Cell* cell = m_context->sheet->cell(m_currentColumn, m_currentRow, true); while (!atEnd()) { readNext(); kDebug() << *this; BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(f) ELSE_TRY_READ_IF(v) SKIP_UNKNOWN } } bool ok; uint styleId = s.toUInt(&ok); const XlsxCellFormat* cellFormat = m_context->styles->cellFormat(styleId); QString formattedStyle; if (cellFormat->applyNumberFormat) formattedStyle = m_context->styles->numberFormatStyleName( cellFormat->numFmtId ); //kDebug() << "type=" << t << "styleId=" << styleId << "applyNumberFormat=" << cellFormat->applyNumberFormat << "numberFormat=" << numberFormat << "value=" << m_value; QString charStyleName; // const bool addTextPElement = true;//m_value.isEmpty() || t != QLatin1String("s"); if (!m_value.isEmpty()) { /* depending on type: 18.18.11 ST_CellType (Cell Type), p. 2679: b (Boolean) Cell containing a boolean. d (Date) Cell contains a date in the ISO 8601 format. e (Error) Cell containing an error. inlineStr (Inline String) Cell containing an (inline) rich string, i.e. one not in the shared string table. If this cell type is used, then the cell value is in the is element rather than the v element in the cell (c element). n (Number) Cell containing a number. s (Shared String) Cell containing a shared string. str (String) Cell containing a formula string. Converting into values described in ODF1.1: "6.7.1. Variable Value Types and Values". */ if (t == QLatin1String("s")) { bool ok; const int stringIndex = m_value.toInt(&ok); if (!ok || stringIndex < 0 || stringIndex >= m_context->sharedStrings->size()) { return KoFilter::WrongFormat; } QString sharedstring = m_context->sharedStrings->at(stringIndex); cell->text = sharedstring; cell->valueType = Cell::ConstString; m_value = sharedstring; // no valueAttr } else if ((t.isEmpty() && !valueIsNumeric(m_value)) || t == QLatin1String("inlineStr")) { //! @todo handle value properly cell->text = m_value; cell->valueType = Cell::ConstString; // no valueAttr } else if (t == QLatin1String("b")) { cell->text = m_value; cell->valueType = Cell::ConstBoolean; cell->valueAttr = Cell::OfficeBooleanValue; } else if (t == QLatin1String("d")) { //! @todo handle value properly cell->text = m_value; cell->valueType = Cell::ConstDate; cell->valueAttr = Cell::OfficeDateValue; } else if (t == QLatin1String("str")) { //! @todo handle value properly cell->text = m_value; cell->valueType = Cell::ConstString; // no valueAttr } else if (t == QLatin1String("n") || t.isEmpty() /* already checked if numeric */) { if (!t.isEmpty()) { // sanity check if (!valueIsNumeric(m_value)) { raiseError(i18n("Expected integer or floating point number")); return KoFilter::WrongFormat; } } const KoGenStyle* const style = mainStyles->style( formattedStyle ); if( style == 0 || valueIsNumeric(m_value) ) { // body->addTextSpan(m_value); cell->valueType = Cell::ConstFloat; cell->valueAttr = Cell::OfficeValue; } else { switch( style->type() ) { case KoGenStyle::NumericDateStyle: cell->valueType = Cell::ConstDate; cell->valueAttr = Cell::OfficeDateValue; m_value = QDate( 1899, 12, 30 ).addDays( m_value.toInt() ).toString( Qt::ISODate ); break; case KoGenStyle::NumericTextStyle: cell->valueType = Cell::ConstString; cell->valueAttr = Cell::OfficeStringValue; break; default: cell->valueType = Cell::ConstFloat; cell->valueAttr = Cell::OfficeValue; break; } } } else if (t == QLatin1String("e")) { if (m_value == QLatin1String("#REF!")) cell->text = "#NAME?"; else cell->text = m_value; //! @todo full parsing needed to retrieve the type cell->valueType = Cell::ConstFloat; cell->valueAttr = Cell::OfficeValue; m_value = QLatin1String("0"); } else { raiseUnexpectedAttributeValueError(t, "c@t"); return KoFilter::WrongFormat; } } // cell style if (!s.isEmpty()) { if (!ok || !cellFormat) { raiseUnexpectedAttributeValueError(s, "c@s"); return KoFilter::WrongFormat; } KoGenStyle cellStyle(KoGenStyle::TableCellAutoStyle, "table-cell"); if (charStyleName.isEmpty()) { KoGenStyle* fontStyle = m_context->styles->fontStyle(cellFormat->fontId); if (!fontStyle) { kWarning() << "No font with ID:" << cellFormat->fontId; } else { KoGenStyle::copyPropertiesFromStyle(*fontStyle, cellStyle, KoGenStyle::TextType); } } if (!cellFormat->setupCellStyle(m_context->styles, &cellStyle)) { return KoFilter::WrongFormat; } if (!formattedStyle.isEmpty()) { cellStyle.addAttribute( "style:data-style-name", formattedStyle ); } if (!m_context->conditionalStyles.isEmpty()) { QString positionLetter; int positionNumber; splitToRowAndColumn(r, positionLetter, positionNumber); QList > maps = m_context->conditionalStyleForPosition(positionLetter, positionNumber); int index = maps.size(); // Adding the lists in reversed priority order, as KoGenStyle when creating the style // adds last added first while (index > 0) { cellStyle.addStyleMap(maps.at(index - 1)); --index; } } const QString cellStyleName = mainStyles->insert( cellStyle, "ce" ); cell->styleName = cellStyleName; } delete cell->valueAttrValue; if (m_value.isEmpty()) { cell->valueAttrValue = 0; } else { cell->valueAttrValue = new QString(m_value); } ++m_currentColumn; // This cell is done now. Select the next cell. READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL f //! f handler (Formula) /*! ECMA-376, 18.3.1.40, p. 1813. Formula for the cell. The formula expression is contained in the character node of this element. No child elements. Parent elements: - [done] c (§18.3.1.4) - nc (§18.11.1.3) - oc (§18.11.1.5) @todo support all elements */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_f() { Cell* cell = m_context->sheet->cell(m_currentColumn, m_currentRow, false); Q_ASSERT(cell); READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); // Range of cells which the formula applies to. Only required for shared formula, array // formula or data table. Only written on the master formula, not subsequent formula's // belonging to the same shared group, array, or data table. //TRY_READ_ATTR(ref) // Type of formula. The possible values defined by the ST_CellFormulaType (§18.18.6), p. 2677 TRY_READ_ATTR(t) // Shared formula groups. int sharedGroupIndex = -1; if (t == QLatin1String("shared")) { TRY_READ_ATTR(si) STRING_TO_INT(si, sharedGroupIndex, "f@si") } while (!atEnd() && !hasError()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isCharacters()) { delete cell->formula; cell->formula = new FormulaImpl(Calligra::Sheets::MSOOXML::convertFormula(text().toString())); } } if (!t.isEmpty()) { if (t == QLatin1String("shared")) { if (sharedGroupIndex >= 0) { /* Shared Group Index, p. 1815 Optional attribute to optimize load performance by sharing formulas. When a formula is a shared formula (t value is shared) then this value indicates the group to which this particular cell's formula belongs. The first formula in a group of shared formulas is saved in the f element. This is considered the 'master' formula cell. Subsequent cells sharing this formula need not have the formula written in their f element. Instead, the attribute si value for a particular cell is used to figure what the formula expression should be based on the cell's relative location to the master formula cell. */ if (d->sharedFormulas.contains(sharedGroupIndex)) { if (!cell->formula /* || cell->formula->isEmpty() */) { // don't do anything if the cell already defines a formula QHash::iterator it = d->sharedFormulas.find(sharedGroupIndex); if (it != d->sharedFormulas.end()) { delete cell->formula; cell->formula = new SharedFormula(it.value()); } } } else if (cell->formula /* && !cell->formula->isEmpty()*/) { // is this cell the master cell? d->sharedFormulas[sharedGroupIndex] = cell; } } } } /* if (!ref.isEmpty()) { const int pos = ref.indexOf(':'); if (pos > 0) { const QString fromCell = ref.left(pos); const QString toCell = ref.mid(pos + 1); const int c1 = Calligra::Sheets::Util::decodeColumnLabelText(fromCell) - 1; const int r1 = Calligra::Sheets::Util::decodeRowLabelText(fromCell) - 1; const int c2 = Calligra::Sheets::Util::decodeColumnLabelText(toCell) - 1; const int r2 = Calligra::Sheets::Util::decodeRowLabelText(toCell) - 1; if (c1 >= 0 && r1 >= 0 && c2 >= c1 && r2 >= r1) { for (int col = c1; col <= c2; ++col) { for (int row = r1; row <= r2; ++row) { if (col != m_currentColumn || row != m_currentRow) { if (Cell* c = m_context->sheet->cell(col, row, true)) c->formula = convertFormulaReference(cell, c); } } } } } } */ READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL v //! v handler (Cell Value) /*! ECMA-376, 18.3.1.96, p. 1891. This element expresses the value contained in a cell. No child elements. Parent elements: - [done] c (§18.3.1.4) - cell (§18.14.1) - nc (§18.11.1.3) - oc (§18.11.1.5) - tp (§18.15.3) @todo support all parent elements */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_v() { READ_PROLOGUE readNext(); // It is possible to have empty element if (name() == "v" && isEndElement()) { READ_EPILOGUE } m_value = text().toString(); m_value.replace('&', "&"); m_value.replace('<', "<"); m_value.replace('>', ">"); m_value.replace('\\', "'"); m_value.replace('"', """); readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL mergeCell /* Parent elements: - [done] mergeCells (§18.3.1.55) Child elements: - none */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_mergeCell() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(ref) QStringList refList = ref.split(':'); if (refList.count() >= 2) { const QString fromCell = refList[0]; const QString toCell = refList[1]; QRegExp rx("([A-Za-z]+)([0-9]+)"); if(rx.exactMatch(fromCell)) { const int fromRow = rx.cap(2).toInt() - 1; const int fromCol = Calligra::Sheets::Util::decodeColumnLabelText(fromCell) - 1; if(rx.exactMatch(toCell)) { Cell* cell = m_context->sheet->cell(fromCol, fromRow, true); cell->rowsMerged = rx.cap(2).toInt() - fromRow; cell->columnsMerged = Calligra::Sheets::Util::decodeColumnLabelText(toCell) - fromCol; // correctly take right/bottom borders from the cells that are merged into this one const KoGenStyle* origCellStyle = mainStyles->style(cell->styleName); KoGenStyle cellStyle; if (origCellStyle) { cellStyle = *origCellStyle; } kDebug() << cell->rowsMerged << cell->columnsMerged << cell->styleName; if (cell->rowsMerged > 1) { Cell* lastCell = m_context->sheet->cell(fromCol, fromRow + cell->rowsMerged - 1, false); kDebug() << lastCell; if (lastCell) { const KoGenStyle* style = mainStyles->style(lastCell->styleName); kDebug() << lastCell->styleName; if (style) { QString val = style->property("fo:border-bottom"); kDebug() << val; if (!val.isEmpty()) cellStyle.addProperty("fo:border-bottom", val); val = style->property("fo:border-line-width-bottom"); if (!val.isEmpty()) cellStyle.addProperty("fo:border-line-width-bottom", val); } } } if (cell->columnsMerged > 1) { Cell* lastCell = m_context->sheet->cell(fromCol + cell->columnsMerged - 1, fromRow, false); if (lastCell) { const KoGenStyle* style = mainStyles->style(lastCell->styleName); if (style) { QString val = style->property("fo:border-right"); if (!val.isEmpty()) cellStyle.addProperty("fo:border-right", val); val = style->property("fo:border-line-width-right"); if (!val.isEmpty()) cellStyle.addProperty("fo:border-line-width-right", val); } } } cell->styleName = mainStyles->insert(cellStyle, "ce"); } } } readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL mergeCells /* Parent elements: - [done] worksheet (§18.3.1.99) Child elements: - mergeCell (Merged Cell) §18.3.1.54 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_mergeCells() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(mergeCell) ELSE_WRONG_FORMAT } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL drawing //! drawing handler (Drawing) /*! ECMA-376, 18.3.1.36, p.1804. This element indicates that the sheet contains drawing components built on the drawingML platform. The relationship Id references the part containing the drawingML definitions. Parent elements: - chartsheet (§18.3.1.12) - dialogsheet (§18.3.1.34) - [done] worksheet (§18.3.1.99) Child elements - see DrawingML. */ KoFilter::ConversionStatus MSOOXML_CURRENT_CLASS::read_drawing() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITH_NS(r, id) if(!r_id.isEmpty() && !this->m_context->path.isEmpty()) { QString drawingPathAndFile = m_context->relationships->target(m_context->path, m_context->file, r_id); QString drawingPath, drawingFile; MSOOXML::Utils::splitPathAndFile(drawingPathAndFile, &drawingPath, &drawingFile); XlsxXmlDrawingReaderContext context(m_context, m_context->sheet, drawingPath, drawingFile); XlsxXmlDrawingReader reader(this); const KoFilter::ConversionStatus result = m_context->import->loadAndParseDocument(&reader, drawingPathAndFile, &context); if (result != KoFilter::OK) { raiseError(reader.errorString()); return result; } #if 0 //TODO if (context->m_positions.contains(XlsxDrawingObject::FromAnchor)) { XlsxDrawingObject::Position pos = context->m_positions[XlsxDrawingObject::FromAnchor]; Cell* cell = m_context->sheet->cell(pos.m_col, pos.m_row, true); cell->drawings << context; } else { delete context; } #endif } while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL hyperlink /* Parent elements: - [done] hyperlinks (§18.3.1.48) Child elements: - none */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_hyperlink() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(ref) TRY_READ_ATTR_WITHOUT_NS(location) TRY_READ_ATTR_WITH_NS(r, id) if (!ref.isEmpty() && (!r_id.isEmpty() || !location.isEmpty())) { const int col = Calligra::Sheets::Util::decodeColumnLabelText(ref) - 1; const int row = Calligra::Sheets::Util::decodeRowLabelText(ref) - 1; if(col >= 0 && row >= 0) { QString link = m_context->relationships->target(m_context->path, m_context->file, r_id); // it follows a hack to get right of the prepended m_context->path... if (link.startsWith(m_context->path)) link = link.mid(m_context->path.length()+1); // append location if (!location.isEmpty()) link += '#' + location; Cell* cell = m_context->sheet->cell(col, row, true); cell->setHyperLink( link ); } } readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL hyperlinks /* Parent elements: - [done] worksheet (§18.3.1.99) Child elements: - [done] hyperlink (Hyperlink) §18.3.1.47 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_hyperlinks() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(hyperlink) ELSE_WRONG_FORMAT } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL customFilters /* Parent elements: - [done] filterColumn (§18.3.2.7) Child elements: - [done] customFilter (Custom Filter Criteria) §18.3.2.2 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_customFilters() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString andValue = attrs.value("and").toString(); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(customFilter) ELSE_WRONG_FORMAT } } if (!m_context->autoFilters.isEmpty()) { if (andValue == "1") { m_context->autoFilters.last().type = "and"; } else { m_context->autoFilters.last().type = "or"; } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL filters /* Parent elements: - [done] filterColumn (§18.3.2.7) Child elements: - dateGroupItem (Date Grouping) §18.3.2.4 - [done] filter (Filter) §18.3.2.6 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_filters() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(blank) m_context->currentFilterCondition.value = "^("; bool hasValueAlready = false; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { if (name() == "filter") { if (hasValueAlready) { m_context->currentFilterCondition.value += "|"; } hasValueAlready = true; TRY_READ(filter) } SKIP_UNKNOWN } } m_context->currentFilterCondition.value += ")$"; m_context->currentFilterCondition.opField = "match"; if (blank == "1") { m_context->currentFilterCondition.value = "0"; m_context->currentFilterCondition.opField = "empty"; } if (!m_context->autoFilters.isEmpty()) { m_context->autoFilters.last().filterConditions.push_back(m_context->currentFilterCondition); } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL customFilter /* Parent elements: - [done] customFilters (§18.3.2.2) Child elements: - none */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_customFilter() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); QString opValue = attrs.value("operator").toString(); TRY_READ_ATTR_WITHOUT_NS(val) m_context->currentFilterCondition.value = val; if (opValue == "notEqual") { m_context->currentFilterCondition.opField = "!="; } else { m_context->currentFilterCondition.opField = "="; } if (!m_context->autoFilters.isEmpty()) { m_context->autoFilters.last().filterConditions.push_back(m_context->currentFilterCondition); } readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL filter /* Parent elements: - [done] filters (§18.3.2.8) Child elements: - none */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_filter() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(val) m_context->currentFilterCondition.value += val; readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL filterColumn /* Parent elements: - [done] autoFilter (§18.3.1.2) Child elements: - colorFilter (Color Filter Criteria) §18.3.2.1 - [done] customFilters (Custom Filters) §18.3.2.3 - dynamicFilter (Dynamic Filter) §18.3.2.5 - extLst (Future Feature Data Storage Area) §18.2.10 - [done] filters (Filter Criteria) §18.3.2.8 - iconFilter (Icon Filter) §18.3.2.9 - top10 (Top 10) §18.3.2.10 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_filterColumn() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(colId) m_context->currentFilterCondition.field = colId; while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(filters) ELSE_TRY_READ_IF(customFilters) SKIP_UNKNOWN } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL autoFilter /* Parent elements: - customSheetView (§18.3.1.25) - filter (§18.10.1.33) - table (§18.5.1.2) - [done] worksheet (§18.3.1.99) Child elements: - extLst (Future Feature Data Storage Area) §18.2.10 - [done] filterColumn (AutoFilter Column) §18.3.2.7 - sortState (Sort State) §18.3.1.92 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_autoFilter() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(ref) // take last numbers and replace it with max row ref.replace(QRegExp("[0-9]+$"), QString::number(m_context->sheet->maxRow()+1)); ref.prepend("."); QString sheetName = m_context->worksheetName; if (sheetName.contains('.') || sheetName.contains(' ') || sheetName.contains('\'')) { sheetName = '\'' + sheetName.replace('\'', "''") + '\''; } ref.prepend(sheetName); int colon = ref.indexOf(':'); if (colon > 0) { ref.insert(colon + 1, '.'); ref.insert(colon + 1, sheetName); } XlsxXmlDocumentReaderContext::AutoFilter autoFilter; autoFilter.area = ref; m_context->autoFilters.push_back(autoFilter); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if (isStartElement()) { TRY_READ_IF(filterColumn) SKIP_UNKNOWN } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL picture /* Parent elements: - chartsheet (§18.3.1.12) - [done] worksheet (§18.3.1.99) Child elements: - none */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_picture() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITH_NS(r, id) const QString link = m_context->relationships->target(m_context->path, m_context->file, r_id); QString destinationName = QLatin1String("Pictures/") + link.mid(link.lastIndexOf('/') + 1); RETURN_IF_ERROR( m_context->import->copyFile(link, destinationName, false ) ) addManifestEntryForFile(destinationName); m_context->sheet->setPictureBackgroundPath(destinationName); readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL tableParts /* Parent elements: - [done] worksheet (§18.3.1.99) Child elements: - [done] tablePart (Table Part) §18.3.1.94 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_tableParts() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if( isStartElement() ) { TRY_READ_IF(tablePart) ELSE_WRONG_FORMAT } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL tablePart /* Parent elements: - [done] tableParts (§18.3.1.95) Child elements: - none */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_tablePart() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); READ_ATTR_WITH_NS(r, id) QString tablePathAndFile = m_context->relationships->target(m_context->path, m_context->file, r_id); XlsxXmlTableReaderContext context; XlsxXmlTableReader reader(this); const KoFilter::ConversionStatus result = m_context->import->loadAndParseDocument(&reader, tablePathAndFile, &context); if (result != KoFilter::OK) { raiseError(reader.errorString()); return result; } readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL legacyDrawing // todo KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_legacyDrawing() { READ_PROLOGUE readNext(); READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL controls /* Parent elements: - [done] worksheet (§18.3.1.99) Child elements: - [done] control (Embedded Control) §18.3.1.19 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_controls() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if( isStartElement() ) { TRY_READ_IF(control) ELSE_WRONG_FORMAT } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL oleObjects /* Parent elements: - [done] dialogsheet (§18.3.1.34) - [done] worksheet (§18.3.1.99) Child elements: - [done] oleObject (Embedded Object) §18.3.1.59 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_oleObjects() { READ_PROLOGUE while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) if( isStartElement() ) { TRY_READ_IF(oleObject) // It seems that MSO 2010 has a concept of Alternate // Content, which it throws in at unexpected times. // This is one such time. So let's try to find the // oleObject inside an mc:AlternateContent tag if possible. ELSE_TRY_READ_IF_NS(mc, AlternateContent) // Should be more specialized what we are looking for ELSE_WRONG_FORMAT } } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL control /* Parent elements: - [done] controls (§18.3.1.21) Child elements: - controlPr (Embedded Control Properties) §18.3.1.20 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_control() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); TRY_READ_ATTR_WITHOUT_NS(shapeId) // TODO: Maybe we want to do something with the actual control element. // In vmldrawing, the shape identifier has also the extra chars below, therefore // we have to add them here for the match shapeId = "_x0000_s" + shapeId; body->addCompleteElement(m_context->oleFrameBegins.value(shapeId).toUtf8()); body->startElement("draw:image"); body->addAttribute("xlink:href", m_context->oleReplacements.value(shapeId)); body->addAttribute("xlink:type", "simple"); body->addAttribute("xlink:show", "embed"); body->addAttribute("xlink:actuate", "onLoad"); body->endElement(); // draw:image body->addCompleteElement(""); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) } READ_EPILOGUE } #undef CURRENT_EL #define CURRENT_EL oleObject /* Parent elements: - [done] oleObjects (§18.3.1.60) Child elements: - objectPr (Embedded Object Properties) §18.3.1.56 */ KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_oleObject() { READ_PROLOGUE const QXmlStreamAttributes attrs(attributes()); READ_ATTR_WITH_NS(r, id) READ_ATTR_WITHOUT_NS(progId) TRY_READ_ATTR_WITHOUT_NS(shapeId) // In vmldrawing, the shape identifier has also the extra chars below, therefore // we have to add them here for the match shapeId = "_x0000_s" + shapeId; const QString link = m_context->relationships->target(m_context->path, m_context->file, r_id); QString destinationName = QLatin1String("") + link.mid(link.lastIndexOf('/') + 1); KoFilter::ConversionStatus status = m_context->import->copyFile(link, destinationName, false); if (status == KoFilter::OK) { addManifestEntryForFile(destinationName); } //TODO find out which cell to pick Cell* cell = m_context->sheet->cell(0, 0, true); cell->appendOleObject( qMakePair(destinationName, m_context->oleReplacements.value(shapeId)), m_context->oleFrameBegins.value(shapeId)); while (!atEnd()) { readNext(); BREAK_IF_END_OF(CURRENT_EL) } READ_EPILOGUE } diff --git a/filters/stage/powerpoint/PptToOdp.cpp b/filters/stage/powerpoint/PptToOdp.cpp index 694785397ee..4b023463404 100644 --- a/filters/stage/powerpoint/PptToOdp.cpp +++ b/filters/stage/powerpoint/PptToOdp.cpp @@ -1,3668 +1,3668 @@ /* This file is part of the KDE project Copyright (C) 2005 Yolla Indria Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). Contact: Amit Aggarwal Copyright (C) 2010 KO GmbH Copyright (C) 2010, 2011 Matus Uzak 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 "PowerPointImport.h" #include "PptToOdp.h" #include "globalobjectcollectors.h" #include "pictures.h" #include "ODrawToOdf.h" #include "msodraw.h" #include "msppt.h" #include "msoleps.h" #include #include #include #include #include #include #include //#define DEBUG_PPTTOODP //#define USE_OFFICEARTDGG_CONTAINER //#define DISABLE_PLACEHOLDER_BORDER #define FONTSIZE_MAX 4000 //according to MS-PPT using namespace MSO; namespace { QString format(double v) { static const QString f("%1"); static const QString e(""); static const QRegExp r("\\.?0+$"); return f.arg(v, 0, 'f').replace(r, e); } QString mm(double v) { static const QString mm("mm"); return format(v) + mm; } QString cm(double v) { static const QString cm("cm"); return format(v) + cm; } QString pt(double v) { static const QString pt("pt"); return format(v) + pt; } QString percent(double v) { return format(v) + '%'; } // The placementId is mapped to one of: "chart", "date-time", "footer", // "graphic", "handout", "header", "notes", "object", "orgchart", "outline", // "page", "page-number", "subtitle", "table", "text" or "title" // // NOTE: we use 'outline' for PT_MasterBody, PT_Body and PT_VerticalBody types // to be compatible with OpenOffice. OpenOffice <= 3.2 does not render lists // properly if the presentation class is not 'outline', 'subtitle', or 'notes'. const char* getPresentationClass(const PlaceholderAtom* p) { if (p == 0) return 0; switch (p->placementId) { case 0x01: return "title"; // PT_MasterTitle case 0x02: return "outline"; // PT_MasterBody case 0x03: return "title"; // PT_MasterCenterTitle case 0x04: return "subtitle"; // PT_MasterSubTitle case 0x05: return "graphic"; // PT_MasterNotesSlideImage case 0x06: return "notes"; // PT_MasterNotesBody case 0x07: return "date-time"; // PT_MasterDate case 0x08: return "page-number"; // PT_MasterSlideNumber case 0x09: return "footer"; // PT_MasterFooter case 0x0A: return "header"; // PT_MasterHeader case 0x0B: return "page"; // PT_NotesSlideImage case 0x0C: return "notes"; // PT_NotesBody case 0x0D: return "title"; // PT_Title case 0x0E: return "outline"; // PT_Body case 0x0F: return "title"; // PT_CenterTitle case 0x10: return "subtitle"; // PT_SubTitle case 0x11: return "title"; // PT_VerticalTitle case 0x12: return "outline"; // PT_VerticalBody case 0x13: return "object"; // PT_Object case 0x14: return "chart"; // PT_Graph case 0x15: return "table"; // PT_Table case 0x16: return "object"; // PT_ClipArt case 0x17: return "orgchart"; // PT_OrgChart case 0x18: return "object"; // PT_Media case 0x19: return "object"; // PT_VerticalObject case 0x1A: return "graphic"; // PT_Picture default: return 0; } } QString getPresentationClass(const MSO::TextContainer* tc) { if (!tc) return QString(); for (int i = 0; imeta.size(); ++i) { const TextContainerMeta& m = tc->meta[i]; if (m.meta.get()) return "page-number"; if (m.meta.get()) return "date-time"; if (m.meta.get()) return "date-time"; if (m.meta.get()) return "header"; if (m.meta.get()) return "footer"; } return QString(); } QString getMasterStyle(const QMap& map, int texttype) { if (map.contains(texttype)) { return map[texttype]; } // fallback for titles if (texttype == 0 || texttype == 6) { if (map.contains(0)) return map[0]; // Tx_TYPE_TITLE if (map.contains(6)) return map[6]; // Tx_TYPE_CENTERTITLE return QString(); } else { // fallback for body if (map.contains(1)) return map[1]; // Tx_TYPE_BODY if (map.contains(5)) return map[5]; // Tx_TYPE_CENTERBODY if (map.contains(7)) return map[7]; // Tx_TYPE_HALFBODY if (map.contains(8)) return map[8]; // Tx_TYPE_QUARTERBODY if (map.contains(4)) return map[4]; // Tx_TYPE_OTHER return QString(); } return QString(); } const MSO::OfficeArtSpContainer* getMasterShape(const MSO::MasterOrSlideContainer* m) { if (!m) { return 0; } const SlideContainer* sc = m->anon.get(); const MainMasterContainer* mm = m->anon.get(); const OfficeArtSpContainer* scp = 0; if (sc) { if (sc->drawing.OfficeArtDg.shape) { scp = sc->drawing.OfficeArtDg.shape.data(); } } else if (mm) { if (mm->drawing.OfficeArtDg.shape) { scp = mm->drawing.OfficeArtDg.shape.data(); } } return scp; } /** * Return the bounding rectangle for this object. **/ QRect getRect(const PptOfficeArtClientAnchor &a) { if (a.rect1) { const SmallRectStruct &r = *a.rect1; return QRect(r.left, r.top, r.right - r.left, r.bottom - r.top); } else { const RectStruct &r = *a.rect2; return QRect(r.left, r.top, r.right - r.left, r.bottom - r.top); } } QString getText(const TextContainer* tc) { if (!tc) return QString(); QString ret; if (tc->text.is()) { const QVector textChars(tc->text.get()->textChars); ret = QString::fromUtf16(textChars.data(), textChars.size()); } else if (tc->text.is()) { // each item represents the low byte of a UTF-16 Unicode character // whose high byte is 0x00 const QByteArray& textChars(tc->text.get()->textChars); - ret = QString::fromAscii(textChars, textChars.size()); + ret = QString::fromLatin1(textChars, textChars.size()); } return ret; } } //namespace (anonymous) /* * ************************************************ * DrawClient * ************************************************ */ class PptToOdp::DrawClient : public ODrawToOdf::Client { private: QRectF getRect(const MSO::OfficeArtClientAnchor&); QRectF getReserveRect(void); QString getPicturePath(const quint32 pib); bool onlyClientData(const MSO::OfficeArtClientData& o); void processClientData(const MSO::OfficeArtClientTextBox* ct, const MSO::OfficeArtClientData& cd, Writer& out); void processClientTextBox(const MSO::OfficeArtClientTextBox& ct, const MSO::OfficeArtClientData* cd, Writer& out); bool processRectangleAsTextBox(const MSO::OfficeArtClientData& cd); KoGenStyle createGraphicStyle( const MSO::OfficeArtClientTextBox* ct, const MSO::OfficeArtClientData* cd, const DrawStyle& ds, Writer& out); void addTextStyles(const MSO::OfficeArtClientTextBox* clientTextbox, const MSO::OfficeArtClientData* clientData, KoGenStyle& style, Writer& out); const MSO::OfficeArtDggContainer* getOfficeArtDggContainer(); const MSO::OfficeArtSpContainer* getMasterShapeContainer(quint32 spid); QColor toQColor(const MSO::OfficeArtCOLORREF& c); QString formatPos(qreal v); /** * Check if a placeholder is valid and allowed by the slide layout. * @param PlaceholderAtom * @return 1 - allowed, 0 - forbidden */ bool placeholderAllowed(const MSO::PlaceholderAtom* pa) const; bool isPlaceholder(const MSO::OfficeArtClientData* cd) const; /** * PPT client specific data. */ struct DrawClientData { const MSO::MasterOrSlideContainer* masterSlide; const MSO::SlideContainer* presSlide; const MSO::NotesContainer* notesMasterSlide; const MSO::NotesContainer* notesSlide; const MSO::SlideListWithTextSubContainerOrAtom* slideTexts; DrawClientData(): masterSlide(0), presSlide(0), notesMasterSlide(0), notesSlide(0), slideTexts(0) {}; }; DrawClientData dc_data[1]; PptToOdp* const ppttoodp; public: DrawClient(PptToOdp* p) :ppttoodp(p) {} void setDrawClientData(const MSO::MasterOrSlideContainer* mc, const MSO::SlideContainer* sc, const MSO::NotesContainer* nmc, const MSO::NotesContainer* nc, const MSO::SlideListWithTextSubContainerOrAtom* stc = 0) { dc_data->masterSlide = mc; dc_data->presSlide = sc; dc_data->notesMasterSlide = nmc; dc_data->notesSlide = nc; dc_data->slideTexts = stc; } }; bool PptToOdp::DrawClient::isPlaceholder(const MSO::OfficeArtClientData* cd) const { if (!cd) { return false; } const PptOfficeArtClientData* pcd = cd->anon.get(); if (pcd && pcd->placeholderAtom && placeholderAllowed(pcd->placeholderAtom.data())) { return true; } return false; } QRectF PptToOdp::DrawClient::getRect(const MSO::OfficeArtClientAnchor& o) { const PptOfficeArtClientAnchor* a = o.anon.get(); if (a) { return ::getRect(*a); } return QRectF(); } QRectF PptToOdp::DrawClient::getReserveRect(void) { //NOTE: No PPT test files at the moment. return QRect(0, 0, 1, 1); } QString PptToOdp::DrawClient::getPicturePath(const quint32 pib) { return ppttoodp->getPicturePath(pib); } bool PptToOdp::DrawClient::onlyClientData(const MSO::OfficeArtClientData& o) { const PptOfficeArtClientData* pcd = o.anon.get(); if (pcd && pcd->placeholderAtom && dc_data->slideTexts) { const PlaceholderAtom* pa = pcd->placeholderAtom.data(); if (pa->position >= 0 && pa->position < dc_data->slideTexts->atoms.size()) { return true; } } return false; } void PptToOdp::DrawClient::processClientData(const MSO::OfficeArtClientTextBox* ct, const MSO::OfficeArtClientData& o, Writer& out) { const TextContainer* textContainer = 0; const TextRuler* textRuler = 0; if (ct) { if (ct->anon.is()) { const PptOfficeArtClientTextBox* tb = ct->anon.get(); foreach(const TextClientDataSubContainerOrAtom& tc, tb->rgChildRec) { if (tc.anon.is()) { const OutlineAtom* outlineAtom = tc.anon.get(); if (outlineAtom->textRulerAtom) { textRuler = &outlineAtom->textRulerAtom->textRuler; break; } } } } } const PptOfficeArtClientData* pcd = o.anon.get(); if (pcd && pcd->placeholderAtom && dc_data->slideTexts) { const PlaceholderAtom* pa = pcd->placeholderAtom.data(); if (pa->position >= 0 && pa->position < dc_data->slideTexts->atoms.size()) { textContainer = &dc_data->slideTexts->atoms[pa->position]; ppttoodp->processTextForBody(out, &o, textContainer, textRuler, isPlaceholder(&o)); } } } void PptToOdp::DrawClient::processClientTextBox(const MSO::OfficeArtClientTextBox& ct, const MSO::OfficeArtClientData* cd, Writer& out) { // NOTE: Workaround! Only in case of a textshape the placeholder flag does // hide the placeholder text => Ignoring the placeholder text in case of // other shapes on master slides. if (ppttoodp->m_processingMasters) { if (isPlaceholder(cd)) { if (!((m_currentShapeType == msosptTextBox) || (m_currentShapeType == msosptRectangle))) { return; } } } const PptOfficeArtClientTextBox* tb = ct.anon.get(); if (tb) { const MSO::TextContainer* textContainer = 0; const MSO::TextRuler* textRuler = 0; foreach(const TextClientDataSubContainerOrAtom& tc, tb->rgChildRec) { if (tc.anon.is()) { textContainer = tc.anon.get(); if (textContainer->textRulerAtom) { textRuler = &textContainer->textRulerAtom->textRuler; } } } ppttoodp->processTextForBody(out, cd, textContainer, textRuler, isPlaceholder(cd)); } } bool PptToOdp::DrawClient::processRectangleAsTextBox(const MSO::OfficeArtClientData& cd) { const PptOfficeArtClientData* pcd = cd.anon.get(); if (pcd && pcd->placeholderAtom) { return true; } else { return false; } } KoGenStyle PptToOdp::DrawClient::createGraphicStyle( const MSO::OfficeArtClientTextBox* clientTextbox, const MSO::OfficeArtClientData* clientData, const DrawStyle& ds, Writer& out) { Q_UNUSED(ds); KoGenStyle style; const PptOfficeArtClientData* cd = 0; if (clientData) { cd = clientData->anon.get(); } const PptOfficeArtClientTextBox* tb = 0; if (clientTextbox) { tb = clientTextbox->anon.get(); } quint32 textType = ppttoodp->getTextType(tb, cd); if (isPlaceholder(clientData)) { // type is presentation bool canBeParentStyle = false; if ( (textType != 99) && out.stylesxml && dc_data->masterSlide) { canBeParentStyle = true; } bool isAutomatic = !canBeParentStyle; // If this object has a placeholder type, it defines a presentation // style, otherwise, it defines a graphic style. A graphic style is // always automatic. KoGenStyle::Type type = KoGenStyle::PresentationStyle; if (isAutomatic) { type = KoGenStyle::PresentationAutoStyle; } style = KoGenStyle(type, "presentation"); if (isAutomatic) { style.setAutoStyleInStylesDotXml(out.stylesxml); } QString parent; // for now we only set parent styles on presentation styled elements if (dc_data->masterSlide) { parent = getMasterStyle(ppttoodp->masterPresentationStyles[dc_data->masterSlide], textType); } if (!parent.isEmpty()) { style.setParentName(parent); } } else { // type is graphic style = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic"); style.setAutoStyleInStylesDotXml(out.stylesxml); } if (out.stylesxml) { const MasterOrSlideContainer* m = dc_data->masterSlide; const TextMasterStyleAtom* msa = getTextMasterStyleAtom(m, textType); if (msa) { KoGenStyle list(KoGenStyle::ListStyle); ppttoodp->defineListStyle(list, textType, *msa); QString listStyleName; listStyleName = out.styles.insert(list); } } return style; } void PptToOdp::DrawClient::addTextStyles( const MSO::OfficeArtClientTextBox* clientTextbox, const MSO::OfficeArtClientData* clientData, KoGenStyle& style, Writer& out) { // content.xml - As soon the content or graphic-style of a placeholder // changed, make it a normal shape to be ODF compliant. // // TODO: check if the graphic-style changed compared to the parent const PptOfficeArtClientData* cd = 0; if (clientData) { cd = clientData->anon.get(); } const PptOfficeArtClientTextBox* tb = 0; if (clientTextbox) { tb = clientTextbox->anon.get(); } bool potentialPlaceholder = false; if (m_currentShapeType == msosptRectangle) { potentialPlaceholder = true; } if (out.stylesxml) { //get the main master slide's MasterOrSlideContainer const MasterOrSlideContainer* m = 0; if (dc_data->masterSlide && isPlaceholder(clientData)) { m = dc_data->masterSlide; while (m->anon.is()) { m = ppttoodp->p->getMaster(m->anon.get()); } } const TextContainer* tc = ppttoodp->getTextContainer(tb, cd); PptTextPFRun pf(ppttoodp->p->documentContainer, m, dc_data->slideTexts, cd, tc); ppttoodp->defineParagraphProperties(style, pf, 0); PptTextCFRun cf(ppttoodp->p->documentContainer, m, tc, 0); ppttoodp->defineTextProperties(style, cf, 0, 0, 0); } #ifdef DISABLE_PLACEHOLDER_BORDER if (isPlaceholder(clientData)) { style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType); //style.addProperty("draw:stroke-width", "none", KoGenStyle::GraphicType); } #endif bool isCustomShape = false; switch (m_currentShapeType) { case msosptPictureFrame: case msosptTextBox: case msosptLine: break; case msosptRectangle: if (!clientData || !processRectangleAsTextBox(*clientData)) { isCustomShape = true; } break; default: isCustomShape = true; break; } // NOTE: Workaround: Set padding to ZERO until the fo:wrap-option support // arrives and other text on shape related issues get fixed. if (isCustomShape) { style.removeProperty("fo:padding-left"); style.removeProperty("fo:padding-right"); style.removeProperty("fo:padding-top"); style.removeProperty("fo:padding-bottom"); style.addPropertyPt("fo:padding", 0); } const QString styleName = out.styles.insert(style); if (isPlaceholder(clientData)) { out.xml.addAttribute("presentation:style-name", styleName); QString className = getPresentationClass(cd->placeholderAtom.data()); const TextContainer* tc = ppttoodp->getTextContainer(tb, cd); if ( className.isEmpty() || (!out.stylesxml && (!potentialPlaceholder || getText(tc).size())) ) { className = getPresentationClass(tc); out.xml.addAttribute("presentation:placeholder", "false"); } else { out.xml.addAttribute("presentation:placeholder", "true"); } if (!className.isEmpty()) { out.xml.addAttribute("presentation:class", className); } } else { out.xml.addAttribute("draw:style-name", styleName); } quint32 textType = ppttoodp->getTextType(tb, cd); bool canBeParentStyle = false; if (isPlaceholder(clientData) && (textType != 99) && out.stylesxml && dc_data->masterSlide) { canBeParentStyle = true; } if (canBeParentStyle) { ppttoodp->masterPresentationStyles[dc_data->masterSlide][textType] = styleName; } } //end addTextStyle() const MSO::OfficeArtDggContainer* PptToOdp::DrawClient::getOfficeArtDggContainer() { #ifdef USE_OFFICEARTDGG_CONTAINER return &ppttoodp->p->documentContainer->drawingGroup.OfficeArtDgg; #else return 0; #endif } const MSO::OfficeArtSpContainer* PptToOdp::DrawClient::getMasterShapeContainer(quint32 spid) { const OfficeArtSpContainer* sp = 0; sp = ppttoodp->retrieveMasterShape(spid); return sp; } QColor PptToOdp::DrawClient::toQColor(const MSO::OfficeArtCOLORREF& c) { //Have to handle the case when OfficeArtCOLORREF/fSchemeIndex == true. //NOTE: If the hspMaster property (0x0301) is provided by the shape, the //colorScheme of the master slide containing the master shape could be //required. Testing required to implement the correct logic. const MSO::MasterOrSlideContainer* mc = dc_data->masterSlide; const MSO::MainMasterContainer* mm = NULL; const MSO::SlideContainer* tm = NULL; QColor ret; if (mc) { if (mc->anon.is()) { mm = mc->anon.get(); ret = ppttoodp->toQColor(c, mm, dc_data->presSlide); } else if (mc->anon.is()) { tm = mc->anon.get(); ret = ppttoodp->toQColor(c, tm, dc_data->presSlide); } } //TODO: hande the case of a notes master slide/notes slide pair return ret; } QString PptToOdp::DrawClient::formatPos(qreal v) { return mm(v * (25.4 / 576)); } bool PptToOdp::DrawClient::placeholderAllowed(const MSO::PlaceholderAtom* pa) const { //For details check the following chapter: 2.5.10 SlideAtom //[MS-PPT] — v20101219 //TODO: Num. and combinations of placeholder shapes matters! if (!pa || (pa->position == (qint32) 0xFFFFFFFF)) { return false; } quint8 placementId = pa->placementId; quint32 geom = SL_TitleSlide; const MSO::MainMasterContainer* mm = 0; const MSO::SlideContainer* tm = 0; if (ppttoodp->m_processingMasters) { const MSO::MasterOrSlideContainer* mc = dc_data->masterSlide; if (mc) { if (mc->anon.is()) { mm = mc->anon.get(); geom = mm->slideAtom.geom; } else if (mc->anon.is()) { tm = mc->anon.get(); geom = tm->slideAtom.geom; } } } else { if (dc_data->presSlide) { geom = dc_data->presSlide->slideAtom.geom; } } //Main Master Slide if (mm) { switch(geom) { case SL_TitleBody: switch (placementId) { case PT_MasterTitle: case PT_MasterBody: case PT_MasterDate: case PT_MasterFooter: case PT_MasterSlideNumber: return true; default: return false; } default: return false; } } //Title Master Slide if (tm) { switch(geom) { case SL_MasterTitle: switch (placementId) { case PT_MasterCenterTitle: case PT_MasterSubTitle: case PT_MasterDate: case PT_MasterFooter: case PT_MasterSlideNumber: return true; default: return false; } default: return false; } } //Presentation Slide switch(geom) { case SL_TitleSlide: switch (placementId) { case PT_CenterTitle: case PT_SubTitle: return true; default: return false; } case SL_TitleBody: switch (placementId) { case PT_Title: case PT_Body: case PT_Table: case PT_OrgChart: case PT_Graph: case PT_Object: case PT_VerticalBody: return true; default: return false; } case SL_TitleOnly: switch (placementId) { case PT_Title: return true; default: return false; } case SL_TwoColumns: //TODO: support placeholder combinations return true; case SL_TwoRows: case SL_ColumnTwoRows: case SL_TwoRowsColumn: case SL_TwoColumnsRow: switch (placementId) { case PT_Title: case PT_Body: case PT_Object: return true; default: return false; } case SL_FourObjects: switch (placementId) { case PT_Title: case PT_Object: return true; default: return false; } case SL_BigObject: switch (placementId) { case PT_Object: return true; default: return false; } case SL_Blank: //TODO: support placeholder combinations return false; case SL_VerticalTitleBody: switch (placementId) { case PT_VerticalTitle: case PT_VerticalBody: return true; default: return false; } case SL_VerticalTwoRows: switch (placementId) { case PT_VerticalTitle: case PT_VerticalBody: case PT_Graph: return true; default: return false; } default: return false; } } /* * ************************************************ * PptToOdp * ************************************************ */ PptToOdp::PptToOdp(PowerPointImport* filter, void (PowerPointImport::*setProgress)(const int)) : p(0), m_filter(filter), m_setProgress(setProgress), m_progress_update(filter && setProgress), m_currentSlideTexts(0), m_currentMaster(0), m_currentSlide(0), m_processingMasters(false), m_firstChunkSymbolAtStart(false), m_isList(false), m_previousListLevel(0) { qsrand(QTime::currentTime().msec()); } PptToOdp::~PptToOdp() { delete p; } QMap createBulletPictures(const PP9DocBinaryTagExtension* pp9, KoStore* store, KoXmlWriter* manifest) { QMap ids; if (!pp9 || !pp9->blipCollectionContainer) { return ids; } foreach (const BlipEntityAtom& a, pp9->blipCollectionContainer->rgBlipEntityAtom) { PictureReference ref = savePicture(a.blip, store); if (ref.name.length() == 0) continue; ids[a.rh.recInstance] = "Pictures/" + ref.name; manifest->addManifestEntry(ids[a.rh.recInstance], ref.mimetype); } return ids; } bool PptToOdp::parse(POLE::Storage& storage) { delete p; p = 0; ParsedPresentation* pp = new ParsedPresentation(); if (!pp->parse(storage)) { delete pp; return false; } p = pp; return true; } KoFilter::ConversionStatus PptToOdp::convert(const QString& inputFile, const QString& to, KoStore::Backend storeType) { if (m_progress_update) { (m_filter->*m_setProgress)(0); } // open inputFile POLE::Storage storage(inputFile.toLocal8Bit()); if (!storage.open()) { qDebug() << "Cannot open " << inputFile; return KoFilter::InvalidFormat; } if (!parse(storage)) { qDebug() << "Parsing and setup failed."; return KoFilter::InvalidFormat; } // using an average here, parsing might take longer than conversion if (m_progress_update) { (m_filter->*m_setProgress)(40); } // create output store KoStore* storeout = KoStore::createStore(to, KoStore::Write, KoOdf::mimeType(KoOdf::Presentation), storeType); if (!storeout) { kWarning() << "Couldn't open the requested file."; return KoFilter::FileNotFound; } KoFilter::ConversionStatus status = doConversion(storeout); if (m_progress_update) { (m_filter->*m_setProgress)(100); } delete storeout; return status; } KoFilter::ConversionStatus PptToOdp::convert(POLE::Storage& storage, KoStore* storeout) { if (!parse(storage)) { qDebug() << "Parsing and setup failed."; return KoFilter::InvalidFormat; } return doConversion(storeout); } KoFilter::ConversionStatus PptToOdp::doConversion(KoStore* storeout) { KoOdfWriteStore odfWriter(storeout); KoXmlWriter* manifest = odfWriter.manifestWriter( KoOdf::mimeType(KoOdf::Presentation)); // store the images from the 'Pictures' stream storeout->disallowNameExpansion(); storeout->enterDirectory("Pictures"); pictureNames = createPictures(storeout, manifest, &p->pictures.anon1.rgfb); // read pictures from the PowerPoint Document structures bulletPictureNames = createBulletPictures(getPP( p->documentContainer), storeout, manifest); storeout->leaveDirectory(); KoGenStyles styles; createMainStyles(styles); // store document content if (!storeout->open("content.xml")) { kWarning() << "Couldn't open the file 'content.xml'."; delete p; p = 0; return KoFilter::CreationError; } storeout->write(createContent(styles)); if (!storeout->close()) { delete p; p = 0; return KoFilter::CreationError; } manifest->addManifestEntry("content.xml", "text/xml"); // store document styles styles.saveOdfStylesDotXml(storeout, manifest); if (!storeout->open("meta.xml")) { kWarning() << "Couldn't open the file 'meta.xml'."; delete p; p = 0; return KoFilter::CreationError; } storeout->write(createMeta()); if (!storeout->close()) { delete p; p = 0; return KoFilter::CreationError; } manifest->addManifestEntry("meta.xml", "text/xml"); if (!storeout->open("settings.xml")) { kWarning() << "Couldn't open the file 'settings.xml'."; delete p; p = 0; return KoFilter::CreationError; } storeout->write("" "\n"); if (!storeout->close()) { delete p; p = 0; return KoFilter::CreationError; } manifest->addManifestEntry("settings.xml", "text/xml"); odfWriter.closeManifestWriter(); delete p; p = 0; return KoFilter::OK; } namespace { QString definePageLayout(KoGenStyles& styles, const MSO::PointStruct& size) { // x and y are given in master units (1/576 inches) double sizeX = size.x * (25.4 / (double)576); double sizeY = size.y * (25.4 / (double)576); QString pageWidth = mm(sizeX); QString pageHeight = mm(sizeY); KoGenStyle pl(KoGenStyle::PageLayoutStyle); pl.setAutoStyleInStylesDotXml(true); // pl.addAttribute("style:page-usage", "all"); // probably not needed pl.addProperty("fo:margin-bottom", "0pt"); pl.addProperty("fo:margin-left", "0pt"); pl.addProperty("fo:margin-right", "0pt"); pl.addProperty("fo:margin-top", "0pt"); pl.addProperty("fo:page-height", pageHeight); pl.addProperty("fo:page-width", pageWidth); pl.addProperty("style:print-orientation", "landscape"); return styles.insert(pl, "pm"); } } //namespace void PptToOdp::defineDefaultTextStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::TextStyle, "text"); style.setDefaultStyle(true); defineDefaultTextProperties(style); styles.insert(style); } void PptToOdp::defineDefaultParagraphStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::ParagraphStyle, "paragraph"); style.setDefaultStyle(true); defineDefaultParagraphProperties(style); defineDefaultTextProperties(style); styles.insert(style); } void PptToOdp::defineDefaultSectionStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::SectionStyle, "section"); style.setDefaultStyle(true); styles.insert(style); } void PptToOdp::defineDefaultRubyStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::RubyStyle, "ruby"); style.setDefaultStyle(true); styles.insert(style); } void PptToOdp::defineDefaultTableStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::TableStyle, "table"); style.setDefaultStyle(true); styles.insert(style); } void PptToOdp::defineDefaultTableColumnStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::TableColumnStyle, "table-column"); style.setDefaultStyle(true); styles.insert(style); } void PptToOdp::defineDefaultTableRowStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::TableRowStyle, "table-row"); style.setDefaultStyle(true); styles.insert(style); } void PptToOdp::defineDefaultTableCellStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::TableCellStyle, "table-cell"); style.setDefaultStyle(true); defineDefaultParagraphProperties(style); defineDefaultTextProperties(style); styles.insert(style); } void PptToOdp::defineDefaultGraphicStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::GraphicStyle, "graphic"); style.setDefaultStyle(true); defineDefaultGraphicProperties(style, styles); defineDefaultParagraphProperties(style); defineDefaultTextProperties(style); styles.insert(style); } void PptToOdp::defineDefaultPresentationStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::PresentationStyle, "presentation"); style.setDefaultStyle(true); defineDefaultGraphicProperties(style, styles); defineDefaultParagraphProperties(style); defineDefaultTextProperties(style); styles.insert(style); } void PptToOdp::defineDefaultDrawingPageStyle(KoGenStyles& styles) { if (!p->documentContainer) return; // write style KoGenStyle style(KoGenStyle::DrawingPageStyle, "drawing-page"); const KoGenStyle::PropertyType dpt = KoGenStyle::DrawingPageType; style.addProperty("draw:background-size", "border", dpt); style.addProperty("draw:fill", "none", dpt); style.setDefaultStyle(true); const MSO::SlideHeadersFootersContainer* hf = getSlideHF(); const OfficeArtDggContainer* drawingGroup = &p->documentContainer->drawingGroup.OfficeArtDgg; DrawStyle ds(drawingGroup); DrawClient drawclient(this); ODrawToOdf odrawtoodf(drawclient); drawclient.setDrawClientData(0, 0, 0, 0); defineDrawingPageStyle(style, ds, styles, odrawtoodf, (hf) ?&hf->hfAtom :0); styles.insert(style); } void PptToOdp::defineDefaultChartStyle(KoGenStyles& styles) { // write style KoGenStyle style(KoGenStyle::ChartStyle, "chart"); style.setDefaultStyle(true); defineDefaultGraphicProperties(style, styles); defineDefaultParagraphProperties(style); defineDefaultTextProperties(style); styles.insert(style); } void PptToOdp::defineDefaultTextProperties(KoGenStyle& style) { const PptTextCFRun cf(p->documentContainer); const TextCFException9* cf9 = 0; const TextCFException10* cf10 = 0; const TextSIException* si = 0; if (p->documentContainer) { const PP9DocBinaryTagExtension* pp9 = getPP( p->documentContainer); const PP10DocBinaryTagExtension* pp10 = getPP( p->documentContainer); if (pp9 && pp9->textDefaultsAtom) { cf9 = &pp9->textDefaultsAtom->cf9; } if (pp10 && pp10->textDefaultsAtom) { cf10 = &pp10->textDefaultsAtom->cf10; } si = &p->documentContainer->documentTextInfo.textSIDefaultsAtom.textSIException; } defineTextProperties(style, cf, cf9, cf10, si); } void PptToOdp::defineDefaultParagraphProperties(KoGenStyle& style) { PptTextPFRun pf(p->documentContainer); defineParagraphProperties(style, pf, 0); } void PptToOdp::defineDefaultGraphicProperties(KoGenStyle& style, KoGenStyles& styles) { const KoGenStyle::PropertyType gt = KoGenStyle::GraphicType; style.addProperty("svg:stroke-width", "0.75pt", gt); // 2.3.8.15 style.addProperty("draw:fill", "none", gt); // 2.3.8.38 style.addProperty("draw:auto-grow-height", false, gt); style.addProperty("draw:stroke", "solid", gt); style.addProperty("draw:fill-color", "#ffffff", gt); const OfficeArtDggContainer* drawingGroup = &p->documentContainer->drawingGroup.OfficeArtDgg; const DrawStyle ds(drawingGroup); DrawClient drawclient(this); ODrawToOdf odrawtoodf(drawclient); odrawtoodf.defineGraphicProperties(style, ds, styles); } template void setRgbUid(const T* a, QByteArray& rgbUid) { if (!a) return; rgbUid = a->rgbUid1 + a->rgbUid2; } QString PptToOdp::getPicturePath(const quint32 pib) const { bool use_offset = false; quint32 offset = 0; const OfficeArtDggContainer& dgg = p->documentContainer->drawingGroup.OfficeArtDgg; QByteArray rgbUid = getRgbUid(dgg, pib, offset); if (!rgbUid.isEmpty()) { if (pictureNames.contains(rgbUid)) { return "Pictures/" + pictureNames[rgbUid]; } else { qDebug() << "UNKNOWN picture reference:" << rgbUid.toHex(); use_offset = true; rgbUid.clear(); } } if (use_offset) { const OfficeArtBStoreDelay& d = p->pictures.anon1; foreach (const OfficeArtBStoreContainerFileBlock& block, d.rgfb) { if (block.anon.is()) { if (block.anon.get()->streamOffset == offset) { const OfficeArtBlip* b = block.anon.get(); setRgbUid(b->anon.get(), rgbUid); setRgbUid(b->anon.get(), rgbUid); setRgbUid(b->anon.get(), rgbUid); setRgbUid(b->anon.get(), rgbUid); setRgbUid(b->anon.get(), rgbUid); setRgbUid(b->anon.get(), rgbUid); setRgbUid(b->anon.get(), rgbUid); if (!rgbUid.isEmpty()) { if (pictureNames.contains(rgbUid)) { qDebug() << "Reusing OfficeArtBlip offset:" << offset; return "Pictures/" + pictureNames[rgbUid]; } } } } } } return QString(); } void PptToOdp::defineTextProperties(KoGenStyle& style, const PptTextCFRun& cf, const TextCFException9* /*cf9*/, const TextCFException10* /*cf10*/, const TextSIException* /*si*/, const bool isSymbol) { // Getting information for all the possible attributes in // style:text-properties for clarity in alphabetical order. const KoGenStyle::PropertyType text = KoGenStyle::TextType; // symbol font has precedence bool isSymbolFont = false; // fo:background-color // fo:color ColorIndexStruct cis = cf.color(); QColor color = toQColor(cis); if (color.isValid()) { style.addProperty("fo:color", color.name(), text); } // fo:country // fo:font-family const FontEntityAtom* font = 0; if (cf.symbolFontRef() && isSymbol) { if ( (font = getFont(cf.symbolFontRef())) != 0 ) { isSymbolFont = true; } } if (!font) { font = getFont(cf.fontRef()); } if (font) { #ifdef DEBUG_PPTTOODP_FONTS qDebug() << "DEBUG: FontEntityAtom"; qDebug() << "> IfCharSet:" << font->lfCharSet; qDebug() << "> fEmbedSubsetted:" << font->fEmbedSubsetted; qDebug() << "> rasterFontType:" << font->rasterFontType; qDebug() << "> deviceFontType:" << font->deviceFontType; qDebug() << "> truetypeFontType:" << font->truetypeFontType; qDebug() << "> fNoFontSubstitution:" << font->fNoFontSubstitution; qDebug() << "DEBUG END: FontEntityAtom"; #endif const QString name = QString::fromUtf16(font->lfFaceName.data(), font->lfFaceName.size()); style.addProperty("fo:font-family", name, text); } // fo:font-size if (cf.fontSize() > 0) { style.addProperty("fo:font-size", pt(cf.fontSize()), text); } // fo:font-style: "italic", "normal" or "oblique style.addProperty("fo:font-style", cf.italic() ?"italic" :"normal", text); // fo:font-variant: "normal" or "small-caps" // fo:font-weight: "100", "200", "300", "400", "500", "600", "700", "800", "900", "bold" or "normal" style.addProperty("fo:font-weight", cf.bold() ?"bold" :"normal", text); // fo:hyphenate // fo:hyphenation-push-char // fo:hyphenation-remain-char-count // fo:language // if (si && si->lang) { // TODO: get mapping from lid to language code // } // fo:letter-spacing // fo:text-shadow style.addProperty("fo:text-shadow", cf.shadow() ?"1pt 1pt" :"none", text); // fo:text-transform: "capitalize", "lowercase", "none" or "uppercase" // style:country-asian // style:country-complex // style:font-charset if (isSymbolFont) { style.addProperty("style:font-charset", "x-symbol", text); } // style:font-family-asian // style:font-family-complex // style:font-family-generic // style:font-family-generic-asian // style:font-family-generic-complex // style:font-name // style:font-name-asian // style:font-name-complex // style:font-pitch // style:font-pitch-asian // style:font-pitch-complex // style:font-relief: "embossed", "engraved" or "none" style.addProperty("style:font-relief", cf.emboss() ?"embossed" :"none", text); // style:font-size-asian // style:font-size-complex // style:font-size-rel // style:font-size-rel-asian // style:font-size-rel-complex // style:font-style-asian // style:font-style-complex // style:font-style-name // style:font-style-name-asian // style:font-style-name-complex // style:font-weight-asian // style:font-weight-complex // style:language-asian // style:language-complex // style:letter-kerning // style:script-type // style:text-blinking // style:text-combine // style:text-combine-end-char // style:text-combine-start-char // style:text-emphasize // style:text-line-through-color // style:text-line-through-mode // style:text-line-through-style // style:text-line-through-text // style:text-line-through-text-style // style:text-line-through-type // style:text-line-through-width // style:text-outline // style:text-position style.addProperty("style:text-position", percent(cf.position()), text); // style:text-rotation-angle // style:text-rotation-scale // style:text-scale // style:text-underline-color // style:text-underline-mode // style:text-underline-style // style:text-underline-type: "double", "none" or "single" style.addProperty("style:text-underline-type", cf.underline() ?"single" :"none", text); // style:text-underline-width // style:use-window-font-color } //end defineTextProperties() void PptToOdp::defineParagraphProperties(KoGenStyle& style, const PptTextPFRun& pf, const quint16 fs) { const KoGenStyle::PropertyType para = KoGenStyle::ParagraphType; // fo:background-color // fo:border // fo:border-bottom // fo:border-left // fo:border-right // fo:border-top // fo:break-after // fo:break-before // fo:hyphenation-keep // fo:hyphenation-ladder-count // fo:keep-together // fo:keep-with-next // fo:line-height style.addProperty("fo:line-height", processParaSpacing(pf.lineSpacing(), fs, true), para); // fo:margin // fo:margin-bottom style.addProperty("fo:margin-bottom", processParaSpacing(pf.spaceAfter(), fs, false), para); // fo:margin-left if (m_isList) { style.addProperty("fo:margin-left", "0cm", para); } else { style.addProperty("fo:margin-left", pptMasterUnitToCm(pf.leftMargin()), para); } // fo:margin-right style.addProperty("fo:margin-right", "0cm", para); // fo:margin-top style.addProperty("fo:margin-top", processParaSpacing(pf.spaceBefore(), fs, false), para); // fo:orphans // fo:padding // fo:padding-bottom // fo:padding-left // fo:padding-right // fo:padding-top // fo:text-align const QString align = textAlignmentToString(pf.textAlignment()); if (!align.isEmpty()) { style.addProperty("fo:text-align", align, para); } // fo:text-align-last // fo:text-indent quint16 indent = pf.indent(); // NOTE: MS PowerPoint UI - Setting the indent value for the paragraph at // level ZERO has no effect, however the set vale is stored. if (!pf.level()) { indent = 0; } if (!m_isList) { style.addProperty("fo:text-indent", pptMasterUnitToCm(indent - pf.leftMargin()), para); } else { //text:space-before already set in style:list-level-properties style.addProperty("fo:text-indent", "0cm", para); } // fo:widows // style:auto-text-indent // style:background-transparency // style:border-line-width // style:border-line-width-bottom // style:border-line-width-left // style:border-line-width-right // style:border-line-width-top // style:font-independent-line-spacing style.addProperty("style:font-independent-line-spacing", (pf.lineSpacing() >= 0) ? "true" : "false", para); // style:justify-single-word // style:line-break // style:line-height-at-least // style:line-spacing // style:page-number // style:punctuation-wrap // style:register-true // style:shadow // style:snap-to-layout-grid // style:tab-stop-distance // style:text-autospace // style:vertical-align // style:writing-mode // style:writing-mode-automatic // text:line-number // text:number-lines } //end defineParagraphProperties() void PptToOdp::defineDrawingPageStyle(KoGenStyle& style, const DrawStyle& ds, KoGenStyles& styles, ODrawToOdf& odrawtoodf, const MSO::HeadersFootersAtom* hf, const MSO::SlideFlags* sf) { const KoGenStyle::PropertyType dp = KoGenStyle::DrawingPageType; // Inherit the background of the main master slide/title master slide or // notes master slide if slideFlags/fMasterBackground == true. The // drawing-page style defined in the will be used. if (!sf || (sf && !sf->fMasterBackground)) { // fFilled - a boolean property which specifies whether fill of the shape // is render based on the properties of the "fill style" property set. if (ds.fFilled()) { // draw:background-size ("border", or "full") style.addProperty("draw:background-size", ds.fillUseRect() ?"border" :"full", dp); // draw:fill ("bitmap", "gradient", "hatch", "none" or "solid") quint32 fillType = ds.fillType(); style.addProperty("draw:fill", getFillType(fillType), dp); // draw:fill-color switch (fillType) { case msofillSolid: { QColor color = odrawtoodf.processOfficeArtCOLORREF(ds.fillColor(), ds); style.addProperty("draw:fill-color", color.name(), dp); break; } // draw:fill-gradient-name case msofillShade: case msofillShadeCenter: case msofillShadeShape: case msofillShadeScale: case msofillShadeTitle: { KoGenStyle gs(KoGenStyle::LinearGradientStyle); odrawtoodf.defineGradientStyle(gs, ds); QString gname = styles.insert(gs); style.addProperty("draw:fill-gradient-name", gname, dp); break; } // draw:fill-hatch-name // draw:fill-hatch-solid // draw:fill-image-height // draw:fill-image-name case msofillPattern: case msofillTexture: case msofillPicture: { quint32 fillBlip = ds.fillBlip(); const QString fillImagePath = getPicturePath(fillBlip); if (!fillImagePath.isEmpty()) { style.addProperty("draw:fill-image-name", "fillImage" + QString::number(fillBlip), dp); style.addProperty("style:repeat", getRepeatStyle(fillType), dp); } break; } //TODO: case msofillBackground: default: break; } // draw:fill-image-ref-point-x // draw:fill-image-ref-point-y // draw:fill-image-ref-point // draw:fill-image-width // draw:gradient-step-count // draw:opacity-name // draw:opacity style.addProperty("draw:opacity", percent(100.0 * toQReal(ds.fillOpacity())), dp); // draw:secondary-fill-color // draw:tile-repeat-offset // style:repeat // handled for image see draw:fill-image-name } else { style.addProperty("draw:fill", "none", dp); } } // presentation:background-objects-visible if (sf && !sf->fMasterObjects) { style.addProperty("presentation:background-objects-visible", false); } else { style.addProperty("presentation:background-objects-visible", true); } // presentation:background-visible style.addProperty("presentation:background-visible", true); // presentation:display-date-time if (hf) { style.addProperty("presentation:display-date-time", hf->fHasDate, dp); } // presentation:display-footer if (hf) { style.addProperty("presentation:display-footer", hf->fHasFooter, dp); } // presentation:display-header if (hf) { style.addProperty("presentation:display-header", hf->fHasHeader, dp); } // presentation:display-page-number if (hf) { style.addProperty("presentation:display-page-number", hf->fHasSlideNumber, dp); } // presentation:duration // presentation:transition-speed // presentation:transition-style // presentation:transition-type // presentation:visibility // svg:fill-rule // smil:direction // smil:fadeColor // smil:subtype // smil:type } //end defineDrawingPageStyle() void PptToOdp::defineListStyle(KoGenStyle& style, const quint32 textType, const TextMasterStyleAtom& levels, const TextMasterStyle9Atom* levels9, const TextMasterStyle10Atom* levels10) { if (levels.lstLvl1) { defineListStyle(style, 0, textType, levels.lstLvl1.data(), ((levels9) ?levels9->lstLvl1.data() :0), ((levels10) ?levels10->lstLvl1.data() :0)); } if (levels.lstLvl2) { defineListStyle(style, 1, textType, levels.lstLvl2.data(), ((levels9) ?levels9->lstLvl2.data() :0), ((levels10) ?levels10->lstLvl2.data() :0)); } if (levels.lstLvl3) { defineListStyle(style, 2, textType, levels.lstLvl3.data(), ((levels9) ?levels9->lstLvl3.data() :0), ((levels10) ?levels10->lstLvl3.data() :0)); } if (levels.lstLvl4) { defineListStyle(style, 3, textType, levels.lstLvl4.data(), ((levels9) ?levels9->lstLvl4.data() :0), ((levels10) ?levels10->lstLvl4.data() :0)); } if (levels.lstLvl5) { defineListStyle(style, 4, textType, levels.lstLvl5.data(), ((levels9) ?levels9->lstLvl5.data() :0), ((levels10) ?levels10->lstLvl5.data() :0)); } } void PptToOdp::defineListStyle(KoGenStyle& style, const quint32 textType, const quint16 indentLevel, const TextMasterStyleLevel* level, const TextMasterStyle9Level* level9, const TextMasterStyle10Level* level10) { PptTextPFRun pf(p->documentContainer, level, level9, textType, indentLevel); PptTextCFRun cf(p->documentContainer, level, level9, indentLevel); ListStyleInput info(pf, cf); info.cf9 = (level9) ?&level9->cf9 :0; info.cf10 = (level10) ?&level10->cf10 :0; defineListStyle(style, indentLevel, info); } namespace { QChar getBulletChar(const PptTextPFRun& pf) { quint16 v = (quint16) pf.bulletChar(); // if ((v == 0xf06c) || (v == 0x006c)) { // 0xF06C from "Windings" is similar to â— // return QChar(0x25cf); // "â—" // } // if (v == 0xf02d) { // 0xF02D from "Symbol" is similar to – // return QChar(0x2013); // } // if (v == 0xf0e8) { // 0xF0E8 is similar to âž” // return QChar(0x2794); // } // if (v == 0xf0d8) { // 0xF0D8 is similar to ➢ // return QChar(0x27a2); // } // if (v == 0xf0fb) { // 0xF0FB is similar to ✗ // return QChar(0x2717); // } // if (v == 0xf0fc) { // 0xF0FC is similar to ✔ // return QChar(0x2714); // } return QChar(v); // return QChar(0x25cf); // "â—" } /** * Convert bulletSize value. * * BulletSize is a 2-byte signed integer that specifies the bullet font size. * It must be a value from the following intervals: * * x = value, x in <25, 400>, specifies bullet font size as a percentage of the * font size of the first text run in the paragraph. * * x in <-4000, -1>, The absolute value specifies the bullet font size in pt. * * @param value to convert * @return processed value in points or percentage */ QString bulletSizeToSizeString(qint16 value) { QString ret; if (value >= 25 && value <= 400) { ret = percent(value); } else if ((value >= -4000) && (value <= -1)) { ret = pt(qAbs(value)); } else { ret = percent(100); } return ret; } } //namespace void PptToOdp::defineListStyle(KoGenStyle& style, const quint16 depth, const ListStyleInput& i) { QBuffer buffer; buffer.open(QIODevice::WriteOnly); KoXmlWriter out(&buffer); QString bulletSize; if (i.pf.bulletSize()) { bulletSize = bulletSizeToSizeString(i.pf.bulletSize()); } else { bulletSize = pt(m_firstChunkFontSize); } QString elementName; bool imageBullet = false; imageBullet = i.pf.bulletBlipRef() != 65535; if (imageBullet) { elementName = "text:list-level-style-image"; out.startElement("text:list-level-style-image"); out.addAttribute("xlink:href", bulletPictureNames.value(i.pf.bulletBlipRef())); out.addAttribute("xlink:type", "simple"); } else if (i.pf.fBulletHasAutoNumber() || i.pf.fHasBullet()) { QString numFormat("1"), numSuffix, numPrefix; processTextAutoNumberScheme(i.pf.scheme(), numFormat, numSuffix, numPrefix); // If there is no bulletChar or the bullet has autonumbering explicitly // we assume it's a numbered list if (i.pf.fBulletHasAutoNumber() || i.pf.bulletChar() == 0) { elementName = "text:list-level-style-number"; out.startElement("text:list-level-style-number"); if (!numFormat.isNull()) { out.addAttribute("style:num-format", numFormat); } // style:display-levels out.addAttribute("text:start-value", i.pf.startNum()); if (!numPrefix.isNull()) { out.addAttribute("style:num-prefix", numPrefix); } if (!numSuffix.isNull()) { out.addAttribute("style:num-suffix", numSuffix); } } else { elementName = "text:list-level-style-bullet"; out.startElement("text:list-level-style-bullet"); out.addAttribute("text:bullet-char", getBulletChar(i.pf)); // text:bullet-relative-size } } //no bullet exists (i.pf.fHasBullet() == false) else { elementName = "text:list-level-style-number"; out.startElement("text:list-level-style-number"); out.addAttribute("style:num-format", ""); } out.addAttribute("text:level", depth + 1); out.startElement("style:list-level-properties"); if (imageBullet) { QString pictureSize = bulletSize; if (pictureSize.endsWith("%")) { pictureSize.chop(1); bool ok = false; qreal size = pictureSize.toDouble(&ok); if (!ok) { qDebug() << "defineBulletStyle: error converting" << pictureSize << "to double"; } size = m_firstChunkFontSize * size / 100.0; pictureSize = pt(size); } // fo:text-align // fo:height out.addAttribute("fo:height", pictureSize); // fo:width out.addAttribute("fo:width", pictureSize); // style:font-name // style:vertical-pos out.addAttribute("style:vertical-pos", "middle"); // style:vertical-rel out.addAttribute("style:vertical-rel", "line"); // svg:x // svg:y } quint16 indent = i.pf.indent(); // text:min-label-distance // text:min-label-width out.addAttribute("text:min-label-width", pptMasterUnitToCm(i.pf.leftMargin() - indent)); // text:space-before out.addAttribute("text:space-before", pptMasterUnitToCm(indent)); out.endElement(); // style:list-level-properties //--------------------------------------------- // text-properties //--------------------------------------------- if (!imageBullet) { KoGenStyle ts(KoGenStyle::TextStyle); const KoGenStyle::PropertyType text = KoGenStyle::TextType; //bulletSize already processed ts.addProperty("fo:font-size", bulletSize, text); //default value doesn't make sense QColor color; if (i.pf.fBulletHasColor()) { color = toQColor(i.pf.bulletColor()); if (color.isValid()) { ts.addProperty("fo:color", color.name(), text); } } const MSO::FontEntityAtom* font = 0; //MSPowerPoint: UI does NOT enable to change font of a //numbered lists label. if (i.pf.fBulletHasFont() && !i.pf.fBulletHasAutoNumber()) { font = getFont(i.pf.bulletFontRef()); } //A list label should NOT inherit a symbol font. if (!font && m_firstChunkSymbolAtStart) { font = getFont(m_firstChunkFontRef); } if (font) { QString family = QString::fromUtf16(font->lfFaceName.data(), font->lfFaceName.size()); ts.addProperty("fo:font-family", family, text); } //MSPowerPoint: A label does NOT inherit Underline from //text-properties of the 1st text chunk. A bullet does NOT //inherit properties in {Italics, Bold}. if (!i.pf.fBulletHasAutoNumber()) { ts.addProperty("fo:font-style", "normal"); ts.addProperty("fo:font-weight", "normal"); } ts.addProperty("style:text-underline-style", "none"); ts.writeStyleProperties(&out, text); } out.endElement(); // text:list-level-style-* // serialize the text:list-style element into the properties QString contents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size()); style.addChildElement(elementName, contents); } //end defineListStyle() template void handleOfficeArtContainer(O& handler, const OfficeArtSpgrContainerFileBlock& c) { const OfficeArtSpContainer* a = c.anon.get(); const OfficeArtSpgrContainer* b= c.anon.get(); if (a) { handler.handle(*a); } else { foreach (const OfficeArtSpgrContainerFileBlock& fb, b->rgfb) { handleOfficeArtContainer(handler, fb); } } } template void handleOfficeArtContainer(O& handler, const MSO::OfficeArtDgContainer& c) { if (c.shape) { handler.handle(*c.shape); } if (c.groupShape) { foreach (const OfficeArtSpgrContainerFileBlock& fb, c.groupShape->rgfb) { handleOfficeArtContainer(handler, fb); } } } class PlaceholderFinder { public: quint32 wanted; const MSO::OfficeArtSpContainer* sp; PlaceholderFinder(int w) :wanted(w), sp(0) {} void handle(const MSO::OfficeArtSpContainer& o) { if (o.clientTextbox) { const PptOfficeArtClientTextBox* b = o.clientTextbox->anon.get(); if (b) { foreach (const TextClientDataSubContainerOrAtom& a, b->rgChildRec) { const TextContainer* tc = a.anon.get(); if (tc && tc->textHeaderAtom.textType == wanted) { if (sp) { qDebug() << "Already found a placeholder with the right type " << wanted; } else { sp = &o; } } } } } } }; void PptToOdp::defineMasterStyles(KoGenStyles& styles) { foreach (const MSO::MasterOrSlideContainer* m, p->masters) { m_currentMaster = m; const SlideContainer* sc = m->anon.get(); const MainMasterContainer* mm = m->anon.get(); // look for a style for each of the values of TextEnumType for (quint16 texttype = 0; texttype <= 8; ++texttype) { // look for placeholder with the right texttype PlaceholderFinder finder(texttype); if (sc) { handleOfficeArtContainer(finder, sc->drawing.OfficeArtDg); } else if (mm) { handleOfficeArtContainer(finder, mm->drawing.OfficeArtDg); } if (finder.sp) { QBuffer buffer; KoXmlWriter dummy(&buffer); Writer w(dummy, styles, true); DrawClient drawclient(this); ODrawToOdf odrawtoodf(drawclient); odrawtoodf.addGraphicStyleToDrawElement(w, *finder.sp); } } // if no style for Tx_TYPE_CENTERTITLE (6) has been defined yet, // derive it from Tx_TYPE_TITLE (0) if (!masterPresentationStyles[m].contains(6) && masterPresentationStyles[m].contains(0)) { KoGenStyle style(KoGenStyle::PresentationStyle, "presentation"); style.setParentName(masterPresentationStyles[m][0]); style.addProperty("fo:text-align", "center", KoGenStyle::ParagraphType); style.addProperty("style:vertical-align", "middle", KoGenStyle::ParagraphType); masterPresentationStyles[m][6] = styles.insert(style); } // if no style for Tx_TYPE_CENTERBODY (5) has been defined yet, // derive it from Tx_TYPE_BODY (1) if (!masterPresentationStyles[m].contains(5) && masterPresentationStyles[m].contains(1)) { KoGenStyle style(KoGenStyle::PresentationStyle, "presentation"); style.setParentName(masterPresentationStyles[m][1]); style.addProperty("fo:text-align", "center", KoGenStyle::ParagraphType); // style.addProperty("style:vertical-align", "middle", // KoGenStyle::ParagraphType); masterPresentationStyles[m][5] = styles.insert(style); } } m_currentMaster = NULL; } void PptToOdp::defineAutomaticDrawingPageStyles(KoGenStyles& styles) { DrawClient drawclient(this); ODrawToOdf odrawtoodf(drawclient); // define for master for use in foreach (const MSO::MasterOrSlideContainer* m, p->masters) { KoGenStyle dp(KoGenStyle::DrawingPageAutoStyle, "drawing-page"); dp.setAutoStyleInStylesDotXml(true); const SlideContainer* sc = m->anon.get(); const MainMasterContainer* mm = m->anon.get(); const HeadersFootersAtom* hf = 0; const OfficeArtSpContainer* scp = getMasterShape(m); if (sc) { if (sc->perSlideHFContainer) { hf = &sc->perSlideHFContainer->hfAtom; } } else if (mm) { if (mm->perSlideHeadersFootersContainer) { hf = &mm->perSlideHeadersFootersContainer->hfAtom; } } //NOTE: Use default values of properties, looks like in case of PPT the //OfficeArtDggContainer has to be ignored DrawStyle ds(0, scp); drawclient.setDrawClientData(m, 0, 0, 0); defineDrawingPageStyle(dp, ds, styles, odrawtoodf, hf); drawingPageStyles[m] = styles.insert(dp, "Mdp"); } QString notesMasterPageStyle; if (p->notesMaster) { const HeadersFootersAtom* hf = 0; if (p->notesMaster->perSlideHFContainer) { hf = &p->notesMaster->perSlideHFContainer->hfAtom; } else if (p->notesMaster->perSlideHFContainer2) { hf = &p->notesMaster->perSlideHFContainer2->hfAtom; } KoGenStyle dp(KoGenStyle::DrawingPageAutoStyle, "drawing-page"); dp.setAutoStyleInStylesDotXml(true); const OfficeArtDggContainer* drawingGroup = &p->documentContainer->drawingGroup.OfficeArtDgg; DrawStyle ds(drawingGroup, p->notesMaster->drawing.OfficeArtDg.shape.data()); drawclient.setDrawClientData(0, 0, p->notesMaster, 0); defineDrawingPageStyle(dp, ds, styles, odrawtoodf, hf); notesMasterPageStyle = styles.insert(dp, "Mdp"); drawingPageStyles[p->notesMaster] = notesMasterPageStyle; } // TODO: define for handouts for use in // define for slides for use in foreach (const MSO::SlideContainer* sc, p->slides) { KoGenStyle dp(KoGenStyle::DrawingPageAutoStyle, "drawing-page"); dp.setAutoStyleInStylesDotXml(false); const MasterOrSlideContainer* m = p->getMaster(sc); const PerSlideHeadersFootersContainer* hfc = getPerSlideHF(sc); HeadersFootersAtom hf; if (hfc) { hf = hfc->hfAtom; } else { //Default values saved by MS Office 2003 require corrections. const SlideHeadersFootersContainer* dhfc = getSlideHF(); if (dhfc) { hf = dhfc->hfAtom; if (hf.fHasUserDate && !dhfc->userDateAtom.data()) { hf.fHasUserDate = false; } if (hf.fHasDate && !hf.fHasUserDate && !hf.fHasTodayDate) { hf.fHasDate = false; } if (hf.fHasFooter && !dhfc->footerAtom.data()) { hf.fHasFooter = false; } } //PerSlideHeadersFootersContainer and SlideHeadersFootersContainer //are both optional, use default values for the drawing-page style else { hf.fHasDate = hf.fHasTodayDate = hf.fHasUserDate = false; hf.fHasSlideNumber = hf.fHasHeader = hf.fHasFooter = false; hf.formatId = -1; } } const OfficeArtSpContainer* masterSlideShape = getMasterShape(m); const OfficeArtSpContainer* slideShape = sc->drawing.OfficeArtDg.shape.data(); //NOTE: Use default values of properties, looks like in case of PPT the //OfficeArtDggContainer has to be ignored DrawStyle ds(0, masterSlideShape, slideShape); drawclient.setDrawClientData(m, sc, 0, 0); defineDrawingPageStyle(dp, ds, styles, odrawtoodf, &hf, &sc->slideAtom.slideFlags); drawingPageStyles[sc] = styles.insert(dp, "dp"); } // define for notes for use in foreach (const MSO::NotesContainer* nc, p->notes) { if (!nc) continue; const HeadersFootersAtom* hf = 0; if (nc->perSlideHFContainer) { hf = &nc->perSlideHFContainer->hfAtom; } else if (nc->perSlideHFContainer2) { hf = &nc->perSlideHFContainer2->hfAtom; } // TODO: derive from notes master slide style KoGenStyle dp(KoGenStyle::DrawingPageAutoStyle, "drawing-page"); dp.setAutoStyleInStylesDotXml(false); const OfficeArtDggContainer* drawingGroup = &p->documentContainer->drawingGroup.OfficeArtDgg; DrawStyle ds(drawingGroup, nc->drawing.OfficeArtDg.shape.data()); drawclient.setDrawClientData(0, 0, p->notesMaster, nc); defineDrawingPageStyle(dp, ds, styles, odrawtoodf, hf, &nc->notesAtom.slideFlags); drawingPageStyles[nc] = styles.insert(dp, "dp"); } } //end defineAutomaticDrawingPageStyles() void PptToOdp::createMainStyles(KoGenStyles& styles) { /* This function follows the flow of the styles.xml file. -> style:styles first, the global objects are looked up and defined. This includes the style:presentation-page-layout elements. Next, the default styles for the 12 style families are defined. -> style:automatic-styles After that, style:page-layout and automatic styles are defined -> office:master-styles At last, the master slides are defined */ /* collect all the global objects into styles.xml/office:document-styles/office:styles */ // TODO: draw:gradient // TODO: svg:linearGradient // TODO: svg:radialGradient // TODO: draw:hatch // draw:fill-image FillImageCollector fillImageCollector(styles, *this); collectGlobalObjects(fillImageCollector, *p); // draw:marker (libmso) // TODO: draw:stroke-dash // StrokeDashCollector strokeDashCollector(styles, *this); // collectGlobalObjects(strokeDashCollector, *p); // TODO: draw:opacity /* Define the style:presentation-page-layout elements. */ // TODO: // Define default styles for some of the 12 style families. No // default styles for the families 'text' and 'paragraph' are // defined, since these have higher precedence than the text and // paragraph settings for the other style families that may // contain text and paragraph settings, like 'graphic' and // 'presentation'. //defineDefaultTextStyle(styles); //defineDefaultParagraphStyle(styles); defineDefaultSectionStyle(styles); defineDefaultRubyStyle(styles); defineDefaultTableStyle(styles); defineDefaultTableColumnStyle(styles); defineDefaultTableRowStyle(styles); defineDefaultTableCellStyle(styles); defineDefaultPresentationStyle(styles); defineDefaultChartStyle(styles); if (m_progress_update) { (m_filter->*m_setProgress)(55); } // NOTE: kpresenter specific: default graphic style and // drawing-page style have higher precedence than those defined by // the corresponding element. This is the case when // the presentation slide inherits background objects from the // master slide. // defineDefaultGraphicStyle(styles); // defineDefaultDrawingPageStyle(styles); /* Define the standard list style */ if (p->documentContainer) { KoGenStyle list(KoGenStyle::ListStyle); PptTextPFRun pf(p->documentContainer); PptTextCFRun cf(p->documentContainer); ListStyleInput info(pf, cf); defineListStyle(list, 0, info); styles.insert(list, "standardListStyle", KoGenStyles::DontAddNumberToName); } /* Define the style:page-layout elements, for ppt files there are only two. */ slidePageLayoutName = definePageLayout(styles, p->documentContainer->documentAtom.slideSize); notesPageLayoutName = definePageLayout(styles, p->documentContainer->documentAtom.notesSize); /* Define the automatic styles */ m_currentSlideTexts = 0; defineMasterStyles(styles); defineAutomaticDrawingPageStyles(styles); if (m_progress_update) { (m_filter->*m_setProgress)(60); } /* Define the draw:layer-set. */ // TODO: /* Define the style:handout-master */ // TODO: /* Define the style:master-pages */ DrawClient drawclient(this); ODrawToOdf odrawtoodf(drawclient); QBuffer notesBuffer; if (p->notesMaster) { // draw the notes master notesBuffer.open(QIODevice::WriteOnly); KoXmlWriter writer(¬esBuffer); Writer out(writer, styles, true); writer.startElement("presentation:notes"); writer.addAttribute("style:page-layout-name", notesPageLayoutName); writer.addAttribute("draw:style-name", drawingPageStyles[p->notesMaster]); m_currentMaster = 0; if (p->notesMaster->drawing.OfficeArtDg.groupShape) { const OfficeArtSpgrContainer& spgr = *(p->notesMaster->drawing.OfficeArtDg.groupShape).data(); drawclient.setDrawClientData(0, 0, p->notesMaster, 0); odrawtoodf.processGroupShape(spgr, out); } writer.endElement(); } m_processingMasters = true; foreach (const MSO::MasterOrSlideContainer* m, p->masters) { const SlideContainer* sc = m->anon.get(); const MainMasterContainer* mm = m->anon.get(); const DrawingContainer* drawing = 0; if (sc) { drawing = &sc->drawing; } else if (mm) { drawing = &mm->drawing; } KoGenStyle master(KoGenStyle::MasterPageStyle); master.addAttribute("style:page-layout-name", slidePageLayoutName); master.addAttribute("draw:style-name", drawingPageStyles[m]); m_currentMaster = m; QBuffer buffer; buffer.open(QIODevice::WriteOnly); KoXmlWriter writer(&buffer); Writer out(writer, styles, true); if (drawing->OfficeArtDg.groupShape) { const OfficeArtSpgrContainer& spgr = *(drawing->OfficeArtDg.groupShape).data(); drawclient.setDrawClientData(m, 0, 0, 0); odrawtoodf.processGroupShape(spgr, out); } master.addChildElement("", QString::fromUtf8(buffer.buffer(), buffer.buffer().size())); if (notesBuffer.buffer().size()) { master.addChildElement("presentation:notes", QString::fromUtf8(notesBuffer.buffer(), notesBuffer.buffer().size())); } masterNames[m] = styles.insert(master, "M"); } m_currentMaster = 0; m_processingMasters = false; // Creating dateTime class object if (getSlideHF()) { int dateTimeFomatId = getSlideHF()->hfAtom.formatId; bool hasTodayDate = getSlideHF()->hfAtom.fHasTodayDate; bool hasUserDate = getSlideHF()->hfAtom.fHasUserDate; dateTime = DateTimeFormat(dateTimeFomatId); dateTime.addDateTimeAutoStyles(styles, hasTodayDate, hasUserDate); } if (m_progress_update) { (m_filter->*m_setProgress)(70); } } //end createMainStyles() QByteArray PptToOdp::createContent(KoGenStyles& styles) { QBuffer presentationBuffer; presentationBuffer.open(QIODevice::WriteOnly); KoXmlWriter presentationWriter(&presentationBuffer); processDeclaration(&presentationWriter); Writer out(presentationWriter, styles); for (int c = 0; c < p->slides.size(); c++) { processSlideForBody(c, out); if (m_progress_update) { //consider progress interval (70, 100) qreal percentage = ((c + 1) / (float)p->slides.size()) * 100; int progress = 70 + (int)((percentage * 28) / 100); (m_filter->*m_setProgress)(progress); } } QByteArray contentData; QBuffer contentBuffer(&contentData); contentBuffer.open(QIODevice::WriteOnly); KoXmlWriter contentWriter(&contentBuffer); contentWriter.startDocument("office:document-content"); contentWriter.startElement("office:document-content"); contentWriter.addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"); contentWriter.addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0"); contentWriter.addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0"); contentWriter.addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0"); contentWriter.addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"); contentWriter.addAttribute("xmlns:presentation", "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"); contentWriter.addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"); contentWriter.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); contentWriter.addAttribute("office:version", "1.2"); // office:automatic-styles styles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, &contentWriter); // office:body contentWriter.startElement("office:body"); contentWriter.startElement("office:presentation"); contentWriter.addCompleteElement(&presentationBuffer); contentWriter.endElement(); // office:presentation contentWriter.endElement(); // office:body contentWriter.endElement(); // office:document-content contentWriter.endDocument(); return contentData; } QByteArray PptToOdp::createMeta() { QByteArray metaData; QBuffer buff(&metaData); buff.open(QIODevice::WriteOnly); KoXmlWriter metaWriter(&buff); metaWriter.startDocument("office:document-meta"); metaWriter.startElement("office:document-meta"); metaWriter.addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0"); metaWriter.addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"); metaWriter.addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"); metaWriter.addAttribute("office:version", "1.2"); metaWriter.startElement("office:meta"); const char *p_str = 0; const MSO::PropertySet &ps = p->summaryInfo.propertySet.propertySet1; for (uint i = 0; i < ps.numProperties; i++) { switch (ps.propertyIdentifierAndOffset.at(i).propertyIdentifier) { case PIDSI_TITLE: p_str = "dc:title"; break; case PIDSI_SUBJECT: p_str = "dc:subject"; break; case PIDSI_AUTHOR: p_str = "meta:initial-creator"; break; case PIDSI_KEYWORDS: p_str = "meta:keyword"; break; case PIDSI_COMMENTS: p_str = "dc:description"; break; case PIDSI_LASTAUTHOR: p_str = "dc:creator"; break; default: break; } if (p_str) { if (ps.property.at(i).vt_lpstr) { metaWriter.startElement(p_str); metaWriter.addTextNode(ps.property.at(i).vt_lpstr->characters); metaWriter.endElement(); } p_str = 0; } } metaWriter.endElement(); // office:meta metaWriter.endElement(); // office:document-meta return metaData; } QString PptToOdp::utf16ToString(const QVector &data) { return QString::fromUtf16(data.data(), data.size()); } QPair PptToOdp::findHyperlink(const quint32 id) { QString friendly; QString target; if( !p->documentContainer->exObjList ) return qMakePair(friendly, target); foreach(const ExObjListSubContainer &container, p->documentContainer->exObjList->rgChildRec) { // Search all ExHyperlinkContainers for specified id const ExHyperlinkContainer *hyperlink = container.anon.get(); if (hyperlink && hyperlink->exHyperlinkAtom.exHyperLinkId == id) { if (hyperlink->friendlyNameAtom) { friendly = utf16ToString(hyperlink->friendlyNameAtom->friendlyName); } if (hyperlink->targetAtom) { target = utf16ToString(hyperlink->targetAtom->target); } // TODO currently location is ignored. Location referes to // position within a file } } return qMakePair(friendly, target); } const TextCFRun *findTextCFRun(const StyleTextPropAtom& style, unsigned int pos) { quint32 counter = 0; foreach(const TextCFRun& cf, style.rgTextCFRun) { if (pos >= counter && pos < counter + cf.count) { return &cf; } counter += cf.count; } return 0; } const TextPFRun *findTextPFRun(const StyleTextPropAtom& style, unsigned int pos) { quint32 counter = 0; foreach(const TextPFRun& pf, style.rgTextPFRun) { if (pos >= counter && pos < counter + pf.count) { return &pf; } } return 0; } void writeMeta(const TextContainerMeta& m, bool master, KoXmlWriter& out) { const SlideNumberMCAtom* a = m.meta.get(); const DateTimeMCAtom* b = m.meta.get(); const GenericDateMCAtom* c = m.meta.get(); const HeaderMCAtom* d = m.meta.get(); const FooterMCAtom* e = m.meta.get(); const RTFDateTimeMCAtom* f = m.meta.get(); if (a) { out.startElement("text:page-number"); out.endElement(); } if (b) { // TODO: datetime format out.startElement("text:time"); out.endElement(); } if (c) { // TODO: datetime format if (master) { out.startElement("presentation:date-time"); } else { out.startElement("text:date"); } out.endElement(); } if (d) { out.startElement("presentation:header"); out.endElement(); } if (e) { out.startElement("presentation:footer"); out.endElement(); } if (f) { // TODO } } template int getMeta(const TextContainerMeta& m, const TextContainerMeta*& meta, const int start, int& end) { const T* a = m.meta.get(); if (a) { if (a->position == start) { meta = &m; } else if (a->position > start && end > a->position) { end = a->position; } } return end; } /** * @brief Write text deindentations the specified amount. Actually it just * closes elements. * * @param xmlWriter XML writer to write closing tags * @param count how many lists and list items to leave open * @param levels the list of levels to remove from */ void writeTextObjectDeIndent(KoXmlWriter& xmlWriter, const int count, QStack& levels) { while (levels.size() > count) { xmlWriter.endElement(); //text:list-item xmlWriter.endElement(); //text:list levels.pop(); } } void PptToOdp::addListElement(KoXmlWriter& out, const QString& listStyle, QStack& levels, quint16 level, const PptTextPFRun &pf) { levels.push(listStyle); out.startElement("text:list"); if (!listStyle.isEmpty()) { out.addAttribute("text:style-name", listStyle); } else { qDebug() << "Warning: list style name not provided!"; } if (pf.fBulletHasAutoNumber()) { QString xmlId = QString("lvl%1").arg(level); xmlId.append(QString("_%1").arg(qrand())); out.addAttribute("xml:id", xmlId); if (m_continueListNumbering.contains(level) && m_continueListNumbering[level]) { out.addAttribute("text:continue-list", m_lvlXmlIdMap[level]); } m_lvlXmlIdMap[level] = xmlId; } out.startElement("text:list-item"); if (pf.fBulletHasAutoNumber()) { if (m_continueListNumbering.contains(level) && (m_continueListNumbering[level] == false)) { out.addAttribute("text:start-value", pf.startNum()); } m_continueListNumbering[level] = true; } // add styleless levels to get the right level of indentation while (levels.size() < level) { out.startElement("text:list"); out.startElement("text:list-item"); levels.push(""); } } int PptToOdp::processTextSpan(Writer& out, PptTextCFRun& cf, const MSO::TextContainer* tc, const QString& text, const int start, int end, quint16* p_fs) { if (!tc) { qDebug() << "processTextSpan: TextContainer missing!"; return -1; } //num. of chars already formatted by this TextCFRun quint32 num = 0; const int count = cf.addCurrentCFRun(tc, start, num); *p_fs = cf.fontSize(); #ifdef DEBUG_PPTTOODP qDebug() << "(TextCFRun) num. of characters:" << count; qDebug() << "(TextCFRun) formatted characters:" << num; qDebug() << "(Text position) start:" << start << "| end:" << end; qDebug() << "font size:" << *p_fs; #endif bool isSymbol = false; // detect symbol inside one character text chunk if ( end == 1 || count == 1 ) { QChar c = text.at(start); if ( c.category() == QChar::Other_PrivateUse ) { isSymbol = true; } } // detect first symbol inside of several characters text chunk else { QString substr = text.mid(start, (end - start)); for (int i = 0; i < substr.length(); i++) { if ((substr.at(i)).category() == QChar::Other_PrivateUse) { if (i == 0) { end = start + 1; isSymbol = true; } else { end = start + i; } break; } } } // TODO: There's no TextCFRun in case of TextCFExceptionAtom or // TextMasterStyleLevel, handle this case. (uzak) // NOTE: TextSIException not processed by defineTextProperties at // the moment, so let's keep it simple! (uzak) const TextSIException* si = 0; #ifdef SI_EXCEPTION_SUPPORT int i = 0; // get the right special info run const QList* tsi = 0; if (tc->specialinfo) { tsi = &tc->specialinfo->rgSIRun; } if (tc->specialinfo2) { tsi = &tc->specialinfo2->rgSIRun; } int siend = 0; if (tsi) { while (i < tsi->size()) { si = &(*tsi)[i].si; siend += (*tsi)[i].count; if (siend > start) { break; } i++; } if (i >= tsi->size()) { si = 0; } } #endif // find a meta character const TextContainerMeta* meta = 0; for (int i = 0; i < tc->meta.size(); ++i) { const TextContainerMeta& m = tc->meta[i]; end = getMeta(m, meta, start, end); end = getMeta(m, meta, start, end); end = getMeta(m, meta, start, end); end = getMeta(m, meta, start, end); end = getMeta(m, meta, start, end); end = getMeta(m, meta, start, end); } //TODO: process bookmarks const TextBookmarkAtom* bookmark = 0; #ifdef BOOKMARK_SUPPORT // find the right bookmark for (int i = 0; i < tc->bookmark.size(); ++i) { if (tc->bookmark[i].begin < start && tc->bookmark[i].end >= start) { bookmark = &tc->bookmark[i]; } } #endif // find the interactive atom const MouseClickTextInfo* mouseclick = 0; const MouseOverTextInfo* mouseover = 0; for (int i = 0; i < tc->interactive.size(); ++i) { const TextContainerInteractiveInfo& ti = tc->interactive[i]; const MouseClickTextInfo *a = ti.interactive.get(); const MouseOverTextInfo *b = ti.interactive.get(); if (a && start >= a->text.range.begin && start < a->text.range.end) { mouseclick = a; } if (b && start >= b->text.range.begin && start < b->text.range.end) { mouseover = b; } } // determine the end of the range #ifdef SI_EXCEPTION_SUPPORT if (si && siend < end) { end = siend; } #endif if (meta) { end = start + 1; // meta is always one character } if (bookmark && bookmark->end < end) { end = bookmark->end; } if (mouseclick && mouseclick->text.range.end < end) { end = mouseclick->text.range.end; } if (mouseover && mouseover->text.range.end < end) { end = mouseover->text.range.end; } KoGenStyle style(KoGenStyle::TextAutoStyle, "text"); style.setAutoStyleInStylesDotXml(out.stylesxml); defineTextProperties(style, cf, 0, 0, si, isSymbol); out.xml.startElement("text:span", false); out.xml.addAttribute("text:style-name", out.styles.insert(style)); // [MS-PPT]: exHyperlinkIdRef must be ignored unless action is in // {II_JumpAction, II_HyperlinkAction, II_CustomShowAction (0x7)} // // NOTE: Jumps to other slides and shows not supported atm. if (mouseclick) { const InteractiveInfoAtom *info = &mouseclick->interactive.interactiveInfoAtom; if (info->action != II_HyperlinkAction) { mouseclick = 0; } } if (mouseover) { const InteractiveInfoAtom *info = &mouseover->interactive.interactiveInfoAtom; if (info->action != II_HyperlinkAction) { mouseover = 0; } } if (mouseclick) { out.xml.startElement("text:a", false); QPair link = findHyperlink( mouseclick->interactive.interactiveInfoAtom.exHyperlinkIdRef); if (!link.second.isEmpty()) { // target out.xml.addAttribute("xlink:href", link.second); out.xml.addAttribute("xlink:type", "simple"); } else if (!link.first.isEmpty()) { out.xml.addAttribute("xlink:href", link.first); out.xml.addAttribute("xlink:type", "simple"); } } else if (mouseover) { out.xml.startElement("text:a", false); QPair link = findHyperlink( mouseover->interactive.interactiveInfoAtom.exHyperlinkIdRef); if (!link.second.isEmpty()) { // target out.xml.addAttribute("xlink:href", link.second); out.xml.addAttribute("xlink:type", "simple"); } else if (!link.first.isEmpty()) { out.xml.addAttribute("xlink:href", link.first); out.xml.addAttribute("xlink:type", "simple"); } } else { // count - specifies the number of characters of the // corresponding text to which current TextCFException apply if (count > 0) { int tmp = start + (count - num); // moved to left by one character in processTextForBody if (tmp <= end) { end = tmp; } } } if (meta) { writeMeta(*meta, m_processingMasters, out.xml); } else { int len = end - start; const QString txt = text.mid(start, len).replace('\r', '\n').replace('\v', '\n'); out.xml.addTextSpan(txt); } if (mouseclick || mouseover) { out.xml.endElement(); //text:a } out.xml.endElement(); //text:span return end; } //end processTextSpan() int PptToOdp::processTextSpans(Writer& out, PptTextCFRun& cf, const MSO::TextContainer* tc, const QString& text, const int start, int end, quint16* p_fs) { quint16 font_size = 0; int pos = start; //using the do while statement to catch empty line do { int r = processTextSpan(out, cf, tc, text, pos, end, &font_size); if (font_size < *p_fs) { *p_fs = font_size; } if (r < pos) { // some error qDebug() << "pos: " << pos << "| end: " << end << " r: " << r; return -2; } pos = r; } while (pos < end); return (pos == end) ?0 :-pos; } QString PptToOdp::defineAutoListStyle(Writer& out, const PptTextPFRun& pf, const PptTextCFRun& cf) { KoGenStyle list(KoGenStyle::ListAutoStyle); list.setAutoStyleInStylesDotXml(out.stylesxml); ListStyleInput info(pf, cf); defineListStyle(list, pf.level(), info); return out.styles.insert(list); } void PptToOdp::processParagraph(Writer& out, QStack& levels, const MSO::OfficeArtClientData* clientData, const MSO::TextContainer* tc, const MSO::TextRuler* tr, const bool isPlaceHolder, const QString& text, const int start, int end) { //TODO: support for notes master slide required! const QString substr = text.mid(start, (end - start)); #ifdef DEBUG_PPTTOODP qDebug() << "> current paragraph:" << substr; qDebug() << "> (hex):" << hex << substr.toUcs4() << dec; #endif const PptOfficeArtClientData* pcd = 0; if (clientData) { pcd = clientData->anon.get(); } quint32 textType = tc->textHeaderAtom.textType; const MasterOrSlideContainer* m = 0; //Get the main master slide's MasterOrSlideContainer. A common shape //(opposite of a placeholder) SHOULD contain text of type Tx_TYPE_OTHER, //but MS Office 2003 does not follow this rule. if (m_currentMaster && (isPlaceHolder || (textType != Tx_TYPE_OTHER))) { m = m_currentMaster; while (m->anon.is()) { m = p->getMaster(m->anon.get()); } #ifdef DEBUG_PPTTOODP const MainMasterContainer* mc = m->anon.get(); Q_ASSERT(mc->slideAtom.masterIdRef == 0); #endif } //The current TextCFException located in the TextContainer will be //prepended to the list in the processTextSpan function. PptTextPFRun pf(p->documentContainer, m, m_currentSlideTexts, pcd, tc, tr, start); PptTextCFRun cf(p->documentContainer, m, tc, pf.level()); //spans have to be processed first to prepare the correct ParagraphStyle QBuffer spans_buf; spans_buf.open(QIODevice::WriteOnly); KoXmlWriter writer(&spans_buf); Writer o(writer, out.styles, out.stylesxml); quint16 min_fontsize = FONTSIZE_MAX; processTextSpans(o, cf, tc, text, start, end, &min_fontsize); //NOTE: Process empty list items as paragraphs to prevent kpresenter //displaying those. m_isList = ( pf.isList() && (start < end) ); if (m_isList) { int depth = pf.level() + 1; quint32 num = 0; //TextCFException of the 1st run of text required to specify //the label font-size in case not provided by TextPFException. cf.addCurrentCFRun(tc, start, num); m_firstChunkFontSize = cf.fontSize(); m_firstChunkFontRef = cf.fontRef(); cf.removeCurrentCFRun(); //A list label should NOT inherit a symbol font. if ((substr.at(0)).category() == QChar::Other_PrivateUse) { m_firstChunkSymbolAtStart = true; } else { m_firstChunkSymbolAtStart = false; } QString listStyle = defineAutoListStyle(out, pf, cf); //check if we have the corresponding style for this level, if not then //close the list and create a new one (K.I.S.S.) if (!levels.isEmpty() && (levels.first() != listStyle)) { writeTextObjectDeIndent(out.xml, 0, levels); } if (!pf.fBulletHasAutoNumber()) { QList levels = m_continueListNumbering.keys(); for (quint16 i = 0; i < levels.size(); i++) { if (levels[i] >= depth) { m_continueListNumbering.remove(levels[i]); m_lvlXmlIdMap.remove(levels[i]); } } } else if (m_previousListLevel > depth) { QList levels = m_continueListNumbering.keys(); for (quint16 i = 0; i < levels.size(); i++) { if (levels[i] > depth) { m_continueListNumbering.remove(levels[i]); m_lvlXmlIdMap.remove(levels[i]); } } } if (levels.isEmpty()) { addListElement(out.xml, listStyle, levels, depth, pf); } else { out.xml.endElement(); //text:list-item out.xml.startElement("text:list-item"); } m_previousListLevel = depth; } else { writeTextObjectDeIndent(out.xml, 0, levels); m_continueListNumbering.clear(); m_lvlXmlIdMap.clear(); m_previousListLevel = 0; } out.xml.startElement("text:p"); KoGenStyle style(KoGenStyle::ParagraphAutoStyle, "paragraph"); style.setAutoStyleInStylesDotXml(out.stylesxml); defineParagraphProperties(style, pf, min_fontsize); //NOTE: Help text layout to apply correct line-height for empty lines. if (start == end) { defineTextProperties(style, cf, 0, 0, 0); } out.xml.addAttribute("text:style-name", out.styles.insert(style)); out.xml.addCompleteElement(&spans_buf); out.xml.endElement(); //text:p } //end processParagraph() int PptToOdp::processTextForBody(Writer& out, const MSO::OfficeArtClientData* clientData, const MSO::TextContainer* tc, const MSO::TextRuler* tr, const bool isPlaceholder) { /* Text in a textcontainer is divided into sections. The sections occur on different levels: - paragraph (TextPFRun) 1-n characters - character (TextCFRun) 1-n characters - variables (TextContainerMeta) 1 character - spelling and language (TextSIRun) 1-n characters - links (TextContainerInteractiveInfo) 1-n characters - indentation (MasterTextPropRun) 1-n characters (ignored) Variables are the smallest level, they should be replaced by special xml elements. TextPFRuns correspond to text:list-item and text:p. MasterTextPropRun also corresponds to text:list-items too. TextCFRuns correspond to text:span elements as do */ // If this is not a placeholder shape, then do not inherit text style from // master styles. // // NOTE: If slideFlags/fMasterScheme == true, master's color scheme MUST be // used. Common shapes should not refer to a color scheme. if (!tc) { qDebug() << "MISSING TextContainer, big mess-up!"; return -1; } #ifdef DEBUG_PPTTOODP quint32 txt_type = tc->textHeaderAtom.textType; QString txt = getText(tc); int len = txt.length(); txt.replace('\v', ""); txt.replace('\r', ""); txt.replace('\n', ""); txt.replace('\t', ""); txt.replace('\f', ""); qDebug() << "\n> textType:" << txt_type; qDebug() << "> current text:" << txt << "| length:" << len; #endif // Let's assume text stored in paragraphs. // // Example:text1text2text3 // Result: // // text1 // text2 // text3 // // // // // Example:text1text2text3 // Result: // text1 // text2 // text3 // // In addition, the text body contains a single terminating paragraph break // character (0x000D) that is not included in the TextCharsAtom record or // TextBytesAtom record. // const QString text = getText(tc).append('\r'); static const QRegExp lineend("[\v\r]"); qint32 pos = 0, end = 0; QStack levels; levels.reserve(5); // loop over all the '\r' delimited lines while (pos < text.length()) { end = text.indexOf(lineend, pos); processParagraph(out, levels, clientData, tc, tr, isPlaceholder, text, pos, end); pos = end + 1; } // close all open text:list elements writeTextObjectDeIndent(out.xml, 0, levels); return 0; } //end processTextForBody() void PptToOdp::processSlideForBody(unsigned slideNo, Writer& out) { const SlideContainer* slide = p->slides[slideNo]; const MasterOrSlideContainer* master = p->getMaster(slide); if (!master) return; int masterNumber = p->masters.indexOf(master); if (masterNumber == -1) return; QString nameStr; // take the slide name if present (usually it is not) if (slide->slideNameAtom) { nameStr = QString::fromUtf16(slide->slideNameAtom->slideName.data(), slide->slideNameAtom->slideName.size()); } // look for a title on the slide if (nameStr.isEmpty()) { foreach(const TextContainer& tc, p->documentContainer->slideList->rgChildRec[slideNo].atoms) { if (tc.textHeaderAtom.textType == Tx_TYPE_TITLE) { nameStr = getText(&tc); break; } } } if (nameStr.isEmpty()) { nameStr = QString("page%1").arg(slideNo + 1); } nameStr.remove('\r'); nameStr.remove('\v'); out.xml.startElement("draw:page"); QString value = masterNames.value(master); if (!value.isEmpty()) { out.xml.addAttribute("draw:master-page-name", value); } out.xml.addAttribute("draw:name", nameStr); value = drawingPageStyles[slide]; if (!value.isEmpty()) { out.xml.addAttribute("draw:style-name", value); } //xmlWriter.addAttribute("presentation:presentation-page-layout-name", "AL1T0"); const HeadersFootersAtom* headerFooterAtom = 0; if (master->anon.is()) { const MainMasterContainer* m = master->anon.get(); if (m->perSlideHeadersFootersContainer) { headerFooterAtom = &m->perSlideHeadersFootersContainer->hfAtom; } } else { const SlideContainer* s = master->anon.get(); if (s->perSlideHFContainer) { headerFooterAtom = &s->perSlideHFContainer->hfAtom; } } if (!headerFooterAtom && getSlideHF()) { headerFooterAtom = &getSlideHF()->hfAtom; } if (!usedDateTimeDeclaration.value(slideNo).isEmpty()) { out.xml.addAttribute("presentation:use-date-time-name", usedDateTimeDeclaration[slideNo]); } if (!usedHeaderDeclaration.value(slideNo).isEmpty()) { if (!usedHeaderDeclaration[slideNo].isEmpty()) out.xml.addAttribute("presentation:use-header-name", usedHeaderDeclaration[slideNo]); } if (!usedFooterDeclaration.value(slideNo).isEmpty()) { if (!usedFooterDeclaration[slideNo].isEmpty()) out.xml.addAttribute("presentation:use-footer-name", usedFooterDeclaration[slideNo]); } m_currentSlideTexts = &p->documentContainer->slideList->rgChildRec[slideNo]; //TODO: try to avoid using those m_currentMaster = master; m_currentSlide = slide; DrawClient drawclient(this); ODrawToOdf odrawtoodf(drawclient); if (slide->drawing.OfficeArtDg.groupShape) { const OfficeArtSpgrContainer& spgr = *(slide->drawing.OfficeArtDg.groupShape).data(); drawclient.setDrawClientData(master, slide, 0, 0, m_currentSlideTexts); odrawtoodf.processGroupShape(spgr, out); } m_currentMaster = NULL; m_currentSlide = NULL; if (slide->drawing.OfficeArtDg.shape) { // leave it out until it is understood // processObjectForBody(*slide->drawing.OfficeArtDg.shape, out); } // draw the notes const NotesContainer* nc = p->notes[slideNo]; if (nc && nc->drawing.OfficeArtDg.groupShape) { m_currentSlideTexts = 0; out.xml.startElement("presentation:notes"); value = drawingPageStyles[nc]; if (!value.isEmpty()) { out.xml.addAttribute("draw:style-name", value); } const OfficeArtSpgrContainer& spgr = *(nc->drawing.OfficeArtDg.groupShape).data(); drawclient.setDrawClientData(0, 0, p->notesMaster, nc, m_currentSlideTexts); odrawtoodf.processGroupShape(spgr, out); out.xml.endElement(); } out.xml.endElement(); // draw:page } //end processSlideForBody() QString PptToOdp::processParaSpacing(const int value, const quint16 fs, const bool percentage) const { // ParaSpacing specifies text paragraph spacing. // // x = value; x in <0, 13200>, specifies spacing as a percentage of the // text line height. x < 0, the absolute value specifies spacing in master // units. if (value < 0) { unsigned int temp = -value; return pptMasterUnitToCm(temp); } // NOTE: MS PowerPoint specific: font-independent-line-spacing is used, // which means that line height is calculated only from the font height as // specified by the font size properties. If a number of font sizes are // used in a paragraph, then use the minimum. // // lineHeight = fontSize + (1/4 * fontSize); if (percentage) { return percent(value); } else { double height = fs + (0.25 * fs); return pt(qFloor(value * height / 100)); } } QString PptToOdp::pptMasterUnitToCm(qint16 value) const { qreal result = value; result *= 2.54; result /= 576; return cm(result); } QString PptToOdp::textAlignmentToString(unsigned int value) const { switch (value) { /** Tx_ALIGNLeft 0x0000 For horizontal text, left aligned. For vertical text, top aligned. */ case 0: return "left"; /** Tx_ALIGNCenter 0x0001 For horizontal text, centered. For vertical text, middle aligned. */ case 1: return "center"; /** Tx_ALIGNRight 0x0002 For horizontal text, right aligned. For vertical text, bottom aligned. */ case 2: return "right"; /** Tx_ALIGNJustify 0x0003 For horizontal text, flush left and right. For vertical text, flush top and bottom. */ case 3: return "justify"; //TODO these were missing from ODF specification v1.1, but are //in [MS-PPT].pdf /** Tx_ALIGNDistributed 0x0004 Distribute space between characters. */ case 4: /** Tx_ALIGNThaiDistributed 0x0005 Thai distribution justification. */ case 5: /** Tx_ALIGNJustifyLow 0x0006 Kashida justify low. */ case 6: return ""; //TODO these two are in ODF specification v1.1 but are missing from //[MS-PPT].pdf //return "end"; //return "start"; } return QString(); } QColor PptToOdp::toQColor(const ColorIndexStruct &color) { QColor ret; // MS-PPT 2.12.2 ColorIndexStruct if (color.index == 0xFE) { return QColor(color.red, color.green, color.blue); } if (color.index == 0xFF) { // color is undefined return ret; } const QList* colorScheme = NULL; const MSO::MasterOrSlideContainer* m = m_currentMaster; const MSO::MainMasterContainer* mmc = NULL; const MSO::SlideContainer* tmc = NULL; const MSO::SlideContainer* sc = m_currentSlide; //TODO: hande the case of a notes master slide/notes slide pair // const MSO::NotesContainer* nmc = NULL; // const MSO::NotesContainer* nc = NULL; // if (m) { // if (m->anon.is()) { // mmc = m->anon.get(); // colorScheme = &mmc->slideSchemeColorSchemeAtom.rgSchemeColor; // } else if (m->anon.is()) { // tmc = m->anon.get(); // colorScheme = &tmc->slideSchemeColorSchemeAtom.rgSchemeColor; // } // } //a title master slide does not provide any additional text formatting //information, use it's master's color scheme while (m) { //masterIdRef MUST be 0x00000000 if the record that contains this //SlideAtom record is a MainMasterContainer record (MS-PPT 2.5.10) if (m->anon.is()) { m = p->getMaster(m->anon.get()); } else { mmc = m->anon.get(); colorScheme = &mmc->slideSchemeColorSchemeAtom.rgSchemeColor; m = NULL; } } if (sc) { if (!sc->slideAtom.slideFlags.fMasterScheme) { colorScheme = &sc->slideSchemeColorSchemeAtom.rgSchemeColor; } } if (!colorScheme) { //NOTE: Using color scheme of the first main master/title master slide if (p->masters[0]->anon.is()) { mmc = p->masters[0]->anon.get(); colorScheme = &mmc->slideSchemeColorSchemeAtom.rgSchemeColor; } else if (p->masters[0]->anon.is()) { tmc = p->masters[0]->anon.get(); colorScheme = &tmc->slideSchemeColorSchemeAtom.rgSchemeColor; } if (!colorScheme) { qWarning() << "Warning: Ivalid color scheme! Returning an invalid color!"; return ret; } } if (colorScheme->size() <= color.index) { qWarning() << "Warning: Incorrect size of rgSchemeColor! Returning an invalid color!"; } else { const ColorStruct cs = colorScheme->at(color.index); ret = QColor(cs.red, cs.green, cs.blue); } return ret; } //end toQColor(const ColorIndexStruct) QColor PptToOdp::toQColor(const MSO::OfficeArtCOLORREF& c, const MSO::StreamOffset* master, const MSO::StreamOffset* common) { QColor ret; //fSchemeIndex - A bit that specifies whether the current application //defined color scheme will be used to determine the color (MS-ODRAW) if (c.fSchemeIndex) { const QList* colorScheme = NULL; const MSO::MainMasterContainer* mmc = NULL; const MSO::SlideContainer* tmc = NULL; const MSO::SlideContainer* sc = NULL; const MSO::NotesContainer* nmc = NULL; const MSO::NotesContainer* nc = NULL; // Get the color scheme of the current main master/title master or // notes master slide. if (master) { MSO::StreamOffset* m = const_cast(master); if ((mmc = dynamic_cast(m))) { colorScheme = &mmc->slideSchemeColorSchemeAtom.rgSchemeColor; } else if ((nmc = dynamic_cast(m))) { colorScheme = &nmc->slideSchemeColorSchemeAtom.rgSchemeColor; } else if ((tmc = dynamic_cast(m))) { colorScheme = &tmc->slideSchemeColorSchemeAtom.rgSchemeColor; } else { qWarning() << "Warning: Incorrect container!"; } } // Get the color scheme of the current presentation slide or notes // slide. If fMasterScheme == true use master's color scheme. if (common) { MSO::StreamOffset* c = const_cast(common); if ((sc = dynamic_cast(c))) { if (!sc->slideAtom.slideFlags.fMasterScheme) { colorScheme = &sc->slideSchemeColorSchemeAtom.rgSchemeColor; } } else if ((nc = dynamic_cast(c))) { if (!nc->notesAtom.slideFlags.fMasterScheme) { colorScheme = &nc->slideSchemeColorSchemeAtom.rgSchemeColor; } } else { qWarning() << "Warning: Incorrect container! Provide SlideContainer of NotesContainer."; } } if (!colorScheme) { //NOTE: Using color scheme of the first main master/title master slide if (p->masters[0]->anon.is()) { mmc = p->masters[0]->anon.get(); colorScheme = &mmc->slideSchemeColorSchemeAtom.rgSchemeColor; } else if (p->masters[0]->anon.is()) { tmc = p->masters[0]->anon.get(); colorScheme = &tmc->slideSchemeColorSchemeAtom.rgSchemeColor; } if (!colorScheme) { qWarning() << "Warning: Ivalid color scheme! Returning an invalid color!"; return ret; } } // Use the red color channel's value as index according to MS-ODRAW if (colorScheme->size() <= c.red) { qWarning() << "Warning: Incorrect size of rgSchemeColor! Returning an invalid color!"; return ret; } else { const ColorStruct cs = colorScheme->value(c.red); ret = QColor(cs.red, cs.green, cs.blue); } } else { ret = QColor(c.red, c.green, c.blue); } return ret; } //end toQColor() void PptToOdp::processTextAutoNumberScheme(int val, QString& numFormat, QString& numSuffix, QString& numPrefix) { switch (val) { //Example: a., b., c., ...Lowercase Latin character followed by a period. case ANM_AlphaLcPeriod: numFormat = 'a'; numSuffix = '.'; break; //Example: A., B., C., ...Uppercase Latin character followed by a period. case ANM_AlphaUcPeriod: numFormat = 'A'; numSuffix = '.'; break; //Example: 1), 2), 3), ...Arabic numeral followed by a closing parenthesis. case ANM_ArabicParenRight: numFormat = '1'; numSuffix = ')'; break; //Example: 1., 2., 3., ...Arabic numeral followed by a period. case ANM_ArabicPeriod: numFormat = '1'; numSuffix = '.'; break; //Example: (i), (ii), (iii), ...Lowercase Roman numeral enclosed in //parentheses. case ANM_RomanLcParenBoth: numPrefix = '('; numFormat = 'i'; numSuffix = ')'; break; //Example: i), ii), iii), ... Lowercase Roman numeral followed by a closing //parenthesis. case ANM_RomanLcParenRight: numFormat = 'i'; numSuffix = ')'; break; //Example: i., ii., iii., ...Lowercase Roman numeral followed by a period. case ANM_RomanLcPeriod: numFormat = 'i'; numSuffix = '.'; break; //Example: I., II., III., ...Uppercase Roman numeral followed by a period. case ANM_RomanUcPeriod: numFormat = 'I'; numSuffix = '.'; break; //Example: (a), (b), (c), ...Lowercase alphabetic character enclosed in //parentheses. case ANM_AlphaLcParenBoth: numPrefix = '('; numFormat = 'a'; numSuffix = ')'; break; //Example: a), b), c), ...Lowercase alphabetic character followed by a //closing case ANM_AlphaLcParenRight: numFormat = 'a'; numSuffix = ')'; break; //Example: (A), (B), (C), ...Uppercase alphabetic character enclosed in //parentheses. case ANM_AlphaUcParenBoth: numPrefix = '('; numFormat = 'A'; numSuffix = ')'; break; //Example: A), B), C), ...Uppercase alphabetic character followed by a //closing case ANM_AlphaUcParenRight: numFormat = 'A'; numSuffix = ')'; break; //Example: (1), (2), (3), ...Arabic numeral enclosed in parentheses. case ANM_ArabicParenBoth: numPrefix = '('; numFormat = '1'; numSuffix = ')'; break; //Example: 1, 2, 3, ...Arabic numeral. case ANM_ArabicPlain: numFormat = '1'; break; //Example: (I), (II), (III), ...Uppercase Roman numeral enclosed in //parentheses. case ANM_RomanUcParenBoth: numPrefix = '('; numFormat = 'I'; numSuffix = ')'; break; //Example: I), II), III), ...Uppercase Roman numeral followed by a closing //parenthesis. case ANM_RomanUcParenRight: numFormat = 'I'; numSuffix = ')'; break; default: numFormat = 'i'; numSuffix = '.'; break; } } //end processTextAutoNumberScheme() const TextContainer* PptToOdp::getTextContainer( const PptOfficeArtClientTextBox* clientTextbox, const PptOfficeArtClientData* clientData) const { if (clientData && clientData->placeholderAtom && m_currentSlideTexts) { const PlaceholderAtom* p = clientData->placeholderAtom.data(); if (p->position >= 0 && p->position < m_currentSlideTexts->atoms.size()) { return &m_currentSlideTexts->atoms[p->position]; } } if (clientTextbox) { // find the text type foreach (const TextClientDataSubContainerOrAtom& a, clientTextbox->rgChildRec) { const TextContainer* tc = a.anon.get(); if (tc) { return tc; } } } return 0; } quint32 PptToOdp::getTextType(const PptOfficeArtClientTextBox* clientTextbox, const PptOfficeArtClientData* clientData) const { const TextContainer* tc = getTextContainer(clientTextbox, clientData); if (tc) return tc->textHeaderAtom.textType; return 99; // 99 means it is undefined here } void PptToOdp::processDeclaration(KoXmlWriter* xmlWriter) { const HeadersFootersAtom* headerFooterAtom = 0; QSharedPointer userDateAtom; QSharedPointer footerAtom; HeaderAtom* headerAtom = 0; const MSO::SlideHeadersFootersContainer* slideHF = getSlideHF(); for (int slideNo = 0; slideNo < p->slides.size(); slideNo++) { const SlideContainer* slide = p->slides[slideNo]; if (slide->perSlideHFContainer) { userDateAtom = slide->perSlideHFContainer->userDateAtom; footerAtom = slide->perSlideHFContainer->footerAtom; headerFooterAtom = &slide->perSlideHFContainer->hfAtom; } else if (slideHF) { userDateAtom = slideHF->userDateAtom; footerAtom = slideHF->footerAtom; headerFooterAtom = &slideHF->hfAtom; } if (headerFooterAtom && headerFooterAtom->fHasHeader && headerAtom) { #if 0 - QString headerText = QString::fromAscii(headerAtom->header, headerAtom->header.size()); + QString headerText = QString::fromLatin1(headerAtom->header, headerAtom->header.size()); QString hdrName = findDeclaration(Header, headerText); if (hdrName == 0 ) { hdrName = QString("hdr%1").arg(declaration.values(Header).count() + 1); insertDeclaration(Header, hdrName, headerText); } usedHeaderDeclaration.insert(slideNo,hdrName); #endif } if (headerFooterAtom && headerFooterAtom->fHasFooter && footerAtom) { QString footerText = QString::fromUtf16(footerAtom->footer.data(), footerAtom->footer.size()); QString ftrName = findDeclaration(Footer, footerText); if ( ftrName == 0) { ftrName = QString("ftr%1").arg((declaration.values(Footer).count() + 1)); insertDeclaration(Footer, ftrName, footerText); } usedFooterDeclaration.insert(slideNo,ftrName); } if (headerFooterAtom && headerFooterAtom->fHasDate) { if(headerFooterAtom->fHasUserDate && userDateAtom) { QString userDate = QString::fromUtf16(userDateAtom->userDate.data(), userDateAtom->userDate.size()); QString dtdName = findDeclaration(DateTime, userDate); if ( dtdName == 0) { dtdName = QString("dtd%1").arg((declaration.values(DateTime).count() + 1)); insertDeclaration(DateTime, dtdName, userDate); } usedDateTimeDeclaration.insert(slideNo,dtdName); } if(headerFooterAtom->fHasTodayDate) { QString dtdName = findDeclaration(DateTime, ""); if ( dtdName == 0) { dtdName = QString("dtd%1").arg((declaration.values(DateTime).count() + 1)); insertDeclaration(DateTime, dtdName, ""); } usedDateTimeDeclaration.insert(slideNo,dtdName); } } } if (slideHF) { if (slideHF->hfAtom.fHasTodayDate) { QList >items = declaration.values(DateTime); for( int i = items.size()-1; i >= 0; --i) { QPair item = items.at(i); xmlWriter->startElement("presentation:date-time-decl"); xmlWriter->addAttribute("presentation:name", item.first); xmlWriter->addAttribute("presentation:source", "current-date"); //xmlWrite->addAttribute("style:data-style-name", "Dt1"); xmlWriter->endElement(); // presentation:date-time-decl } } else if (slideHF->hfAtom.fHasUserDate) { QList >items = declaration.values(DateTime); for( int i = 0; i < items.size(); ++i) { QPair item = items.at(i); xmlWriter->startElement("presentation:date-time-decl"); xmlWriter->addAttribute("presentation:name", item.first); xmlWriter->addAttribute("presentation:source", "fixed"); xmlWriter->addTextNode(item.second); //Future - Add Fixed date data here xmlWriter->endElement(); //presentation:date-time-decl } } if (headerAtom && slideHF->hfAtom.fHasHeader) { QList< QPair < QString, QString > > items = declaration.values(Header); for( int i = items.size()-1; i >= 0; --i) { QPair item = items.value(i); xmlWriter->startElement("presentation:header-decl"); xmlWriter->addAttribute("presentation:name", item.first); xmlWriter->addTextNode(item.second); xmlWriter->endElement(); //presentation:header-decl } } if (footerAtom && slideHF->hfAtom.fHasFooter) { QList< QPair < QString, QString > > items = declaration.values(Footer); for( int i = items.size()-1 ; i >= 0; --i) { QPair item = items.at(i); xmlWriter->startElement("presentation:footer-decl"); xmlWriter->addAttribute("presentation:name", item.first); xmlWriter->addTextNode(item.second); xmlWriter->endElement(); //presentation:footer-decl } } } } //end processDeclaration() QString PptToOdp::findDeclaration(DeclarationType type, const QString &text) const { QList< QPair< QString , QString > > items = declaration.values(type); for( int i = 0; i < items.size(); ++i) { QPairitem = items.at(i); if ( item.second == text ) { return item.first; } } return 0; } QString PptToOdp::findNotesDeclaration(DeclarationType type, const QString &text) const { QList >items = notesDeclaration.values(type); for( int i = 0; i < items.size(); ++i) { QPairitem = items.at(i); if ( item.second == text) { return item.first; } } return 0; } void PptToOdp::insertDeclaration(DeclarationType type, const QString &name, const QString &text) { QPairitem; item.first = name; item.second = text; declaration.insertMulti(type, item); } void PptToOdp::insertNotesDeclaration(DeclarationType type, const QString &name, const QString &text) { QPair item; item.first = name; item.second = text; notesDeclaration.insertMulti(type, item); } // @brief check if the provided groupShape contains the master shape // @param spid identifier of the master shape // @return pointer to the OfficeArtSpContainer const OfficeArtSpContainer* checkGroupShape(const OfficeArtSpgrContainer& o, quint32 spid) { if (o.rgfb.size() < 2) return NULL; const OfficeArtSpContainer* sp = 0; foreach(const OfficeArtSpgrContainerFileBlock& co, o.rgfb) { if (co.anon.is()) { sp = co.anon.get(); if (sp->shapeProp.spid == spid) { return sp; } } //TODO: the shape could be located deeper in the hierarchy } return NULL; } const OfficeArtSpContainer* PptToOdp::retrieveMasterShape(quint32 spid) const { const OfficeArtSpContainer* sp = 0; //check all main master slides foreach (const MSO::MasterOrSlideContainer* m, p->masters) { const SlideContainer* sc = m->anon.get(); const MainMasterContainer* mm = m->anon.get(); const DrawingContainer* drawing = 0; if (sc) { drawing = &sc->drawing; } else if (mm) { drawing = &mm->drawing; } if (drawing->OfficeArtDg.groupShape) { const OfficeArtSpgrContainer& spgr = *(drawing->OfficeArtDg.groupShape).data(); sp = checkGroupShape(spgr, spid); } if (sp) { return sp; } } //check all notes master slides if (p->notesMaster) { if (p->notesMaster->drawing.OfficeArtDg.groupShape) { const OfficeArtSpgrContainer& spgr = *(p->notesMaster->drawing.OfficeArtDg.groupShape).data(); sp = checkGroupShape(spgr, spid); } if (sp) { return sp; } } #ifdef CHECK_SLIDES //check all presentation slides for (int c = 0; c < p->slides.size(); c++) { const SlideContainer* slide = p->slides[c]; if (slide->drawing.OfficeArtDg.groupShape) { const OfficeArtSpgrContainer& spgr = *(slide->drawing.OfficeArtDg.groupShape).data(); sp = checkGroupShape(spgr, spid); } if (sp) { return sp; } } #endif #ifdef CHECK_NOTES //check all notes slides for (int c = 0; c < p->notes.size(); c++) { const NotesContainer* notes = p->notes[c]; if (notes->drawing.OfficeArtDg.groupShape) { const OfficeArtSpgrContainer& spgr = *(notes->drawing.OfficeArtDg.groupShape).data(); sp = checkGroupShape(spgr, spid); } if (sp) { return sp; } } #endif return NULL; } diff --git a/filters/words/mobi/PalmDocCompression.cpp b/filters/words/mobi/PalmDocCompression.cpp index 1c1c517e040..4697fc89ef0 100644 --- a/filters/words/mobi/PalmDocCompression.cpp +++ b/filters/words/mobi/PalmDocCompression.cpp @@ -1,256 +1,256 @@ /* This file is part of the KDE project Copyright (C) 2012 Mojtaba Shahi Senobari 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 "PalmDocCompression.h" #include "MobiFile.h" #include #include #include #include PalmDocCompression::PalmDocCompression(): m_winSize(2046), m_buffSize(10), m_maxBlockSize(4096) { } PalmDocCompression::~PalmDocCompression() { } void PalmDocCompression::compressContent(QByteArray input, QByteArray &output, QList &recordOffset) { /// PalmDOC uses LZ77 compression techniques. /** LZ77 algorithms achieve compression by replacing portions of the data with references to matching data that has already passed through both encoder and decoder. A match is encoded by a pair of numbers called a length-distance pair, which is equivalent to the statement "each of the next length characters is equal to the character exactly distance characters behind it in the uncompressed stream." (The "distance" is sometimes called the "offset" instead.) In the PalmDoc format, a length-distance pair is always encoded by a two-byte sequence. Of the 16 bits that make up these two bytes, 11 bits go to encoding the distance, 3 go to encoding the length, and the remaining two are used to make sure the decoder can identify the first byte as the beginning of such a two-byte sequence. */ /** To create the two bytes length-distance lets first talk about its structure, two bytes 10 00000000000 000 first two bits are used to make sure the decoder can identify the first byte as the beginning of such a two-byte sequence. the next 11 bits are for distance and other 3 bits are for length so i get my window size 4096 and buffer size 7 bits but for length There is some thing This line form the its wiki http://wiki.mobileread.com/wiki/PalmDOC "the 2 leftmost bits of this byte ('10') are discarded, and the following 6 bits are combined with the 8 bits of the next byte to make a 14 bit "distance, length" item. Those 14 bits are broken into 11 bits of distance backwards from the current location in the uncompressed text, and 3 bits of length to copy from that point (copying n+3 bytes, 3 to 10 bytes)." I got that for copress i should do length = length - 3. So i get a base 0X0001000000000000 then i add this with distance and then shift it 3 bits to left and add it to length */ /** 0xc0 to 0xff: "byte pair": this byte is decoded into 2 characters: a space character, and a letter formed from this byte XORed with 0x80. 0x01 to 0x08: "literals": the byte is interpreted as a count from 1 to 8, and that many literals are copied unmodified from the compressed stream to the decompressed stream. */ /// PalmDOC data is always divided into 4096 byte blocks and /// the blocks are acted upon independently. QBuffer *outBuf = new QBuffer(&output); outBuf->open(QBuffer::ReadWrite); QDataStream out(outBuf); startCompressing(input, out, recordOffset); outBuf->close(); delete outBuf; } void PalmDocCompression::startCompressing(QByteArray input, QDataStream &out, QList &recordOffset) { int winIndex = -1; int lookahead = 0; QByteArray temp; temp.clear(); while (input.length() != lookahead) { int start = winIndex - m_winSize + 1; if (start < 0) { start = 0; } int length = 0; int pos = 0; qint16 base = m_maxBlockSize; // check for out put size for reach to max block size if (((lookahead % m_maxBlockSize) == 0)) { input = input.right(input.size() - lookahead); winIndex = -1; lookahead = 0; start = 0; recordOffset << (qint32)out.device()->pos(); } // check space char if (input.at(lookahead) == ' ') { // check for next letter int index = lookahead; index++; if ((index % m_maxBlockSize) != 0) { if (QChar(input.at(index)).isLetter() - && ((QChar(input.at(index)).toAscii() >= (qint8)0X09) - && (QChar(input.at(index)).toAscii() <= (qint8)0X7f))) { + && ((QChar(input.at(index)).toLatin1() >= (qint8)0X09) + && (QChar(input.at(index)).toLatin1() <= (qint8)0X7f))) { winIndex += 2; lookahead += 2; out << (qint8)(input.at(index) ^ (qint8)0X80); continue; } } } // litterals ascii is between 0X09 - 0X7f - if (QChar(input.at(lookahead)).toAscii() < (qint8)0X09 || - QChar(input.at(lookahead)).toAscii() > (qint8)0X7f ) { + if (QChar(input.at(lookahead)).toLatin1() < (qint8)0X09 || + QChar(input.at(lookahead)).toLatin1() > (qint8)0X7f ) { // Check the length of unknown characters. int len = 1; int index = lookahead + 1; while (1) { - if (QChar(input.at(index)).toAscii() < (qint8)0X09 || - QChar(input.at(index)).toAscii() > (qint8)0X7f ) { + if (QChar(input.at(index)).toLatin1() < (qint8)0X09 || + QChar(input.at(index)).toLatin1() > (qint8)0X7f ) { if ((index % m_maxBlockSize) == 0) break; index++; len++; } else { break; } } //int remain = m_maxBlockSize - (lookahead % m_maxBlockSize); // if (len == 1) { // out << (qint8)0X20; // continue; // } // if (remain > len) { out << (qint8)len; for (int i = 0; i < len; i++) { out << (qint8)input.at(lookahead); lookahead++; } winIndex += len; continue; // } // else { // int temp = remain - 1; // out << (qint8)temp; // for (int i = 0; i < temp; i++) { // out << (qint8)input.at(lookahead); // lookahead++; // } // // We are in next block. // input = input.right(input.size() - lookahead); // winIndex = -1; // lookahead = 0; // start = 0; // recordOffset << (int)out.device()->pos(); // temp = len - temp; // if (temp != 0) // out << (qint8)temp; // for (int i = 0; i < temp; i++) { // out << (qint8)input.at(lookahead); // lookahead++; // } // winIndex += temp; // continue; // } } for (int i = start; i <= winIndex; i++) { if (input.at(i) == input.at(lookahead)) { // if ((lookahead + 1) % m_maxBlockSize == 0) { // break; //} // save the match char position. pos = i; length++; lookahead++; // Go to find more matches. for (int j = i + 1, k = 1; (j <= winIndex) && (k < m_buffSize); j++, k++) { if (input.at(j) != input.at(lookahead)) { break; } if ((lookahead % m_maxBlockSize) == 0) { break; } length++; lookahead++; } break; } } if (length == 0) { out << (qint8) input.at(lookahead); winIndex++; lookahead++; } else { // I dont encode the match over 3 bits if (length == 1 ) { out << (qint8) input.at((winIndex + 1)); } else if (length == 2) { out << (qint8) input.at((winIndex + 1)); out << (qint8) input.at((winIndex + 2)); } else { qint16 distance = (qint16)(winIndex - pos + 1); base = distance + base; base = base << 3; qint16 offset_length = base + (qint16)((length - 3)); out << offset_length; } winIndex += length; } } } diff --git a/filters/words/rtf/import/rtf-qt/src/PictDestination.cpp b/filters/words/rtf/import/rtf-qt/src/PictDestination.cpp index 16c62e67095..291f6413c58 100644 --- a/filters/words/rtf/import/rtf-qt/src/PictDestination.cpp +++ b/filters/words/rtf/import/rtf-qt/src/PictDestination.cpp @@ -1,76 +1,76 @@ /* Copyright (C) 2010 Brad Hards This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "PictDestination.h" #include "rtfreader.h" namespace RtfReader { PictDestination::PictDestination( Reader *reader, AbstractRtfOutput *output, const QString &name ) : Destination( reader, output, name ) { } PictDestination::~PictDestination() { } void PictDestination::handleControlWord( const QString &controlWord, bool hasValue, const int value ) { if ( controlWord == "jpegblip" ) { // handle this later } else if ( controlWord == "wmetafile" ) { qDebug() << "todo: get WMF data"; } else if ( controlWord == "picw" ) { qDebug() << "pict width: " << value; m_imageFormat.setWidth( value ); } else if ( controlWord == "pich" ) { qDebug() << "pict height: " << value; m_imageFormat.setHeight( value ); } else if ( controlWord == "picscalex" ) { qDebug() << "X scale: " << value; } else if ( controlWord == "picscaley" ) { qDebug() << "Y scale: " << value; } else if ( controlWord == "piccropl" ) { qDebug() << "Left crop:" << value; } else if ( controlWord == "piccropr" ) { qDebug() << "Right crop:" << value; } else if ( controlWord == "piccropt" ) { qDebug() << "Top crop:" << value; } else if ( controlWord == "piccropb" ) { qDebug() << "Bottom crop:" << value; } else if ( controlWord == "pichgoal" ) { qDebug() << "Goal Height:" << value; } else if ( controlWord == "picwgoal" ) { qDebug() << "Goal Width:" << value; } else { qDebug() << "unexpected control word in pict:" << controlWord; } } void PictDestination::handlePlainText( const QString &plainText ) { - m_pictHexData += plainText.toAscii(); + m_pictHexData += plainText.toLatin1(); } void PictDestination::aboutToEndDestination() { QImage image = QImage::fromData( QByteArray::fromHex( m_pictHexData ) ); m_output->createImage(image, m_imageFormat); } } diff --git a/kexi/formeditor/commands.cpp b/kexi/formeditor/commands.cpp index 02ae04d9d4f..4a90ab52d90 100644 --- a/kexi/formeditor/commands.cpp +++ b/kexi/formeditor/commands.cpp @@ -1,2332 +1,2332 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2005-2010 JarosÅ‚aw Staniek 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "WidgetInfo.h" #include "formIO.h" #include "container.h" #include "objecttree.h" //unused #include "formmanager.h" #include "form.h" #include "widgetlibrary.h" #include "events.h" #include "utils.h" //removed 2.0 #include "widgetpropertyset.h" #include "widgetwithsubpropertiesinterface.h" #include #include #include #include "commands.h" #include #include using namespace KFormDesigner; // Command Command::Command(Command *parent) : KUndo2Command(parent) , m_blockRedoOnce(false) { } Command::Command(const QString &text, Command *parent) : KUndo2Command(parent) , m_blockRedoOnce(false) { Q_UNUSED(text); } Command::~Command() { } void Command::blockRedoOnce() { m_blockRedoOnce = true; } void Command::redo() { if (m_blockRedoOnce) { m_blockRedoOnce = false; return; } execute(); } //! kDebug() stream operator. Writes command @a c to the debug output in a nicely formatted way. KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const Command &c) { dbg.nospace() << "Command"; const int count = c.childCount(); dbg.nospace() << "name=" << c.text() << "#=" << count; for (int i = 0; i < count; i++) { dbg.nospace() << "- subcommand" << i+1 << ":" << *static_cast(c.child(i)) << "\n"; } return dbg.space(); } // PropertyCommand namespace KFormDesigner { class PropertyCommand::Private { public: Private() : uniqueId(0) { } Form *form; QVariant value; QHash oldValues; //!< (widget_name -> value) hash QByteArray propertyName; int uniqueId; }; } PropertyCommand::PropertyCommand(Form& form, const QByteArray &wname, const QVariant &oldValue, const QVariant &value, const QByteArray &propertyName, Command *parent) : Command(parent), d( new Private ) { d->form = &form; d->value = value; d->propertyName = propertyName; d->oldValues.insert(wname, oldValue); init(); } PropertyCommand::PropertyCommand(Form& form, const QHash &oldValues, const QVariant &value, const QByteArray &propertyName, Command *parent) : Command(parent), d( new Private ) { d->form = &form; d->value = value; d->propertyName = propertyName; d->oldValues = oldValues; init(); } PropertyCommand::~PropertyCommand() { delete d; } void PropertyCommand::init() { if (d->oldValues.count() > 1) { setText( i18nc("(qtundo-format)", "Change \"%1\" property for multiple widgets", QString(d->propertyName)) ); } else { setText( i18nc("(qtundo-format)", "Change \"%1\" property for widget \"%2\"", QString(d->propertyName), QString(d->oldValues.constBegin().key())) ); } } Form* PropertyCommand::form() const { return d->form; } QVariant PropertyCommand::value() const { return d->value; } void PropertyCommand::setValue(const QVariant &value) { d->value = value; //moved to Form::slotPropertyChanged(): emit d->form->modified(); } void PropertyCommand::setUniqueId(int id) { d->uniqueId = id; } void PropertyCommand::execute() { QWidget *selected = d->form->selectedWidget(); bool reSelectWidgets = true; if (selected && d->oldValues.count() == 1 && d->oldValues.contains(selected->objectName().toLatin1()) ) { // do not reselect widget; this e.g. avoids removing resize handles reSelectWidgets = false; } if (reSelectWidgets) { d->form->selectFormWidget(); } d->form->setUndoing(true); if (reSelectWidgets) { d->form->selectWidgets(d->oldValues.keys(), Form::ReplacePreviousSelection); /* foreach (const QByteArray& name, d->oldValues.keys()) { ObjectTreeItem* item = d->form->objectTree()->lookup(name); if (item) { //we're checking for item!=0 because the name could be of a form widget d->form->selectWidget(item->widget(), Form::AddToPreviousSelection | Form::LastSelection); } }*/ } // if (d->oldValues.count() > 1) { // set property for multiple widgets for (QHash::ConstIterator oldValuesIt( d->oldValues.constBegin() ); oldValuesIt != d->oldValues.constEnd(); ++oldValuesIt) { ObjectTreeItem* item = d->form->objectTree()->lookup(oldValuesIt.key()); if (item) { //we're checking for item!=0 because the name could be of a form widget QWidget *widget = item->widget(); WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(widget); QWidget *subWidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget; if (subWidget && -1 != subWidget->metaObject()->indexOfProperty(d->propertyName)) item->widget()->setProperty(d->propertyName, d->value); } } // } d->form->propertySet().changeProperty(d->propertyName, d->value); d->form->setUndoing(false); } void PropertyCommand::undo() { d->form->selectFormWidget(); d->form->setUndoing(true); QHash::ConstIterator endIt = d->oldValues.constEnd(); for (QHash::ConstIterator it = d->oldValues.constBegin(); it != endIt; ++it) { ObjectTreeItem* item = d->form->objectTree()->lookup(it.key()); if (!item) continue; //better this than a crash QWidget *widget = item->widget(); d->form->selectWidget(widget, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(widget); QWidget *subWidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget; if (subWidget && -1 != subWidget->metaObject()->indexOfProperty(d->propertyName)) subWidget->setProperty(d->propertyName, it.value()); } d->form->propertySet().changeProperty(d->propertyName, d->oldValues.constBegin().value()); d->form->setUndoing(false); } bool PropertyCommand::mergeWith(const KUndo2Command * command) { if (id() != command->id()) return false; const PropertyCommand* propertyCommand = static_cast(command); if (d->uniqueId > 0 && propertyCommand->d->uniqueId == d->uniqueId) { if (d->oldValues.count() == propertyCommand->d->oldValues.count()) { d->value = propertyCommand->value(); return true; } } return false; } QByteArray PropertyCommand::propertyName() const { return d->propertyName; } const QHash& PropertyCommand::oldValues() const { return d->oldValues; } QByteArray PropertyCommand::widgetName() const { if (d->oldValues.count() != 1) return QByteArray(); return d->oldValues.keys().first(); } QVariant PropertyCommand::oldValue() const { if (d->oldValues.count() != 1) return QVariant(); return d->oldValues.constBegin().value(); } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PropertyCommand &c) { dbg.nospace() << "PropertyCommand text=" << c.text() << "widgets=" << c.oldValues().keys() << "value=" << c.value() << "oldValues=" << c.oldValues().values(); return dbg.space(); } // GeometryPropertyCommand (for multiple widgets) namespace KFormDesigner { class GeometryPropertyCommand::Private { public: Private() { } Form *form; QStringList names; QPoint pos; QPoint oldPos; }; } GeometryPropertyCommand::GeometryPropertyCommand(Form& form, const QStringList &names, const QPoint& oldPos, Command *parent) : Command(parent), d( new Private ) { d->form = &form; d->names = names; d->oldPos = oldPos; setText( i18nc("(qtundo-format)", "Move multiple widgets") ); } GeometryPropertyCommand::~GeometryPropertyCommand() { delete d; } void GeometryPropertyCommand::execute() { d->form->setUndoing(true); int dx = d->pos.x() - d->oldPos.x(); int dy = d->pos.y() - d->oldPos.y(); // We move every widget in our list by (dx, dy) foreach (const QString& widgetName, d->names) { ObjectTreeItem* item = d->form->objectTree()->lookup(widgetName); if (!item) continue; //better this than a crash QWidget *w = item->widget(); w->move(w->x() + dx, w->y() + dy); } d->form->setUndoing(false); } void GeometryPropertyCommand::undo() { d->form->setUndoing(true); int dx = d->pos.x() - d->oldPos.x(); int dy = d->pos.y() - d->oldPos.y(); // We move every widget in our list by (-dx, -dy) to undo the move foreach (const QString& widgetName, d->names) { ObjectTreeItem* item = d->form->objectTree()->lookup(widgetName); if (!item) continue; //better this than a crash QWidget *w = item->widget(); w->move(w->x() - dx, w->y() - dy); } d->form->setUndoing(false); } QPoint GeometryPropertyCommand::pos() const { return d->pos; } void GeometryPropertyCommand::setPos(const QPoint& pos) { d->pos = pos; // moved emit d->form->modified(); } QPoint GeometryPropertyCommand::oldPos() const { return d->oldPos; } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const GeometryPropertyCommand &c) { dbg.nospace() << "GeometryPropertyCommand pos=" << c.pos() << "oldPos=" << c.oldPos() << "widgets=" << c.d->names; return dbg.space(); } ///////////////// AlignWidgetsCommand //////// namespace KFormDesigner { class AlignWidgetsCommand::Private { public: Private() { } Form *form; Form::WidgetAlignment alignment; QHash pos; }; } AlignWidgetsCommand::AlignWidgetsCommand(Form &form, Form::WidgetAlignment alignment, const QWidgetList &list, Command *parent) : Command(parent), d( new Private ) { d->form = &form; d->alignment = alignment; foreach (QWidget *w, list) { d->pos.insert(w->objectName().toLatin1().constData(), w->pos()); } switch (d->alignment) { case Form::AlignToGrid: setText( i18nc("(qtundo-format)", "Align Widgets to Grid") ); break; case Form::AlignToLeft: setText( i18nc("(qtundo-format)", "Align Widgets to Left") ); break; case Form::AlignToRight: setText( i18nc("(qtundo-format)", "Align Widgets to Right") ); break; case Form::AlignToTop: setText( i18nc("(qtundo-format)", "Align Widgets to Top") ); break; case Form::AlignToBottom: setText( i18nc("(qtundo-format)", "Align Widgets to Bottom") ); break; default:; } } AlignWidgetsCommand::~AlignWidgetsCommand() { delete d; } void AlignWidgetsCommand::execute() { // To avoid creation of GeometryPropertyCommand d->form->selectFormWidget(); QWidgetList list; foreach (const QByteArray& name, d->pos.keys()) { ObjectTreeItem *item = d->form->objectTree()->lookup(name); if (item && item->widget()) list.append(item->widget()); } const int gridX = d->form->gridSize(); const int gridY = d->form->gridSize(); QWidget *parentWidget = d->form->selectedWidgets()->first()->parentWidget(); switch (d->alignment) { case Form::AlignToGrid: { foreach (QWidget *w, list) { const int tmpx = alignValueToGrid(w->x(), gridX); const int tmpy = alignValueToGrid(w->y(), gridY); if ((tmpx != w->x()) || (tmpy != w->y())) w->move(tmpx, tmpy); } break; } case Form::AlignToLeft: { int tmpx = parentWidget->width(); foreach (QWidget *w, list) { if (w->x() < tmpx) tmpx = w->x(); } foreach (QWidget *w, list) { w->move(tmpx, w->y()); } break; } case Form::AlignToRight: { int tmpx = 0; foreach (QWidget *w, list) { if (w->x() + w->width() > tmpx) tmpx = w->x() + w->width(); } foreach (QWidget *w, list) { w->move(tmpx - w->width(), w->y()); } break; } case Form::AlignToTop: { int tmpy = parentWidget->height(); foreach (QWidget *w, list) { if (w->y() < tmpy) tmpy = w->y(); } foreach (QWidget *w, list) { w->move(w->x(), tmpy); } break; } case Form::AlignToBottom: { int tmpy = 0; foreach (QWidget *w, list) { if (w->y() + w->height() > tmpy) tmpy = w->y() + w->height(); } foreach (QWidget *w, list) { w->move(w->x(), tmpy - w->height()); } break; } default: return; } // We restore selection foreach (QWidget *w, list) { d->form->selectWidget(w, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); } } void AlignWidgetsCommand::undo() { // To avoid creation of GeometryPropertyCommand d->form->selectFormWidget(); // We move widgets to their original pos QHash::ConstIterator endIt = d->pos.constEnd(); for (QHash::ConstIterator it = d->pos.constBegin(); it != endIt; ++it) { ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); if (item && item->widget()) { item->widget()->move(d->pos.value(item->widget()->objectName().toLatin1().constData())); // we restore selection d->form->selectWidget(item->widget(), Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); } } } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const AlignWidgetsCommand &c) { dbg.nospace() << "AlignWidgetsCommand text=" << c.text() << "form=" << c.d->form->widget()->objectName() << "widgets=" << c.d->pos.keys(); return dbg.space(); } ///// AdjustSizeCommand /////////// namespace KFormDesigner { class AdjustSizeCommand::Private { public: Private() { } Form *form; AdjustSizeCommand::Adjustment type; QHash pos; QHash sizes; }; } AdjustSizeCommand::AdjustSizeCommand(Form& form, Adjustment type, const QWidgetList &list, Command *parent) : Command(parent), d( new Private ) { d->form = &form; d->type = type; foreach (QWidget *w, list) { if (w->parentWidget() && KexiUtils::objectIsA(w->parentWidget(), "QStackedWidget")) { w = w->parentWidget(); // widget is WidgetStack page if (w->parentWidget() && w->parentWidget()->inherits("QTabWidget")) // widget is tabwidget page w = w->parentWidget(); } d->sizes.insert(w->objectName().toLatin1().constData(), w->size()); if (d->type == SizeToGrid) // SizeToGrid also move widgets d->pos.insert(w->objectName().toLatin1().constData(), w->pos()); } switch (d->type) { case SizeToGrid: setText( i18nc("(qtundo-format)", "Resize Widgets to Grid") ); break; case SizeToFit: setText( i18nc("(qtundo-format)", "Resize Widgets to Fit Contents") ); break; case SizeToSmallWidth: setText( i18nc("(qtundo-format)", "Resize Widgets to Narrowest") ); break; case SizeToBigWidth: setText( i18nc("(qtundo-format)", "Resize Widgets to Widest") ); break; case SizeToSmallHeight: setText( i18nc("(qtundo-format)", "Resize Widgets to Shortest") ); break; case SizeToBigHeight: setText( i18nc("(qtundo-format)", "Resize Widgets to Tallest") ); break; default:; } } AdjustSizeCommand::~AdjustSizeCommand() { delete d; } void AdjustSizeCommand::execute() { // To avoid creation of GeometryPropertyCommand d->form->selectFormWidget(); int gridX = d->form->gridSize(); int gridY = d->form->gridSize(); int tmpw = 0, tmph = 0; QWidgetList list; QHash::ConstIterator endIt = d->sizes.constEnd(); for (QHash::ConstIterator it = d->sizes.constBegin(); it != endIt; ++it) { ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); if (item && item->widget()) list.append(item->widget()); } switch (d->type) { case SizeToGrid: { int tmpx = 0, tmpy = 0; // same as in 'Align to Grid' + for the size foreach (QWidget *w, list) { tmpx = alignValueToGrid(w->x(), gridX); tmpy = alignValueToGrid(w->y(), gridY); tmpw = alignValueToGrid(w->width(), gridX); tmph = alignValueToGrid(w->height(), gridY); if ((tmpx != w->x()) || (tmpy != w->y())) w->move(tmpx, tmpy); if ((tmpw != w->width()) || (tmph != w->height())) w->resize(tmpw, tmph); } break; } case SizeToFit: { foreach (QWidget *w, list) { ObjectTreeItem *item = d->form->objectTree()->lookup(w->objectName()); if (item && !item->children()->isEmpty()) { // container QSize s; if (item->container() && item->container()->layout()) s = w->sizeHint(); else s = getSizeFromChildren(item); // minimum size for containers if (s.width() < 30) s.setWidth(30); if (s.height() < 30) s.setHeight(30); // small hack for flow layouts int type = item->container() ? item->container()->layoutType() : Form::NoLayout; if (type == Form::HFlow) s.setWidth(s.width() + 5); else if (type == Form::VFlow) s.setHeight(s.height() + 5); w->resize(s); } else if (item && item->container()) // empty container w->resize(item->container()->form()->gridSize() * 5, item->container()->form()->gridSize() * 5); // basic size else { QSize sizeHint(w->sizeHint()); if (sizeHint.isValid()) w->resize(sizeHint); } } break; } case SizeToSmallWidth: { foreach (QWidget *w, list) { if ((tmpw == 0) || (w->width() < tmpw)) tmpw = w->width(); } foreach (QWidget *w, list) { if (tmpw != w->width()) w->resize(tmpw, w->height()); } break; } case SizeToBigWidth: { foreach (QWidget *w, list) { if (w->width() > tmpw) tmpw = w->width(); } foreach (QWidget *w, list) { if (tmpw != w->width()) w->resize(tmpw, w->height()); } break; } case SizeToSmallHeight: { foreach (QWidget *w, list) { if ((tmph == 0) || (w->height() < tmph)) tmph = w->height(); } foreach (QWidget *w, list) { if (tmph != w->height()) w->resize(w->width(), tmph); } break; } case SizeToBigHeight: { foreach (QWidget *w, list) { if (w->height() > tmph) tmph = w->height(); } foreach (QWidget *w, list) { if (tmph != w->height()) w->resize(w->width(), tmph); } break; } default: break; } // We restore selection foreach (QWidget *w, list) { d->form->selectWidget(w, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); } } QSize AdjustSizeCommand::getSizeFromChildren(ObjectTreeItem *item) { if (!item->container()) { // multi pages containers (eg tabwidget) QSize s; // get size for each container, and keep the biggest one foreach (ObjectTreeItem *titem, *item->children()) { s = s.expandedTo(getSizeFromChildren(titem)); } return s; } int tmpw = 0, tmph = 0; foreach (ObjectTreeItem *titem, *item->children()) { if (!titem->widget()) continue; tmpw = qMax(tmpw, titem->widget()->geometry().right()); tmph = qMax(tmph, titem->widget()->geometry().bottom()); } return QSize(tmpw, tmph) + QSize(10, 10); } void AdjustSizeCommand::undo() { // To avoid creation of GeometryPropertyCommand d->form->selectFormWidget(); // We resize widgets to their original size QHash::ConstIterator endIt = d->sizes.constEnd(); for (QHash::ConstIterator it = d->sizes.constBegin(); it != endIt; ++it) { ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); if (item && item->widget()) { item->widget()->resize(d->sizes[item->widget()->objectName().toLatin1().constData()]); if (d->type == SizeToGrid) item->widget()->move(d->pos[item->widget()->objectName().toLatin1().constData()]); d->form->selectWidget(item->widget(), Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); // restore selection } } } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const AdjustSizeCommand &c) { dbg.nospace() << "AdjustSizeCommand text=" << c.text() << "form=" << c.d->form->widget()->objectName() << "widgets=" << c.d->sizes.keys(); return dbg.space(); } // LayoutPropertyCommand namespace KFormDesigner { class LayoutPropertyCommand::Private { public: Private() { } Form *form; QHash geometries; }; } LayoutPropertyCommand::LayoutPropertyCommand(Form& form, const QByteArray &wname, const QVariant &oldValue, const QVariant &value, Command *parent) : PropertyCommand(form, wname, oldValue, value, "layout", parent) , d( new Private ) { ObjectTreeItem* titem = d->form->objectTree()->lookup(wname); if (!titem) return; //better this than a crash Container *container = titem->container(); // We save the geometry of each wigdet foreach (ObjectTreeItem *titem, *container->objectTree()->children()) { d->geometries.insert(titem->name().toLatin1(), titem->widget()->geometry()); } setText( i18nc("(qtundo-format)", "Change layout of widget \"%1\"", QString(oldValues().constBegin().key())) ); } LayoutPropertyCommand::~LayoutPropertyCommand() { delete d; } void LayoutPropertyCommand::execute() { PropertyCommand::execute(); } void LayoutPropertyCommand::undo() { ObjectTreeItem* titem = d->form->objectTree()->lookup(oldValues().constBegin().key()); if (!titem) return; //better this than a crash Container *container = titem->container(); container->setLayoutType(Form::NoLayout); // We put every widget back in its old location QHash::ConstIterator endIt = d->geometries.constEnd(); for (QHash::ConstIterator it = d->geometries.constBegin(); it != endIt; ++it) { ObjectTreeItem *tree = container->form()->objectTree()->lookup(it.key()); if (tree) tree->widget()->setGeometry(it.value()); } PropertyCommand::undo(); } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const LayoutPropertyCommand &c) { dbg.nospace() << "LayoutPropertyCommand text=" << c.text() << "oldValues=" << c.oldValues().keys() << "value=" << c.value(); return dbg.space(); } // InsertWidgetCommand namespace KFormDesigner { class InsertWidgetCommand::Private { public: Private() { } Form *form; QString containerName; QPoint pos; QByteArray widgetName; QByteArray _class; QRect insertRect; }; } InsertWidgetCommand::InsertWidgetCommand(const Container& container, Command *parent) : Command(parent), d( new Private ) { d->form = container.form(); d->containerName = container.widget()->objectName(); kDebug() << "containerName:" << d->containerName; d->_class = d->form->selectedClass(); d->pos = container.selectionOrInsertingBegin(); d->widgetName = d->form->objectTree()->generateUniqueName( d->form->library()->namePrefix(d->_class).toLatin1(), /* !numberSuffixRequired */false); kDebug() << "widgetName:" << d->widgetName; d->insertRect = container.selectionOrInsertingRectangle(); init(); } InsertWidgetCommand::InsertWidgetCommand(const Container& container, const QByteArray& className, const QPoint& pos, const QByteArray& namePrefix, Command *parent) : Command(parent), d( new Private ) { d->form = container.form(); d->containerName = container.widget()->objectName(); kDebug() << "containerName:" << d->containerName; d->_class = className; d->pos = pos; //d->insertRect is null (default) kDebug() << "namePrefix:" << namePrefix; if (namePrefix.isEmpty()) { d->widgetName = d->form->objectTree()->generateUniqueName( d->form->library()->namePrefix(d->_class).toLatin1()); } else { d->widgetName = d->form->objectTree()->generateUniqueName( namePrefix, false /* !numberSuffixRequired */); } kDebug() << "widgetName:" << d->widgetName; init(); } InsertWidgetCommand::~InsertWidgetCommand() { delete d; } void InsertWidgetCommand::init() { if (!d->widgetName.isEmpty()) { setText( i18nc("(qtundo-format)", "Insert widget \"%1\"", QString(d->widgetName)) ); } else { setText( i18nc("(qtundo-format)", "Insert widget") ); } } void InsertWidgetCommand::execute() { if (!d->form->objectTree()) return; ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName); if (!titem) return; //better this than a crash Container *container = titem->container(); WidgetFactory::CreateWidgetOptions options = WidgetFactory::DesignViewMode | WidgetFactory::AnyOrientation; if (d->form->library()->internalProperty(d->_class, "orientationSelectionPopup").toBool()) { if (d->insertRect.isValid()) { if (d->insertRect.width() < d->insertRect.height()) { options |= WidgetFactory::VerticalOrientation; options ^= WidgetFactory::AnyOrientation; } else if (d->insertRect.width() > d->insertRect.height()) { options |= WidgetFactory::HorizontalOrientation; options ^= WidgetFactory::AnyOrientation; } } if (options & WidgetFactory::AnyOrientation) { options ^= WidgetFactory::AnyOrientation; options |= d->form->library()->showOrientationSelectionPopup( d->_class, container->widget(), d->form->widget()->mapToGlobal(d->pos)); if (options & WidgetFactory::AnyOrientation) return; //cancelled } } else options |= WidgetFactory::AnyOrientation; QWidget *w = d->form->library()->createWidget(d->_class, container->widget(), d->widgetName, container, options); if (!w) { d->form->abortWidgetInserting(); WidgetInfo *winfo = d->form->library()->widgetInfoForClassName(d->_class); KMessageBox::sorry(d->form ? d->form->widget() : 0, i18n("Could not insert widget of type \"%1\". A problem with widget's creation encountered.", winfo ? winfo->name() : QString())); kWarning() << "ERROR: widget creation failed"; return; } Q_ASSERT(!w->objectName().isEmpty()); //! @todo allow setting this for data view mode as well if (d->form->mode() == Form::DesignMode) { //don't generate accelerators for widgets in design mode KAcceleratorManager::setNoAccel(w); } // w->installEventFilter(container); // if the insertRect is invalid (ie only one point), we use widget' size hint if (((d->insertRect.width() < 21) && (d->insertRect.height() < 21))) { QSize s = w->sizeHint(); if (s.isEmpty()) s = QSize(20, 20); // Minimum size to avoid creating a (0,0) widget int x, y; if (d->insertRect.isValid()) { x = d->insertRect.x(); y = d->insertRect.y(); } else { x = d->pos.x(); y = d->pos.y(); } d->insertRect = QRect(x, y, s.width() + 16/* add some space so more text can be entered*/, s.height()); } // fix widget size is align-to-grid is enabled if (d->form->isSnapWidgetsToGridEnabled()) { const int grid = d->form->gridSize(); int v = alignValueToGrid(d->insertRect.width(), grid); if (v < d->insertRect.width()) // do not allow to make the widget smaller v += grid; d->insertRect.setWidth( v ); v = alignValueToGrid(d->insertRect.height(), grid); if (v < d->insertRect.height()) // do not allow to make the widget smaller v += grid; d->insertRect.setHeight( v ); } w->move(d->insertRect.x(), d->insertRect.y()); // w->resize(d->insertRect.width() - 1, d->insertRect.height() - 1); // -1 is not to hide dots w->resize(d->insertRect.size()); //2.0?? w->setStyle(container->widget()->style()); //2.0 not needed w->setBackgroundOrigin(QWidget::ParentOrigin); w->show(); d->form->abortWidgetInserting(); // ObjectTreeItem object already exists for widgets which corresponds to a Container // it's already created in Container's constructor ObjectTreeItem *item = d->form->objectTree()->lookup(d->widgetName); if (!item) { //not yet created... kDebug() << "Creating ObjectTreeItem:"; item = new ObjectTreeItem(d->form->library()->displayName(d->_class), d->widgetName, w, container); d->form->objectTree()->addItem(container->objectTree(), item); } //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface //(e.g. KexiDBAutoField) if (d->form->mode() == Form::DesignMode && dynamic_cast(w)) { dynamic_cast(w)->assignItem(item); } // We add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later QList list( d->form->library()->autoSaveProperties( w->metaObject()->className()) ); foreach (const QByteArray& name, list) { if (-1 != w->metaObject()->indexOfProperty(name)) item->addModifiedProperty(name, w->property(name)); } container->reloadLayout(); // reload the layout to take the new wigdet into account container->selectWidget(w); if (!d->form->library()->internalProperty(w->metaObject()->className(), "dontStartEditingOnInserting").toBool()) { d->form->library()->startInlineEditing( w->metaObject()->className(), w, item->container() ? item->container() : container); // we edit the widget on creation } //! @todo update widget's width for entered text's metrics kDebug() << "widget added " << this; } void InsertWidgetCommand::undo() { ObjectTreeItem* titem = d->form->objectTree()->lookup(d->widgetName); if (!titem) return; //better this than a crash QWidget *widget = titem->widget(); Container *container = d->form->objectTree()->lookup(d->containerName)->container(); container->deleteWidget(widget); } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InsertWidgetCommand &c) { dbg.nospace() << "InsertWidgetCommand text=" << c.text() << "generatedName=" << c.d->widgetName << "container=" << c.d->containerName << "form=" << c.d->form->widget()->objectName() << "class=" << c.d->_class << "rect=" << c.d->insertRect << "pos=" << c.d->pos; return dbg.space(); } QByteArray InsertWidgetCommand::widgetName() const { return d->widgetName; } /// CreateLayoutCommand /////////////// namespace KFormDesigner { class CreateLayoutCommand::Private { public: Private() { } Form *form; QString containerName; QString name; QHash pos; Form::LayoutType layoutType; }; } CreateLayoutCommand::CreateLayoutCommand(Form &form, Form::LayoutType layoutType, const QWidgetList &list, Command *parent) : Command(parent), d( new Private ) { d->form = &form; d->layoutType = layoutType; CustomSortableWidgetList *realList = 0; switch (d->layoutType) { case Form::NoLayout: case Form::HBox: case Form::Grid: case Form::HSplitter: case Form::HFlow: realList = new HorizontalWidgetList(d->form->toplevelContainer()->widget()); break; case Form::VBox: case Form::VSplitter: case Form::VFlow: realList = new VerticalWidgetList(d->form->toplevelContainer()->widget()); break; } foreach (QWidget *w, list) { realList->append(w); } realList->sort(); // we sort them now, before creating the layout foreach (QWidget *w, *realList) { d->pos.insert(w->objectName().toLatin1().constData(), w->geometry()); } ObjectTreeItem *item = 0; if (!realList->isEmpty()) { d->form->objectTree()->lookup(realList->first()->objectName()); } if (item && item->parent()->container()) { d->containerName = item->parent()->name(); } delete realList; } CreateLayoutCommand::CreateLayoutCommand(Command *parent) : Command(parent), d( new Private ) { // d will be initialized in subclass } CreateLayoutCommand::~CreateLayoutCommand() { delete d; } void CreateLayoutCommand::init() { switch (d->layoutType) { case Form::HBox: setText( i18nc("(qtundo-format)", "Group Widgets Horizontally") ); break; case Form::VBox: setText( i18nc("(qtundo-format)", "Group Widgets Vertically") ); break; case Form::Grid: setText( i18nc("(qtundo-format)", "Group Widgets in a Grid") ); break; case Form::HSplitter: setText( i18nc("(qtundo-format)", "Group Widgets Horizontally in a Splitter") ); break; case Form::VSplitter: setText( i18nc("(qtundo-format)", "Group Widgets Vertically in a Splitter") ); break; case Form::HFlow: setText( i18nc("(qtundo-format)", "Group Widgets By Rows") ); break; case Form::VFlow: setText( i18nc("(qtundo-format)", "Group Widgets Vertically By Columns") ); break; default: setText( i18nc("(qtundo-format)", "Group widgets") ); break; } } void CreateLayoutCommand::execute() { WidgetLibrary *lib = d->form->library(); if (!lib) return; ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName); Container *container = titem ? titem->container() : 0; if (!container) container = d->form->toplevelContainer(); // use toplevelContainer by default QByteArray classname; switch (d->layoutType) { case Form::HSplitter: case Form::VSplitter: classname = "QSplitter"; break; default: classname = Container::layoutTypeToString(d->layoutType).toLatin1(); } if (d->name.isEmpty())// the name must be generated only once d->name = d->form->objectTree()->generateUniqueName(classname); QWidget *w = lib->createWidget(classname, container->widget(), d->name.toLatin1(), container); //! @todo allow setting this for data view mode as well if (w) { if (d->form->mode() == Form::DesignMode) { //don't generate accelerators for widgets in design mode KAcceleratorManager::setNoAccel(w); } } ObjectTreeItem *tree = w ? d->form->objectTree()->lookup(w->objectName()) : 0; if (!tree) return; container->selectWidget(0); w->move(d->pos.constBegin().value().topLeft()); // we move the layout at the position of the topleft widget // sizeHint of these widgets depends on geometry, so give them appropriate geometry if (d->layoutType == Form::HFlow) w->resize(QSize(700, 20)); else if (d->layoutType == Form::VFlow) w->resize(QSize(20, 700)); w->show(); // We reparent every widget to the Layout and insert them into it QHash::ConstIterator endIt = d->pos.constEnd(); for (QHash::ConstIterator it = d->pos.constBegin(); it != endIt; ++it) { ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); if (item && item->widget()) { item->widget()->setParent(w); item->eventEater()->setContainer(tree->container()); d->form->objectTree()->reparent(item->name(), d->name); } } if (d->layoutType == Form::HSplitter) ((QSplitter*)w)->setOrientation(Qt::Horizontal); else if (d->layoutType == Form::VSplitter) ((QSplitter*)w)->setOrientation(Qt::Vertical); else if (tree->container()) { tree->container()->setLayoutType(d->layoutType); w->resize(tree->container()->layout()->sizeHint()); // the layout doesn't have its own size } container->selectWidget(w); //! @todo 2.0 unused? //FormManager::self()->windowChanged(d->form->widget()); // to reload the ObjectTreeView } void CreateLayoutCommand::undo() { ObjectTreeItem *parent = d->form->objectTree()->lookup(d->containerName); if (!parent) parent = d->form->objectTree(); // We reparent every widget to the Container and take them out of the layout QHash::ConstIterator endIt = d->pos.constEnd(); for (QHash::ConstIterator it = d->pos.constBegin(); it != endIt; ++it) { ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); if (item && item->widget()) { item->widget()->setParent(parent->widget()); item->widget()->move(0, 0); item->eventEater()->setContainer(parent->container()); if (d->pos.value(it.key()).isValid()) item->widget()->setGeometry(d->pos.value(it.key())); d->form->objectTree()->reparent(item->name(), d->containerName); } } if (!parent->container()) return; ObjectTreeItem* titem = d->form->objectTree()->lookup(d->name); if (!titem) return; //better this than a crash QWidget *w = titem->widget(); parent->container()->deleteWidget(w); // delete the layout widget //! @todo 2.0 unused? // FormManager::self()->windowChanged(d->form->widget()); // to reload ObjectTreeView } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const CreateLayoutCommand &c) { dbg.nospace() << "CreateLayoutCommand text=" << c.text() << "generatedName=" << c.d->name << "widgets=" << c.d->pos.keys() << "container=" << c.d->containerName << "form=" << c.d->form->widget()->objectName(); return dbg.space(); } /// BreakLayoutCommand /////////////// BreakLayoutCommand::BreakLayoutCommand(const Container &container, Command *parent) : CreateLayoutCommand(parent) { d->containerName = container.topLevelWidget()->objectName(); d->name = container.widget()->objectName(); d->form = container.form(); d->layoutType = container.layoutType(); foreach (ObjectTreeItem *titem, *container.objectTree()->children()) { QRect r( container.widget()->mapTo(container.widget()->parentWidget(), titem->widget()->pos()), titem->widget()->size() ); d->pos.insert(titem->widget()->objectName().toLatin1().constData(), r); } setText( i18nc("(qtundo-format)", "Break Layout: \"%1\"", d->name) ); } BreakLayoutCommand::~BreakLayoutCommand() { } void BreakLayoutCommand::execute() { CreateLayoutCommand::undo(); } void BreakLayoutCommand::undo() { CreateLayoutCommand::execute(); } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const BreakLayoutCommand &c) { dbg.nospace() << "BreakLayoutCommand text=" << c.text() << "widgets=" << c.d->pos.keys() << "container=" << c.d->containerName << "form=" << c.d->form->widget()->objectName(); return dbg.space(); } // PasteWidgetCommand namespace KFormDesigner { class PasteWidgetCommand::Private { public: Private() { } Form *form; QString data; QString containerName; QPoint pos; QStringList names; }; } PasteWidgetCommand::PasteWidgetCommand(const QDomDocument &domDoc, const Container& container, const QPoint& p, Command *parent) : Command(parent), d( new Private ) { d->form = container.form(); d->data = domDoc.toString(); d->containerName = container.widget()->objectName(); d->pos = p; if (domDoc.firstChildElement("UI").firstChildElement("widget").isNull()) return; QRect boundingRect; for (QDomNode n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { // more than one widget const QDomElement el = n.toElement(); if (el.tagName() != "widget") continue; QDomElement rect; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) rect = n.firstChild().toElement(); } QDomElement x = rect.firstChildElement("x"); QDomElement y = rect.firstChildElement("y"); QDomElement w = rect.firstChildElement("width"); QDomElement h = rect.firstChildElement("height"); int rx = x.text().toInt(); int ry = y.text().toInt(); int rw = w.text().toInt(); int rh = h.text().toInt(); QRect r(rx, ry, rw, rh); boundingRect = boundingRect.unite(r); } //2.0 d->pos -= boundingRect.topLeft(); setText( i18nc("(qtundo-format)", "Paste") ); } PasteWidgetCommand::~PasteWidgetCommand() { delete d; } void PasteWidgetCommand::execute() { ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName); if (!titem) return; //better this than a crash Container *container = titem->container(); QString errMsg; int errLine; int errCol; QDomDocument domDoc("UI"); bool parsed = domDoc.setContent(d->data, false, &errMsg, &errLine, &errCol); if (!parsed) { kDebug() << errMsg; kDebug() << "line: " << errLine << "col: " << errCol; return; } kDebug() << domDoc.toString(); if (!domDoc.firstChildElement("UI").hasChildNodes()) // nothing in the doc return; QDomElement el = domDoc.firstChildElement("UI").firstChildElement("widget"); if (el.isNull()) return; QDomNode n; for (n = el.nextSibling(); !n.isNull() && n.toElement().tagName() != "widget"; n = n.nextSibling()) ; if (n.isNull()) { // only one "widget" child tag, so we can paste it at cursor pos QDomElement el = domDoc.firstChildElement("UI").firstChildElement("widget").toElement(); fixNames(el); if (d->pos.isNull()) fixPos(el, container); else changePos(el, d->pos); d->form->setInteractiveMode(false); FormIO::loadWidget(container, el); d->form->setInteractiveMode(true); } else { int minX = INT_MAX, minY = INT_MAX; if (!d->pos.isNull()) { // compute top-left point for the united rectangles for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { // more than one "widget" child tag if (n.toElement().tagName() != "widget") { continue; } QDomElement el = n.toElement(); QDomElement rectEl; for (QDomNode n2 = el.firstChild(); !n2.isNull(); n2 = n2.nextSibling()) { if ((n2.toElement().tagName() == "property") && (n2.toElement().attribute("name") == "geometry")) { rectEl = n2.firstChild().toElement(); break; } } int x = rectEl.firstChildElement("x").text().toInt(); if (x < minX) minX = x; int y = rectEl.firstChildElement("y").text().toInt(); if (y < minY) minY = y; } } for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { // more than one "widget" child tag if (n.toElement().tagName() != "widget") { continue; } QDomElement el = n.toElement(); fixNames(el); if (d->pos.isNull()) { fixPos(el, container); } else { //container->widget()->mapTo( moveWidgetBy( el, container, QPoint(-minX, -minY) + d->pos // fix position by subtracting the original // offset and adding the new one ); } d->form->setInteractiveMode(false); FormIO::loadWidget(container, el); d->form->setInteractiveMode(true); } } //FormIO::setCurrentForm(0); d->names.clear(); // We store the names of all the created widgets, to delete them later for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.toElement().tagName() != "widget") { continue; } for (QDomNode m = n.firstChild(); !m.isNull(); m = m.nextSibling()) { if ((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) { d->names.append(m.toElement().text()); break; } } } container->form()->selectFormWidget(); foreach (const QString& widgetName, d->names) { // We select all the pasted widgets ObjectTreeItem *item = d->form->objectTree()->lookup(widgetName); if (item) { container->selectWidget(item->widget(), Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); } } } void PasteWidgetCommand::undo() { ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName); if (!titem) return; //better this than a crash Container *container = titem->container(); // We just delete all the widgets we have created foreach (const QString& widgetName, d->names) { ObjectTreeItem* titem = container->form()->objectTree()->lookup(widgetName); if (!titem) { continue; //better this than a crash } QWidget *w = titem->widget(); container->deleteWidget(w); } } void PasteWidgetCommand::changePos(QDomElement &el, const QPoint &newPos) { //QDomElement el = widg.cloneNode(true).toElement(); QDomElement rect; // Find the widget geometry if there is one for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) { rect = n.firstChild().toElement(); break; } } QDomElement x = rect.firstChildElement("x"); x.removeChild(x.firstChild()); QDomText valueX = el.ownerDocument().createTextNode(QString::number(newPos.x())); x.appendChild(valueX); QDomElement y = rect.firstChildElement("y"); y.removeChild(y.firstChild()); QDomText valueY = el.ownerDocument().createTextNode(QString::number(newPos.y())); y.appendChild(valueY); //return el; } void PasteWidgetCommand::fixPos(QDomElement &el, Container *container) { /* QDomElement rect; for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) rect = n.firstChild().toElement(); } QDomElement x = rect.namedItem("x").toElement(); QDomElement y = rect.namedItem("y").toElement(); QDomElement wi = rect.namedItem("width").toElement(); QDomElement h = rect.namedItem("height").toElement(); int rx = x.text().toInt(); int ry = y.text().toInt(); int rw = wi.text().toInt(); int rh = h.text().toInt(); QRect r(rx, ry, rw, rh); QWidget *w = d->form->widget()->childAt(r.x() + 6, r.y() + 6, false); if(!w) return; while((w->geometry() == r) && (w != 0))// there is already a widget there, with the same size { w = d->form->widget()->childAt(w->x() + 16, w->y() + 16, false); r.moveBy(10,10); } // the pasted wigdet should stay inside container's boundaries if(r.x() < 0) r.moveLeft(0); else if(r.right() > container->widget()->width()) r.moveLeft(container->widget()->width() - r.width()); if(r.y() < 0) r.moveTop(0); else if(r.bottom() > container->widget()->height()) r.moveTop(container->widget()->height() - r.height()); if(r != QRect(rx, ry, rw, rh)) //return el; //else changePos(el, QPoint(r.x(), r.y())); */ moveWidgetBy(el, container, QPoint(0, 0)); } void PasteWidgetCommand::moveWidgetBy(QDomElement &el, Container *container, const QPoint &p) { QDomElement rect; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) { rect = n.firstChild().toElement(); break; } } QDomElement x = rect.firstChildElement("x"); QDomElement y = rect.firstChildElement("y"); QDomElement wi = rect.firstChildElement("width"); QDomElement h = rect.firstChildElement("height"); int rx = x.text().toInt(); int ry = y.text().toInt(); int rw = wi.text().toInt(); int rh = h.text().toInt(); QRect r(rx + p.x(), ry + p.y(), rw, rh); kDebug() << "Moving widget by " << p << "from " << rx << ry << "to" << r.topLeft(); QWidget *w = d->form->widget()->childAt(r.x() + 6, r.y() + 6); while (w && (w->geometry() == r)) { // there is already a widget there, with the same size w = d->form->widget()->childAt(w->x() + 16, w->y() + 16); r.translate(10, 10); } // the pasted wigdet should stay inside container's boundaries if (r.x() < 0) r.moveLeft(0); else if (r.right() > container->widget()->width()) r.moveLeft(container->widget()->width() - r.width()); if (r.y() < 0) r.moveTop(0); else if (r.bottom() > container->widget()->height()) r.moveTop(container->widget()->height() - r.height()); if (r != QRect(rx, ry, rw, rh)) //return el; //else changePos(el, QPoint(r.x(), r.y())); } void PasteWidgetCommand::fixNames(QDomElement &el) { QString wname; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "name")) { wname = n.toElement().text(); while (d->form->objectTree()->lookup(wname)) { // name already exists bool ok; int num = wname.right(1).toInt(&ok, 10); if (ok) wname = wname.left(wname.length() - 1) + QString::number(num + 1); else wname += "2"; } if (wname != n.toElement().text()) { // we change the name, so we recreate the element n.removeChild(n.firstChild()); QDomElement type = el.ownerDocument().createElement("string"); QDomText valueE = el.ownerDocument().createTextNode(wname); type.appendChild(valueE); n.toElement().appendChild(type); } } if (n.toElement().tagName() == "widget") { // fix child widgets names QDomElement child = n.toElement(); fixNames(child); } } } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PasteWidgetCommand &c) { dbg.nospace() << "PasteWidgetCommand pos=" << c.d->pos << "widgets=" << c.d->names << "container=" << c.d->containerName << "form=" << c.d->form->widget()->objectName() << "data=" << (c.d->data.left(80) + "..."); return dbg.space(); } // DeleteWidgetCommand namespace KFormDesigner { class DeleteWidgetCommand::Private { public: Private() { } Form *form; QDomDocument domDoc; QHash containers; QHash parents; }; } DeleteWidgetCommand::DeleteWidgetCommand(Form& form, const QWidgetList &list, Command *parent) : Command(parent), d( new Private ) { d->form = &form; KFormDesigner::widgetsToXML(d->domDoc, d->containers, d->parents, *d->form, list); setText( i18nc("(qtundo-format)", "Delete widget") ); /* moved d->domDoc.appendChild(d->domDoc.createElement("UI")); QDomElement parent = d->domDoc.namedItem("UI").toElement(); QWidgetList topLevelList(list); removeChildrenFromList(topLevelList); foreach (QWidget *w, topLevelList) { ObjectTreeItem *item = d->form->objectTree()->lookup(w->objectName()); if (!item) return; // We need to store both parentContainer and parentWidget as they may be different (eg for TabWidget page) d->containers.insert( item->name().toLatin1(), - d->form->parentContainer(item->widget())->widget()->objectName().toLatin1().constData() + d->form->parentContainer(item->widget())->widget()->objectName().toLatin1()) ); d->parents.insert( item->name().toLatin1(), item->parent()->name().toLatin1() ); FormIO::saveWidget(item, parent, d->domDoc); d->form->connectionBuffer()->saveAllConnectionsForWidget( item->widget()->objectName(), d->domDoc); } FormIO::cleanClipboard(parent);*/ } DeleteWidgetCommand::~DeleteWidgetCommand() { delete d; } void DeleteWidgetCommand::execute() { // ObjectTreeItem *containerItemToSelect = 0; QHash::ConstIterator endIt = d->containers.constEnd(); for (QHash::ConstIterator it = d->containers.constBegin(); it != endIt; ++it) { ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); if (!item || !item->widget()) continue; Container *cont = d->form->parentContainer(item->widget()); /* not needed, Container::deleteWidgets() will select if (!containerItemToSelect) containerItemToSelect = cont->objectTree()->selectableItem();*/ cont->deleteWidget(item->widget()); } } void DeleteWidgetCommand::undo() { QByteArray wname; d->form->setInteractiveMode(false); for (QDomNode n = d->domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { #ifdef KFD_SIGSLOTS if (n.toElement().tagName() == "connections") // restore the widget connections d->form->connectionBuffer()->load(n); #endif if (n.toElement().tagName() != "widget") continue; // We need first to know the name of the widget for (QDomNode m = n.firstChild(); !m.isNull(); n = m.nextSibling()) { if ((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) { wname = m.toElement().text().toLatin1(); break; } } ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containers.value(wname)); if (!titem) return; //better this than a crash Container *cont = titem->container(); ObjectTreeItem *parent = d->form->objectTree()->lookup(d->parents.value(wname)); QDomElement widg = n.toElement(); if (parent) FormIO::loadWidget(cont, widg, parent->widget()); else FormIO::loadWidget(cont, widg); } d->form->setInteractiveMode(true); } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const DeleteWidgetCommand &c) { dbg.nospace() << "DeleteWidgetCommand containers=" << c.d->containers.keys() << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName(); return dbg.space(); } // DuplicateWidgetCommand namespace KFormDesigner { class DuplicateWidgetCommand::Private { public: Private() : pasteCommand(0) { } ~Private() { delete pasteCommand; } Form *form; QDomDocument domDoc; QHash containers; QHash parents; PasteWidgetCommand *pasteCommand; }; } DuplicateWidgetCommand::DuplicateWidgetCommand( const Container& container, const QWidgetList &list, const QPoint& copyToPoint, Command *parent) : Command(parent), d( new Private ) { d->form = container.form(); QDomDocument docToCopy; KFormDesigner::widgetsToXML(docToCopy, d->containers, d->parents, *d->form, list); d->pasteCommand = new PasteWidgetCommand(docToCopy, container, copyToPoint); setText( i18nc("(qtundo-format)", "Duplicate widget") ); } DuplicateWidgetCommand::~DuplicateWidgetCommand() { delete d; } void DuplicateWidgetCommand::execute() { d->pasteCommand->execute(); } void DuplicateWidgetCommand::undo() { d->pasteCommand->undo(); } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const DuplicateWidgetCommand &c) { dbg.nospace() << "DuplicateWidgetCommand containers=" << c.d->containers.keys() << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName(); return dbg.space(); } // CutWidgetCommand namespace KFormDesigner { class CutWidgetCommand::Private { public: Private() : data(0) { } ~Private() { delete data; } QMimeData *data; }; } CutWidgetCommand::CutWidgetCommand(Form& form, const QWidgetList &list, Command *parent) : DeleteWidgetCommand(form, list, parent), d2( new Private ) { setText( i18nc("(qtundo-format)", "Cut") ); } CutWidgetCommand::~CutWidgetCommand() { delete d2; } void CutWidgetCommand::execute() { DeleteWidgetCommand::execute(); delete d2->data; d2->data = KFormDesigner::deepCopyOfClipboardData(); // save clipboard contents // d->domDoc has been filled in DeleteWidgetCommand ctor KFormDesigner::copyToClipboard(d->domDoc.toString()); } void CutWidgetCommand::undo() { DeleteWidgetCommand::undo(); QClipboard *cb = QApplication::clipboard(); cb->setMimeData( d2->data ); // restore prev. clipboard contents } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const CutWidgetCommand &c) { dbg.nospace() << "CutWidgetCommand containers=" << c.d->containers.keys() << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName() << "data=" << (c.d2->data->text().left(80) + "..."); return dbg.space(); } // PropertyCommandGroup namespace KFormDesigner { class PropertyCommandGroup::Private { public: Private() { } // //! Used to store pointers to subcommands that shouldn't be executed // //! on CommandGroup::execute() // QSet commandsShouldntBeExecuted; }; } PropertyCommandGroup::PropertyCommandGroup(const QString &text, Command *parent) : Command(text, parent), d( new Private() ) { } PropertyCommandGroup::~PropertyCommandGroup() { delete d; } #if 0 //bool CommandGroup::copyPropertyValuesFrom(const CommandGroup &commandGroup) bool PropertyCommandGroup::mergeWith(const KUndo2Command * command) { if (id() != command->id()) { return false; } const int thisChildCount = childCount(); const int otherChildCount = command->childCount(); if (thisChildCount != otherChildCount) { kWarning() << "Cannot copy properties: number of widgets differ\n" << "this command group=" << *this << "\n" << "other command group=" << *command; return false; } kDebug() << "Values to copy:" << thisChildCount; for (int i = 0; i < thisChildCount; i++) { const PropertyCommand *thisPropertyCommand = dynamic_cast( child(i) ); if (!thisPropertyCommand) { kWarning() << "This command is not of PropertyCommand class\n" << "this command group=" << *this << "\n" << "other command group=" << *command; return false; } const PropertyCommand *otherPropertyCommand = dynamic_cast( command->child(i) ); if (!otherPropertyCommand) { kWarning() << "Other command is not of PropertyCommand class\n" << "this command group=" << *this << "\n" << "other command group=" << *command; return false; } if (thisPropertyCommand->propertyName() != otherPropertyCommand->propertyName()) { kWarning() << "Name of this and the other property does not match\n" << "this command=" << *thisPropertyCommand << "\n" << "other command=" << *otherPropertyCommand << "\n" << "this command group=" << *this << "\n" << "other command group=" << commandGroup; return false; } if (thisPropertyCommand->widgetName().isEmpty()) { kWarning() << "Single widget of this property command not found\n" << "this command=" << *thisPropertyCommand << "\n" << "other command=" << *otherPropertyCommand << "\n" << "this command group=" << *this << "\n" << "other command group=" << commandGroup; return false; } if (otherPropertyCommand->widgetName().isEmpty()) { kWarning() << "Single widget of other property command not found\n" << "this command=" << *thisPropertyCommand << "\n" << "other command=" << *otherPropertyCommand << "\n" << "this command group=" << *this << "\n" << "other command group=" << commandGroup; return false; } if (thisPropertyCommand->widgetName() != otherPropertyCommand->widgetName()) { kWarning() << "Name of the single widget of this property command differs " "from the name of the other property command\n" << "this command=" << *thisPropertyCommand << "\n" << "other command=" << *otherPropertyCommand << "\n" << "this command group=" << *this << "\n" << "other command group=" << commandGroup; return false; } } // 2. copy for (int i = 0; i < thisChildCount; i++) { PropertyCommand *thisPropertyCommand = dynamic_cast( child(i) ); const PropertyCommand *otherPropertyCommand = dynamic_cast( command->child(i) ); kDebug() << "Copying value of property" << thisPropertyCommand->propertyName() << "(widget" << thisPropertyCommand->widgetName() << ") from" << thisPropertyCommand->value() << "to" << otherPropertyCommand->value(); thisPropertyCommand->setValue( otherPropertyCommand->value() ); } return true; } #endif void PropertyCommandGroup::execute() { KUndo2Command::redo(); } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PropertyCommandGroup &c) { dbg.nospace() << "PropertyCommandGroup" << static_cast(c); return dbg.space(); } // InlineTextEditingCommand namespace KFormDesigner { class InlineTextEditingCommand::Private { public: Private() : oldTextKnown(false) { } Form *form; QPointer widget; QByteArray editedWidgetClass; QString text; QString oldText; /*! Used to make sure that oldText is set only on the first execution of InlineTextEditingCommand::execute() */ bool oldTextKnown : 1; }; } InlineTextEditingCommand::InlineTextEditingCommand( Form& form, QWidget *widget, const QByteArray &editedWidgetClass, const QString &text, Command *parent) : Command(parent) , d( new Private ) { d->form = &form; d->widget = widget; d->editedWidgetClass = editedWidgetClass; d->text = text; d->widget = widget; } InlineTextEditingCommand::~InlineTextEditingCommand() { delete d; } void InlineTextEditingCommand::execute() { WidgetInfo *wi = d->form->library()->widgetInfoForClassName(d->editedWidgetClass); if (!wi) return; QString oldText; d->form->setSlotPropertyChangedEnabled(false); bool ok = wi->factory()->changeInlineText(d->form, d->widget, d->text, oldText); if (!ok && wi && wi->inheritedClass()) { ok = wi->inheritedClass()->factory()->changeInlineText(d->form, d->widget, d->text, oldText); } d->form->setSlotPropertyChangedEnabled(true); if (!ok) return; if (!d->oldTextKnown) { d->oldText = oldText; d->oldTextKnown = true; } } void InlineTextEditingCommand::undo() { WidgetInfo *wi = d->form->library()->widgetInfoForClassName(d->editedWidgetClass); if (!wi) return; QString dummy; d->form->setSlotPropertyChangedEnabled(false); bool ok = wi->factory()->changeInlineText(d->form, d->widget, d->oldText, dummy); if (!ok && wi && wi->inheritedClass()) { ok = wi->inheritedClass()->factory()->changeInlineText(d->form, d->widget, d->oldText, dummy); } d->form->setSlotPropertyChangedEnabled(true); } bool InlineTextEditingCommand::mergeWith(const KUndo2Command * command) { if (id() != command->id()) return false; // int uniqueId; const InlineTextEditingCommand* inlineTextEditingCommand = static_cast(command); // if (d->uniqueId > 0 && inlineTextEditingCommand->d->uniqueId == d->uniqueId) { if ( form() == inlineTextEditingCommand->form() && text() == inlineTextEditingCommand->oldText()) { kDebug() << "Changed from" << text() << "to" << inlineTextEditingCommand->text(); d->text = inlineTextEditingCommand->text(); return true; } return false; } Form* InlineTextEditingCommand::form() const { return d->form; } QString InlineTextEditingCommand::text() const { return d->text; } QString InlineTextEditingCommand::oldText() const { return d->oldText; } KFORMEDITOR_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InlineTextEditingCommand &c) { dbg.nospace() << "InlineTextEditingCommand" << static_cast(c); return dbg.space(); } /////// Tab related commands (to allow tab creation/deletion undoing) namespace KFormDesigner { class InsertPageCommand::Private { public: Private() { } KFormDesigner::Form *form; QString containername; QString name; QString parentname; }; } InsertPageCommand::InsertPageCommand(Container *container, QWidget *parent) : Command() , d(new Private) { d->containername = container->widget()->objectName(); d->form = container->form(); d->parentname = parent->objectName(); setText( i18nc("(qtundo-format)", "Add Page") ); } InsertPageCommand::~InsertPageCommand() { delete d; } void InsertPageCommand::execute() { execute(QString(), QString(), -1); } void InsertPageCommand::execute(const QString& pageWidgetName, const QString& pageName, int pageIndex) { Container *container = d->form->objectTree()->lookup(d->containername)->container(); QWidget *parent = d->form->objectTree()->lookup(d->parentname)->widget(); if (d->name.isEmpty()) { if (pageWidgetName.isEmpty()) { d->name = container->form()->objectTree()->generateUniqueName( container->form()->library()->displayName("QWidget").toLatin1(), /*!numberSuffixRequired*/false); } else { d->name = pageWidgetName; } } QWidget *page = container->form()->library()->createWidget( "QWidget", parent, d->name.toLatin1(), container); page->setAutoFillBackground(true); // page->setPaletteBackgroundColor(Qt::red); ObjectTreeItem *item = container->form()->objectTree()->lookup(d->name); QByteArray classname = parent->metaObject()->className(); if (classname == "KFDTabWidget") { TabWidgetBase *tab = dynamic_cast(parent); const QString realPageName = pageName.isEmpty() ? i18n("Page %1", tab->count() + 1) : pageName; if (pageIndex < 0) pageIndex = tab->count(); tab->insertTab(pageIndex, page, realPageName); tab->setCurrentWidget(page); item->addModifiedProperty("title", realPageName); } else if (classname == "QStackedWidget" || /* compat */ classname == "QWidgetStack") { QStackedWidget *stack = dynamic_cast(parent); stack->addWidget(page); stack->setCurrentWidget(page); item->addModifiedProperty("stackIndex", stack->indexOf(page)); } } void InsertPageCommand::undo() { undo(QString()); } void InsertPageCommand::undo(const QString& name) { if (!name.isEmpty()) { d->name = name; } QWidget *page = d->form->objectTree()->lookup(d->name)->widget(); QWidget *parent = d->form->objectTree()->lookup(d->parentname)->widget(); QWidgetList list; list.append(page); Command *com = new DeleteWidgetCommand(*d->form, list); QByteArray classname = parent->metaObject()->className(); if (classname == "KFDTabWidget") { TabWidgetBase *tab = dynamic_cast(parent); tab->removeTab(tab->indexOf(page)); } else if (classname == "QStackedWidget" || /* compat */ classname == "QWidgetStack") { QStackedWidget *stack = dynamic_cast(parent); int index = stack->indexOf(page); if (index > 0) index--; else if (index < (stack->count()-1)) index++; else index = -1; if (index >= 0) stack->setCurrentIndex(index); stack->removeWidget(page); } com->execute(); delete com; } namespace KFormDesigner { class RemovePageCommand::Private { public: Private() { } KFormDesigner::Form *form; QString containername; QString name; QString pageName; int pageIndex; QString parentname; InsertPageCommand *insertCommand; }; } RemovePageCommand::RemovePageCommand(Container *container, QWidget *parent) : Command() , d(new Private) { d->containername = container->widget()->objectName(); d->form = container->form(); TabWidgetBase *tab = dynamic_cast(parent); if (tab) { d->name = tab->currentWidget()->objectName(); d->pageName = tab->tabText(tab->currentIndex()); d->pageIndex = tab->currentIndex(); } d->parentname = parent->objectName(); d->insertCommand = new InsertPageCommand(container, parent); setText( i18nc("(qtundo-format)", "Remove Page") ); } RemovePageCommand::~RemovePageCommand() { delete d->insertCommand; delete d; } void RemovePageCommand::execute() { d->insertCommand->undo(d->name); } void RemovePageCommand::undo() { d->insertCommand->execute(d->name, d->pageName, d->pageIndex); } int RemovePageCommand::pageIndex() const { return d->pageIndex; } QString RemovePageCommand::pageName() const { return d->pageName; } diff --git a/kexi/formeditor/formIO.cpp b/kexi/formeditor/formIO.cpp index ce681113d87..65ae3814a86 100644 --- a/kexi/formeditor/formIO.cpp +++ b/kexi/formeditor/formIO.cpp @@ -1,1709 +1,1709 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2005-2009 JarosÅ‚aw Staniek 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FormWidget.h" #include "form.h" #include "container.h" #include "objecttree.h" //unused #include "formmanager.h" #include "widgetlibrary.h" #ifndef KEXI_NO_FORM_SPRING_ELEMENT # include "spring.h" #endif #include "events.h" #include "utils.h" #include "widgetwithsubpropertiesinterface.h" #include "formIO.h" #define KEXI_NO_FLOWLAYOUT #ifdef __GNUC__ #warning Port Kexi flow layout! #else #pragma WARNING( Port Kexi flow layout! ) #endif #ifndef KEXI_NO_FLOWLAYOUT #include #endif #define KEXI_NO_PIXMAPCOLLECTION #ifdef __GNUC__ #warning pixmapcollection #else #pragma WARNING( pixmapcollection ) #endif #ifndef KEXI_NO_PIXMAPCOLLECTION #include "pixmapcollection.h" #endif //! @return Value of attribute "name", or "objectName" in absence of "name". //! This is for compatibility with Kexi 1.x. static QString nameAttribute(const QDomElement& el) { QString res( el.attribute("name") ); if (res.isEmpty()) { res = el.attribute("objectName"); } return res; } //! A blank widget used when the class name is not supported CustomWidget::CustomWidget(const QByteArray &className, QWidget *parent) : QWidget(parent), m_className(className) { setBackgroundRole(QPalette::Dark); } CustomWidget::~CustomWidget() { } void CustomWidget::paintEvent(QPaintEvent *) { QPainter p(this); p.setBrush(palette().text()); QRect r(rect()); r.setX(r.x() + 2); p.drawText(r, Qt::AlignTop, m_className); } using namespace KFormDesigner; QHash *FormIO::m_buddies = 0; ObjectTreeItem *FormIO::m_currentItem = 0; Form *FormIO::m_currentForm = 0; bool FormIO::m_savePixmapsInline = false; // FormIO itself KFORMEDITOR_EXPORT uint KFormDesigner::version() { return KFORMDESIGNER_VERSION; } ///////////////////////////////////////////////////////////////////////////// ///////////// Saving/loading functions ////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// FormIO::FormIO() { } FormIO::~FormIO() { } bool FormIO::saveFormToFile(Form *form, const QString &filename) { QString _filename; if (!form->filename().isEmpty() && filename.isEmpty()) { _filename = form->filename(); } if (filename.isEmpty()) { _filename = KFileDialog::getSaveFileName(KUrl(), i18n("*.ui|Qt Designer UI Files")); if (_filename.isEmpty()) { return false; } } else { _filename = filename; } form->setFilename(_filename); QDomDocument domDoc; if (!saveFormToDom(form, domDoc)) return false; QFile file(_filename); if (!file.open(QIODevice::WriteOnly)) return false; QTextStream stream(&file); stream << domDoc.toString(3); file.close(); return true; } bool FormIO::saveFormToByteArray(Form *form, QByteArray &dest) { QDomDocument domDoc; if (!saveFormToDom(form, domDoc)) return false; dest = domDoc.toByteArray(); return true; } bool FormIO::saveFormToString(Form *form, QString &dest, int indent) { QDomDocument domDoc; if (!saveFormToDom(form, domDoc)) return false; dest = domDoc.toString(indent); return true; } bool FormIO::saveFormToDom(Form *form, QDomDocument &domDoc) { m_currentForm = form; domDoc = QDomDocument("UI"); QDomElement uiElement = domDoc.createElement("UI"); domDoc.appendChild(uiElement); uiElement.setAttribute("version", "3.1"); uiElement.setAttribute("stdsetdef", 1); //update format version information form->headerProperties()->insert("version", QString::number(form->formatVersion())); //custom properties QDomElement headerPropertiesEl = domDoc.createElement("kfd:customHeader"); QHash::ConstIterator itEnd = form->headerProperties()->constEnd(); for (QHash::ConstIterator it = form->headerProperties()->constBegin(); it != itEnd; ++it) { headerPropertiesEl.setAttribute(it.key(), it.value()); } uiElement.appendChild(headerPropertiesEl); // We save the savePixmapsInline property in the Form QDomElement inlinePix = domDoc.createElement("pixmapinproject"); uiElement.appendChild(inlinePix); // We create the top class element QDomElement baseClass = domDoc.createElement("class"); uiElement.appendChild(baseClass); QDomText baseClassV = domDoc.createTextNode("QWidget"); baseClass.appendChild(baseClassV); // Save the toplevel widgets, and so the whole Form saveWidget(form->objectTree(), uiElement, domDoc); // We then save the layoutdefaults element QDomElement layoutDefaults = domDoc.createElement("layoutDefaults"); layoutDefaults.setAttribute("spacing", QString::number(form->defaultSpacing())); layoutDefaults.setAttribute("margin", QString::number(form->defaultMargin())); uiElement.appendChild(layoutDefaults); // Save tab Stops if (form->autoTabStops()) form->autoAssignTabStops(); QDomElement tabStops = domDoc.createElement("tabstops"); uiElement.appendChild(tabStops); foreach (ObjectTreeItem *item, *form->tabStops()) { QDomElement tabstop = domDoc.createElement("tabstop"); tabStops.appendChild(tabstop); QDomText tabStopText = domDoc.createTextNode(item->name()); tabstop.appendChild(tabStopText); } #ifdef __GNUC__ #warning pixmapcollection #else #pragma WARNING( pixmapcollection ) #endif #ifndef KEXI_NO_PIXMAPCOLLECTION // Save the Form 's PixmapCollection form->pixmapCollection()->save(uiElement); #endif #ifdef KFD_SIGSLOTS // Save the Form connections form->connectionBuffer()->save(uiElement); #endif form->setUndoStackClean(); m_currentForm = 0; m_currentItem = 0; //m_currentWidget = 0; return true; } bool FormIO::loadFormFromByteArray(Form *form, QWidget *container, QByteArray &src, bool preview) { QString errMsg; int errLine; int errCol; QDomDocument inBuf; bool parsed = inBuf.setContent(src, false, &errMsg, &errLine, &errCol); if (!parsed) { kDebug() << errMsg; kDebug() << "line:" << errLine << "col:" << errCol; return false; } if (!loadFormFromDom(form, container, inBuf)) { return false; } if (preview) { form->setMode(Form::DataMode); } return true; } bool FormIO::loadFormFromString(Form *form, QWidget *container, QString &src, bool preview) { QString errMsg; int errLine; int errCol; #ifdef KEXI_DEBUG_GUI form->m_recentlyLoadedUICode = src; #endif QDomDocument inBuf; bool parsed = inBuf.setContent(src, false, &errMsg, &errLine, &errCol); if (!parsed) { kDebug() << errMsg; kDebug() << "line:" << errLine << "col: " << errCol; return false; } if (!loadFormFromDom(form, container, inBuf)) { return false; } if (preview) { form->setMode(Form::DataMode); } return true; } bool FormIO::loadFormFromFile(Form *form, QWidget *container, const QString &filename) { QString errMsg; int errLine; int errCol; QString _filename; if (filename.isEmpty()) { _filename = KFileDialog::getOpenFileName(KUrl(), i18n("*.ui|Qt Designer UI Files")); if (_filename.isEmpty()) { return false; } } else { _filename = filename; } QFile file(_filename); if (!file.open(QIODevice::ReadOnly)) { //! @todo proved err msg to the user kDebug() << "Cannot open the file " << _filename; return false; } QDomDocument doc; if (!doc.setContent(&file, false/* !namespaceProcessing*/, &errMsg, &errLine, &errCol)) { //! @todo proved err msg to the user kDebug() << errMsg; kDebug() << errLine << "col:" << errCol; return false; } return loadFormFromDom(form, container, doc); } bool FormIO::loadFormFromDom(Form *form, QWidget *container, QDomDocument &inBuf) { m_currentForm = form; QDomElement ui = inBuf.firstChildElement("UI"); //custom properties form->headerProperties()->clear(); QDomElement headerPropertiesEl = ui.firstChildElement("kfd:customHeader"); QDomAttr attr = headerPropertiesEl.firstChild().toAttr(); while (!attr.isNull() && attr.isAttr()) { form->headerProperties()->insert(attr.name().toLatin1(), attr.value()); attr = attr.nextSibling().toAttr(); } //update format version information uint ver = 1; //the default if (form->headerProperties()->contains("version")) { bool ok; uint v = (*form->headerProperties())["version"].toUInt(&ok); if (ok) ver = v; } kDebug() << "original format version: " << ver; form->setOriginalFormatVersion(ver); if (ver < KFormDesigner::version()) { //! @todo We can either 1) convert from old format and later save in a new one or 2) keep old format. //! To do this we may need to look at the original format version number. kWarning() << "original format is older than current: " << KFormDesigner::version(); form->setFormatVersion(KFormDesigner::version()); } else form->setFormatVersion(ver); if (ver > KFormDesigner::version()) { //! @todo display information about too new format and that "some information will not be available". kWarning() << "original format is newer than current: " << KFormDesigner::version(); } // Load the pixmap collection m_savePixmapsInline = ((ui.firstChildElement("pixmapinproject").isNull()) || (!ui.firstChildElement("images").isNull())); #ifdef __GNUC__ #warning pixmapcollection #else #pragma WARNING( pixmapcollection ) #endif #ifndef KEXI_NO_PIXMAPCOLLECTION form->pixmapCollection()->load(ui.namedItem("collection")); #endif QDomElement element = ui.firstChildElement("widget"); createToplevelWidget(form, container, element); // Loading the tabstops QDomElement tabStops = ui.firstChildElement("tabstops"); if (!tabStops.isNull()) { int i = 0; uint itemsNotFound = 0; for (QDomNode n = tabStops.firstChild(); !n.isNull(); n = n.nextSibling(), i++) { QString name = n.toElement().text(); ObjectTreeItem *item = form->objectTree()->lookup(name); if (!item) { kWarning() << "ERROR : no ObjectTreeItem "; continue; } const int index = form->tabStops()->indexOf(item); /* Compute a real destination index: "a number of not found items so far". */ const int realIndex = i - itemsNotFound; if ((index != -1) && (index != realIndex)) { // the widget is not in the same place, so we move it form->tabStops()->removeOne(item); form->tabStops()->insert(realIndex, item); } if (index == -1) { itemsNotFound++; kDebug() << "FormIO: item '" << name << "' not in list"; } } } #ifdef KFD_SIGSLOTS // Load the form connections form->connectionBuffer()->load(ui.namedItem("connections")); #endif m_currentForm = 0; m_currentItem = 0; return true; } ///////////////////////////////////////////////////////////////////////////// ///////////// Functions to save/load properties ///////////////////////////// ///////////////////////////////////////////////////////////////////////////// void FormIO::savePropertyValue(QDomElement &parentNode, QDomDocument &parent, const char *name, const QVariant &value, QWidget *w, WidgetLibrary *lib) { // Widget specific properties and attributes /////////////// // kDebug() << "Saving the property: " << name; WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(w); QWidget *subwidget = w; bool addSubwidgetFlag = false; int propertyId = w->metaObject()->indexOfProperty(name); const bool propertyIsName = qstrcmp(name, "objectName") == 0 || qstrcmp(name, "name") == 0; if (!propertyIsName && propertyId == -1 && subpropIface && subpropIface->subwidget()) { // try property from subwidget subwidget = subpropIface->subwidget(); propertyId = subpropIface->subwidget()->metaObject()->indexOfProperty(name); addSubwidgetFlag = true; } if (!propertyIsName && propertyId == -1) { kDebug() << "The object doesn't have this property. Let's try the WidgetLibrary."; if (lib) lib->saveSpecialProperty(w->metaObject()->className(), name, value, w, parentNode, parent); return; } QMetaProperty meta; if (!propertyIsName) { meta = subwidget->metaObject()->property(propertyId); } if (!propertyIsName && (!meta.isValid() || !meta.isStored(subwidget))) //not storable return; QDomElement propertyE = parent.createElement("property"); propertyE.setAttribute("name", propertyIsName ? "name" /* compat with 1.x */ : name); if (addSubwidgetFlag) propertyE.setAttribute("subwidget", "true"); if (meta.isValid() && meta.isEnumType()) { // this property is enum or set type QDomElement type; QDomText valueE; if (meta.isFlagType()) { /* js: it's simpler in Qt4: QStringList list = QStringList::fromStrList(meta.enumerator().valueToKeys(value.toInt())); type = parent.createElement("set"); valueE = parent.createTextNode(list.join("|"));*/ type = parent.createElement("set"); valueE = parent.createTextNode( meta.enumerator().valueToKeys(value.toInt())); type.appendChild(valueE); } else { QString s = meta.enumerator().valueToKey(value.toInt()); type = parent.createElement("enum"); valueE = parent.createTextNode(s); type.appendChild(valueE); } propertyE.appendChild(type); parentNode.appendChild(propertyE); return; } if (value.type() == QVariant::Pixmap) { QDomText valueE; QDomElement type = parent.createElement("pixmap"); QByteArray property = propertyE.attribute("name").toLatin1(); //todo QCString pixmapName = m_currentItem->widget()->property("pixmapName").toCString(); if (m_savePixmapsInline /* (js)too risky: || m_currentItem->pixmapName(property).isNull() */) valueE = parent.createTextNode(saveImage(parent, value.value())); else valueE = parent.createTextNode(m_currentItem->pixmapName(property)); type.appendChild(valueE); propertyE.appendChild(type); parentNode.appendChild(propertyE); return; } // Saving a "normal" property writeVariant(parent, propertyE, value); parentNode.appendChild(propertyE); } void FormIO::writeVariant(QDomDocument &parent, QDomElement &parentNode, const QVariant& value) { QDomElement type; QDomText valueE; switch (value.type()) { case QVariant::String: { type = parent.createElement("string"); valueE = parent.createTextNode(value.toString()); type.appendChild(valueE); break; } case QVariant::CString: { type = parent.createElement("cstring"); valueE = parent.createTextNode(value.toString()); type.appendChild(valueE); break; } case QVariant::Rect: { type = parent.createElement("rect"); QDomElement x = parent.createElement("x"); QDomElement y = parent.createElement("y"); QDomElement w = parent.createElement("width"); QDomElement h = parent.createElement("height"); QDomText valueX = parent.createTextNode(QString::number(value.toRect().x())); QDomText valueY = parent.createTextNode(QString::number(value.toRect().y())); QDomText valueW = parent.createTextNode(QString::number(value.toRect().width())); QDomText valueH = parent.createTextNode(QString::number(value.toRect().height())); x.appendChild(valueX); y.appendChild(valueY); w.appendChild(valueW); h.appendChild(valueH); type.appendChild(x); type.appendChild(y); type.appendChild(w); type.appendChild(h); break; } case QVariant::Color: { type = parent.createElement("color"); QDomElement r = parent.createElement("red"); QDomElement g = parent.createElement("green"); QDomElement b = parent.createElement("blue"); const QColor color(value.value()); QDomText valueR = parent.createTextNode(QString::number(color.red())); QDomText valueG = parent.createTextNode(QString::number(color.green())); QDomText valueB = parent.createTextNode(QString::number(color.blue())); r.appendChild(valueR); g.appendChild(valueG); b.appendChild(valueB); type.appendChild(r); type.appendChild(g); type.appendChild(b); break; } case QVariant::Bool: { type = parent.createElement("bool"); //valueE = parent.createTextNode(QString::number(value.toBool())); valueE = parent.createTextNode(value.toBool() ? "true" : "false"); type.appendChild(valueE); break; } case QVariant::Int: case QVariant::UInt: { type = parent.createElement("number"); valueE = parent.createTextNode(QString::number(value.toInt())); type.appendChild(valueE); break; } case QVariant::Size: { type = parent.createElement("size"); QDomElement w = parent.createElement("width"); QDomElement h = parent.createElement("height"); QDomText valueW = parent.createTextNode(QString::number(value.toSize().width())); QDomText valueH = parent.createTextNode(QString::number(value.toSize().height())); w.appendChild(valueW); h.appendChild(valueH); type.appendChild(w); type.appendChild(h); break; } case QVariant::Point: { type = parent.createElement("point"); QDomElement x = parent.createElement("x"); QDomElement y = parent.createElement("y"); QDomText valueX = parent.createTextNode(QString::number(value.toPoint().x())); QDomText valueY = parent.createTextNode(QString::number(value.toPoint().y())); x.appendChild(valueX); y.appendChild(valueY); type.appendChild(x); type.appendChild(y); break; } case QVariant::Font: { type = parent.createElement("font"); QDomElement f = parent.createElement("family"); QDomElement p = parent.createElement("pointsize"); QDomElement w = parent.createElement("weight"); QDomElement b = parent.createElement("bold"); QDomElement i = parent.createElement("italic"); QDomElement u = parent.createElement("underline"); QDomElement s = parent.createElement("strikeout"); const QFont font(value.value()); QDomText valueF = parent.createTextNode(font.family()); QDomText valueP = parent.createTextNode(QString::number(font.pointSize())); QDomText valueW = parent.createTextNode(QString::number(font.weight())); QDomText valueB = parent.createTextNode(QString::number(font.bold())); QDomText valueI = parent.createTextNode(QString::number(font.italic())); QDomText valueU = parent.createTextNode(QString::number(font.underline())); QDomText valueS = parent.createTextNode(QString::number(font.strikeOut())); f.appendChild(valueF); p.appendChild(valueP); w.appendChild(valueW); b.appendChild(valueB); i.appendChild(valueI); u.appendChild(valueU); s.appendChild(valueS); type.appendChild(f); type.appendChild(p); type.appendChild(w); type.appendChild(b); type.appendChild(i); type.appendChild(u); type.appendChild(s); break; } case QVariant::Cursor: { type = parent.createElement("cursor"); valueE = parent.createTextNode(QString::number(value.value().shape())); type.appendChild(valueE); break; } case QVariant::SizePolicy: { type = parent.createElement("sizepolicy"); QDomElement h(parent.createElement("hsizetype")); QDomElement v(parent.createElement("vsizetype")); QDomElement hs(parent.createElement("horstretch")); QDomElement vs(parent.createElement("verstretch")); const QSizePolicy sizePolicy(value.value()); const QDomText valueH(parent.createTextNode(QString::number(sizePolicy.horizontalPolicy()))); const QDomText valueV(parent.createTextNode(QString::number(sizePolicy.verticalPolicy()))); const QDomText valueHS(parent.createTextNode(QString::number(sizePolicy.horizontalStretch()))); const QDomText valueVS(parent.createTextNode(QString::number(sizePolicy.verticalStretch()))); h.appendChild(valueH); v.appendChild(valueV); hs.appendChild(valueHS); vs.appendChild(valueVS); type.appendChild(h); type.appendChild(v); type.appendChild(hs); type.appendChild(vs); break; } case QVariant::Time: { type = parent.createElement("time"); QDomElement h = parent.createElement("hour"); QDomElement m = parent.createElement("minute"); QDomElement s = parent.createElement("second"); QDomText valueH = parent.createTextNode(QString::number(value.toTime().hour())); QDomText valueM = parent.createTextNode(QString::number(value.toTime().minute())); QDomText valueS = parent.createTextNode(QString::number(value.toTime().second())); h.appendChild(valueH); m.appendChild(valueM); s.appendChild(valueS); type.appendChild(h); type.appendChild(m); type.appendChild(s); break; } case QVariant::Date: { type = parent.createElement("date"); QDomElement y = parent.createElement("year"); QDomElement m = parent.createElement("month"); QDomElement d = parent.createElement("day"); QDomText valueY = parent.createTextNode(QString::number(value.toDate().year())); QDomText valueM = parent.createTextNode(QString::number(value.toDate().month())); QDomText valueD = parent.createTextNode(QString::number(value.toDate().day())); y.appendChild(valueY); m.appendChild(valueM); d.appendChild(valueD); type.appendChild(y); type.appendChild(m); type.appendChild(d); break; } case QVariant::DateTime: { type = parent.createElement("datetime"); QDomElement h = parent.createElement("hour"); QDomElement m = parent.createElement("minute"); QDomElement s = parent.createElement("second"); QDomElement y = parent.createElement("year"); QDomElement mo = parent.createElement("month"); QDomElement d = parent.createElement("day"); QDomText valueH = parent.createTextNode(QString::number(value.toDateTime().time().hour())); QDomText valueM = parent.createTextNode(QString::number(value.toDateTime().time().minute())); QDomText valueS = parent.createTextNode(QString::number(value.toDateTime().time().second())); QDomText valueY = parent.createTextNode(QString::number(value.toDateTime().date().year())); QDomText valueMo = parent.createTextNode(QString::number(value.toDateTime().date().month())); QDomText valueD = parent.createTextNode(QString::number(value.toDateTime().date().day())); h.appendChild(valueH); m.appendChild(valueM); s.appendChild(valueS); y.appendChild(valueY); mo.appendChild(valueMo); d.appendChild(valueD); type.appendChild(h); type.appendChild(m); type.appendChild(s); type.appendChild(y); type.appendChild(mo); type.appendChild(d); break; } default: break; } parentNode.appendChild(type); } void FormIO::savePropertyElement(QDomElement &parentNode, QDomDocument &domDoc, const QString &tagName, const QString &property, const QVariant &value) { QDomElement propertyE = domDoc.createElement(tagName); propertyE.setAttribute("name", property); writeVariant(domDoc, propertyE, value); parentNode.appendChild(propertyE); } QVariant FormIO::readPropertyValue(QDomNode node, QObject *obj, const QString &name) { QDomElement tag = node.toElement(); QString text = tag.text(); QString type = tag.tagName(); if (type == "string" || type == "cstring") return text; else if (type == "rect") { QDomElement x = node.firstChildElement("x"); QDomElement y = node.firstChildElement("y"); QDomElement w = node.firstChildElement("width"); QDomElement h = node.firstChildElement("height"); int rx = x.text().toInt(); int ry = y.text().toInt(); int rw = w.text().toInt(); int rh = h.text().toInt(); return QRect(rx, ry, rw, rh); } else if (type == "color") { const QDomElement r(node.firstChildElement("red")); const QDomElement g(node.firstChildElement("green")); const QDomElement b(node.firstChildElement("blue")); return QColor(r.text().toInt(), g.text().toInt(), b.text().toInt()); } else if (type == "bool") { if (text == "true") return true; else if (text == "false") return false; return text.toInt() != 0; } else if (type == "number") { return text.toInt(); } else if (type == "size") { QDomElement w = node.firstChildElement("width"); QDomElement h = node.firstChildElement("height"); return QSize(w.text().toInt(), h.text().toInt()); } else if (type == "point") { QDomElement x = node.firstChildElement("x"); QDomElement y = node.firstChildElement("y"); return QPoint(x.text().toInt(), y.text().toInt()); } else if (type == "font") { QDomElement fa = node.firstChildElement("family"); QDomElement p = node.firstChildElement("pointsize"); QDomElement w = node.firstChildElement("weight"); QDomElement b = node.firstChildElement("bold"); QDomElement i = node.firstChildElement("italic"); QDomElement u = node.firstChildElement("underline"); QDomElement s = node.firstChildElement("strikeout"); QFont f; f.setFamily(fa.text()); f.setPointSize(p.text().toInt()); f.setWeight(w.text().toInt()); f.setBold(b.text().toInt()); f.setItalic(i.text().toInt()); f.setUnderline(u.text().toInt()); f.setStrikeOut(s.text().toInt()); return f; } else if (type == "cursor") { return QCursor((Qt::CursorShape) text.toInt()); } else if (type == "time") { QDomElement h = node.firstChildElement("hour"); QDomElement m = node.firstChildElement("minute"); QDomElement s = node.firstChildElement("second"); return QTime(h.text().toInt(), m.text().toInt(), s.text().toInt()); } else if (type == "date") { QDomElement y = node.firstChildElement("year"); QDomElement m = node.firstChildElement("month"); QDomElement d = node.firstChildElement("day"); return QDate(y.text().toInt(), m.text().toInt(), d.text().toInt()); } else if (type == "datetime") { QDomElement h = node.firstChildElement("hour"); QDomElement m = node.firstChildElement("minute"); QDomElement s = node.firstChildElement("second"); QDomElement y = node.firstChildElement("year"); QDomElement mo = node.firstChildElement("month"); QDomElement d = node.firstChildElement("day"); QTime t(h.text().toInt(), m.text().toInt(), s.text().toInt()); QDate da(y.text().toInt(), mo.text().toInt(), d.text().toInt()); return QDateTime(da, t); } else if (type == "sizepolicy") { QDomElement h = node.firstChildElement("hsizetype"); QDomElement v = node.firstChildElement("vsizetype"); QDomElement hs = node.firstChildElement("horstretch"); QDomElement vs = node.firstChildElement("verstretch"); QSizePolicy s; s.setHorizontalPolicy((QSizePolicy::SizeType)h.text().toInt()); s.setVerticalPolicy((QSizePolicy::SizeType)v.text().toInt()); s.setHorizontalStretch(hs.text().toInt()); s.setVerticalStretch(vs.text().toInt()); return s; } else if (type == "pixmap") { #ifdef __GNUC__ #warning pixmapcollection #else #pragma WARNING( pixmapcollection ) #endif #ifndef KEXI_NO_PIXMAPCOLLECTION if (m_savePixmapsInline || !m_currentForm || !m_currentItem || !m_currentForm->pixmapCollection()->contains(text)) return loadImage(tag.ownerDocument(), text); else { m_currentItem->setPixmapName(name.toLatin1(), text); return m_currentForm->pixmapCollection()->getPixmap(text); } #endif return QPixmap(); } else if (type == "enum") { return text; } else if (type == "set") { WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(obj); QObject *subobject = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : obj; - const QMetaProperty meta(KexiUtils::findPropertyWithSuperclasses(subobject, name.toLatin1().constData())); + const QMetaProperty meta(KexiUtils::findPropertyWithSuperclasses(subobject, name.toLatin1())); if (meta.isValid()) { if (meta.isFlagType()) { /*qt4 Q3StrList keys; QStringList list = QStringList::split("|", text); QStringList::ConstIterator it, end( list.constEnd() ); for( it = list.constBegin(); it != end; ++it) keys.append((*it).toLatin1());*/ return meta.enumerator().keysToValue(text.toLatin1()); } else { // Metaproperty not found, probably because subwidget is not created. // We will return a string list here with hope that names will // be resolved and translated into an integer value later when subwidget is created, // e.g. near KexiFormView::updateValuesForSubproperties() return text.split("|"); } } } return QVariant(); } ///////////////////////////////////////////////////////////////////////////// ///////////// Functions to save/load widgets //////////////////////////////// ///////////////////////////////////////////////////////////////////////////// void FormIO::saveWidget(ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, bool insideGridLayout) { if (!item) return; kDebug() << item->className() << item->widget()->objectName(); #ifndef KEXI_NO_FORM_SPRING_ELEMENT // we let Spring class handle saving itself if (item->className() == "Spring") { Spring::saveSpring(item, parent, domDoc, insideGridLayout); return; } #endif bool resetCurrentForm = false; m_currentItem = item; if (!m_currentForm) { // copying widget resetCurrentForm = true; m_currentForm = item->container() ? item->container()->form() : item->parent()->container()->form(); } WidgetLibrary *lib = m_currentForm->library(); // if(item->container()) // lib = item->container()->form()->manager()->lib(); // else // lib = item->parent()->container()->form()->manager()->lib(); // We create the "widget" element QDomElement tclass = domDoc.createElement("widget"); parent.appendChild(tclass); if (insideGridLayout) { tclass.setAttribute("row", item->gridRow()); tclass.setAttribute("column", item->gridCol()); if (item->spanMultipleCells()) { tclass.setAttribute("rowspan", item->gridRowSpan()); tclass.setAttribute("colspan", item->gridColSpan()); } } if (!item->parent()) // Toplevel widget tclass.setAttribute("class", "QWidget"); // For compatibility, HBox, VBox and Grid are saved as "QLayoutWidget" else if (KexiUtils::objectIsA(item->widget(), QList() << "HBox" << "VBox" << "Grid" << "HFlow" << "VFlow")) tclass.setAttribute("class", "QLayoutWidget"); else if (KexiUtils::objectIsA(item->widget(), "CustomWidget")) tclass.setAttribute("class", item->className()); else // Normal widgets tclass.setAttribute("class", lib->savingName(item->widget()->metaObject()->className())); // We save every property in the modifProp list of the ObjectTreeItem QHash hash(*(item->modifiedProperties())); QStringList names(hash.keys()); savePropertyValue(tclass, domDoc, "objectName", item->widget()->objectName(), item->widget()); names.removeOne("objectName"); // Important: save dataSource property FIRST before properties like "alignment" // - needed when subproperties are defined after subwidget creation, and subwidget is created after setting "dataSource" // (this is the case for KexiDBAutoField) //! @todo more properties like "dataSource" may needed here... // if (-1 != item->widget()->metaObject()->findProperty("dataSource")) // savePropertyValue(tclass, domDoc, "dataSource", item->widget()->property("dataSource"), item->widget()); // We don't want to save the geometry if the widget is inside a layout (so parent.tagName() == "grid" for example) if (item && !item->parent()) { // save form widget size, but not its position savePropertyValue(tclass, domDoc, "geometry", QRect(QPoint(0, 0), item->widget()->size()), item->widget()); } // normal widget (if == "UI', it means we're copying widget) else if (parent.tagName() == "widget" || parent.tagName() == "UI") savePropertyValue(tclass, domDoc, "geometry", item->widget()->property("geometry"), item->widget()); names.removeOne("geometry"); names.removeOne("layout"); // Save the buddy widget for a label if ( dynamic_cast(item->widget()) && dynamic_cast(item->widget())->buddy()) { savePropertyElement( tclass, domDoc, "property", "buddy", dynamic_cast(item->widget())->buddy()->objectName()); } if (names.contains("paletteBackgroundColor")) { savePropertyElement( tclass, domDoc, "property", "paletteBackgroundColor", item->widget()->palette().color(item->widget()->backgroundRole())); names.removeOne("paletteBackgroundColor"); } if (names.contains("paletteForegroundColor")) { savePropertyElement( tclass, domDoc, "property", "paletteForegroundColor", item->widget()->palette().color(item->widget()->foregroundRole())); names.removeOne("paletteForegroundColor"); } QStringList alignProperties; alignProperties << "hAlign" << "vAlign" << "wordbreak" << "alignment"; foreach (const QString& name, alignProperties) { if (names.contains(name)) { names.removeOne(name); savePropertyValue( tclass, domDoc, "alignment", item->widget()->property("alignment"), item->widget()); break; } } foreach (const QString& name, names) { savePropertyValue(tclass, domDoc, name.toLatin1(), item->widget()->property(name.toLatin1()), item->widget(), lib); } hash.clear(); if (KexiUtils::objectIsA(item->widget(), "CustomWidget")) { QDomDocument doc("TEMP"); doc.setContent(item->unknownProperties()); for (QDomNode n = doc.firstChild(); !n.isNull(); n = n.nextSibling()) { tclass.appendChild(n.cloneNode()); } } // Saving container 's layout if there is one QDomElement layout; if (item->container() && item->container()->layoutType() != Form::NoLayout) { if (item->container()->layout()) { // there is a layout layout = domDoc.createElement("temp"); savePropertyValue(layout, domDoc, "objectName", "unnamed", item->widget()); if (item->modifiedProperties()->contains("layoutMargin")) savePropertyElement(layout, domDoc, "property", "margin", item->container()->layoutMargin()); if (item->modifiedProperties()->contains("layoutSpacing")) savePropertyElement(layout, domDoc, "property", "spacing", item->container()->layoutSpacing()); tclass.appendChild(layout); } } Form::LayoutType layoutType = item->container() ? item->container()->layoutType() : Form::NoLayout; switch (layoutType) { case Form::Grid: { // grid layout layout.setTagName("grid"); foreach (ObjectTreeItem *titem, *item->children()) { saveWidget(titem, layout, domDoc, true); } break; } case Form::HBox: case Form::VBox: { // as we don't save geometry, we need to sort widgets in the right order, not creation order CustomSortableWidgetList *list; if (layout.tagName() == "hbox") { list = new HorizontalWidgetList(item->container()->form()->toplevelContainer()->widget()); layout.setTagName("hbox"); } else { list = new HorizontalWidgetList(item->container()->form()->toplevelContainer()->widget()); layout.setTagName("vbox"); } foreach (ObjectTreeItem *titem, *item->children()) { list->append(titem->widget()); } list->sort(); foreach (QWidget *w, *list) { ObjectTreeItem *titem = item->container()->form()->objectTree()->lookup( w->objectName() ); if (item) saveWidget(titem, layout, domDoc); } delete list; break; } case Form::HFlow: case Form::VFlow: { #ifdef KEXI_NO_FLOWLAYOUT #ifdef __GNUC__ #warning Port Kexi flow layout! #else #pragma WARNING( Port Kexi flow layout! ) #endif #else layout.setTagName("grid"); KexiFlowLayout *flow = static_cast(item->container()->layout()); if (!flow) break; QWidgetList *list = (QWidgetList*)flow->widgetList(); // save some special properties savePropertyElement(layout, domDoc, "property", "customLayout", Container::layoutTypeToString(item->container()->layoutType())); savePropertyElement(layout, domDoc, "property", "justify", static_cast(item->container()->layout())->isJustified()); // fill the widget's grid info, ie just simulate grid layout item->container()->createGridLayout(true); foreach (QWidget *w, *list) { ObjectTreeItem *titem = item->container()->form()->objectTree()->lookup( w->name() ); if (item) saveWidget(titem, layout, domDoc, true); // save grid info for compatibility with QtDesigner } delete list; #endif break; } default: { foreach (ObjectTreeItem *titem, *item->children()) { saveWidget(titem, tclass, domDoc); } } } addIncludeFileName(lib->includeFileName( item->widget()->metaObject()->className()), domDoc); if (resetCurrentForm) m_currentForm = 0; m_currentItem = 0; } void FormIO::cleanClipboard(QDomElement &uiElement) { // remove includehints element not needed if (!uiElement.firstChildElement("includehints").isNull()) uiElement.removeChild(uiElement.firstChildElement("includehints")); // and ensure images and connection are at the end if (!uiElement.firstChildElement("connections").isNull()) uiElement.insertAfter(uiElement.firstChildElement("connections"), QDomNode()); if (!uiElement.firstChildElement("images").isNull()) uiElement.insertAfter(uiElement.firstChildElement("images"), QDomNode()); } void FormIO::loadWidget(Container *container, const QDomElement &el, QWidget *parent) { bool resetCurrentForm = false; if (!m_currentForm) { // pasting widget resetCurrentForm = true; m_currentForm = container->form(); } // We first look for the widget's name QString wname; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ( n.toElement().tagName() == "property" && nameAttribute(n.toElement()) == "name" /* compat with 1.x */) { wname = n.toElement().text(); break; } } ObjectTreeItem *item = container->form()->objectTree()->lookup(wname); if (item) { kWarning() << "Widget" << wname << "already exists! Skipping..."; return; } QByteArray classname, alternate; // We translate some name (for compatibility) if (el.tagName() == "spacer") classname = "Spring"; else if (el.attribute("class") == "QLayoutWidget") { for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { QString tagName = n.toElement().tagName(); if (tagName == "property") continue; if (tagName == "hbox") classname = "HBox"; else if (tagName == "vbox") classname = "VBox"; else if (tagName == "grid") { // first, see if it is flow layout for (QDomNode child = n.firstChild(); !child.isNull(); child = child.nextSibling()) { if ( child.toElement().tagName() == "property" && nameAttribute(child.toElement()) == "customLayout") { classname = child.toElement().text().toLatin1(); break; } } if (classname.isEmpty()) // normal grid classname = "Grid"; } } } else { // check if this classname is an alternate one, and replace it if necessary classname = el.attribute("class").toLatin1(); alternate = container->form()->library()->classNameForAlternate(classname); } QWidget *w; if (alternate == "CustomWidget") { w = new CustomWidget(classname, container->widget()); w->setObjectName(wname); } else { if (!alternate.isEmpty()) { classname = alternate; } WidgetFactory::CreateWidgetOptions widgetOptions = WidgetFactory::DefaultOptions; if (container->form()->mode() != Form::DesignMode) { widgetOptions ^= WidgetFactory::DesignViewMode; } if (!parent) w = container->form()->library()->createWidget(classname, container->widget(), wname.toLatin1(), container, widgetOptions); else w = container->form()->library()->createWidget(classname, parent, wname.toLatin1(), container, widgetOptions); } if (!w) return; //! @todo allow setting this for data view mode as well if (m_currentForm->mode() == Form::DesignMode) { //don't generate accelerators for widgets in design mode KAcceleratorManager::setNoAccel(w); } //2.0??? w->setStyle(container->widget()->style()); w->show(); // We create and insert the ObjectTreeItem at the good place in the ObjectTree item = container->form()->objectTree()->lookup(wname); kDebug() << wname << item << classname << (parent ? parent->objectName() : QString()); if (!item) { // not yet created kDebug() << "Creating ObjectTreeItem:"; item = new ObjectTreeItem(container->form()->library()->displayName(classname), wname, w, container); if (parent) { ObjectTreeItem *titem = container->form()->objectTree()->lookup(parent->objectName()); if (titem) container->form()->objectTree()->addItem(titem, item); else kDebug() << "FORMIO :: ERROR no parent widget "; } else container->form()->objectTree()->addItem(container->objectTree(), item); } //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface //(e.g. KexiDBAutoField) if (container->form()->mode() == Form::DesignMode && dynamic_cast(w)) { dynamic_cast(w)->assignItem(item); } m_currentItem = item; // if we are inside a Grid, we need to insert the widget in the good cell if (container->layoutType() == Form::Grid) { QGridLayout *layout = (QGridLayout*)container->layout(); if (el.hasAttribute("rowspan")) { // widget spans multiple cells if (layout) { layout->addWidget( w, el.attribute("row").toInt(), el.attribute("column").toInt(), el.attribute("rowspan").toInt(), el.attribute("colspan").toInt()); //! @todo alignment attribute? } item->setGridPos(el.attribute("row").toInt(), el.attribute("column").toInt(), el.attribute("rowspan").toInt(), el.attribute("colspan").toInt()); } else { if (layout) { layout->addWidget(w, el.attribute("row").toInt(), el.attribute("column").toInt()); } item->setGridPos(el.attribute("row").toInt(), el.attribute("column").toInt(), 0, 0); } } else if (container->layout()) container->layout()->addWidget(w); readChildNodes(item, container, el, w); if (item->container() && item->container()->layout()) item->container()->layout()->activate(); // add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later const QList autoSaveProperties( container->form()->library()->autoSaveProperties(w->metaObject()->className()) ); KFormDesigner::WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(w); QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w; foreach (const QByteArray &propName, autoSaveProperties) { if (subwidget && -1 != subwidget->metaObject()->indexOfProperty(propName)) { item->addModifiedProperty(propName, subwidget->property(propName)); } } if (resetCurrentForm) m_currentForm = 0; m_currentItem = 0; } void FormIO::createToplevelWidget(Form *form, QWidget *container, QDomElement &el) { // We first look for the widget's name QString wname; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ( n.toElement().tagName() == "property" && nameAttribute(n.toElement()) == "name" /* compat with 1.x */) { wname = n.toElement().text(); break; } } // And rename the widget and its ObjectTreeItem container->setObjectName(wname); if (form->objectTree()) form->objectTree()->rename(form->objectTree()->name(), wname); form->setInteractiveMode(false); QHash *oldBuddies = 0; if (m_buddies) // save old buddies (for subforms) oldBuddies = m_buddies; m_buddies = new QHash(); m_currentItem = form->objectTree(); readChildNodes(form->objectTree(), form->toplevelContainer(), el, container); // Now the Form is fully loaded, we can assign the buddies for (QHash::ConstIterator it(m_buddies->constBegin()); it!=m_buddies->constEnd(); ++it) { ObjectTreeItem *item = form->objectTree()->lookup(it.key()); if (!item || !item->widget()) { kDebug() << "Cannot assign buddy for widget " << it.value()->objectName() << " to " << it.key(); continue; } it.value()->setBuddy(item->widget()); } delete m_buddies; m_buddies = oldBuddies; // and restore it m_currentItem = 0; form->setInteractiveMode(true); } void FormIO::readChildNodes(ObjectTreeItem *item, Container *container, const QDomElement &el, QWidget *w) { QString eltag = el.tagName(); WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(w); QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { QString tag = n.toElement().tagName(); QDomElement node = n.toElement(); if ((tag == "property") || (tag == "attribute")) { const QString name( node.attribute("name") ); const bool isQt3NameProperty = name == QLatin1String("name"); //if(name == "geometry") // hasGeometryProp = true; if ( (eltag == "grid" || eltag == "hbox" || eltag == "vbox") && (isQt3NameProperty || name == "objectName") ) { // we don't care about layout names continue; } if (node.attribute("subwidget") == "true") { //this is property for subwidget: remember it for delayed setting //because now the subwidget could be not created yet (true e.g. for KexiDBAutoField) item->addSubproperty(name.toLatin1(), readPropertyValue(node.firstChild(), w, name)); const QVariant val(readPropertyValue(node.firstChild(), w, name)); kDebug() << val.toStringList(); item->addSubproperty(name.toLatin1(), val); //subwidget->setProperty(name.toLatin1(), val); item->addModifiedProperty(name.toLatin1(), val); continue; } // We cannot assign the buddy now as the buddy widget may not be created yet if (name == "buddy") { m_buddies->insert(readPropertyValue(node.firstChild(), w, name).toString(), (QLabel*)w); } else if ( (eltag == "grid" || eltag == "hbox" || eltag == "vbox") && item->container() && item->container()->layout() ) { // We load the margin of a Layout if (name == "margin") { int margin = readPropertyValue(node.firstChild(), w, name).toInt(); item->container()->setLayoutMargin(margin); item->container()->layout()->setMargin(margin); } // We load the spacing of a Layout else if (name == "spacing") { int spacing = readPropertyValue(node.firstChild(), w, name).toInt(); item->container()->setLayoutSpacing(spacing); item->container()->layout()->setSpacing(spacing); } else if ((name == "justify")) { #ifdef KEXI_NO_FLOWLAYOUT #ifdef __GNUC__ #warning Port Kexi flow layout! #else #pragma WARNING( Port Kexi flow layout! ) #endif #else bool justify = readPropertyValue(node.firstChild(), w, name).toBool(); KexiFlowLayout *flow = static_cast(item->container()->layout()); if (flow) flow->setJustified(justify); #endif } } else if (name == "paletteBackgroundColor" || name == "paletteForegroundColor") { QPalette widgetPalette(w->palette()); QVariant val(readPropertyValue(node.firstChild(), w, name)); if (!val.isNull()) widgetPalette.setColor( name == "paletteBackgroundColor" ? w->backgroundRole() : w->foregroundRole(), val.value()); w->setPalette(widgetPalette); item->addModifiedProperty(name.toLatin1(), val); } else if (!isQt3NameProperty && -1 == subwidget->metaObject()->indexOfProperty(name.toLatin1())) { // If the object doesn't have this property, we let the Factory handle it (maybe a special property) if (w->metaObject()->className() == QString::fromLatin1("CustomWidget")) item->storeUnknownProperty(node); else { bool read = container->form()->library()->readSpecialProperty( w->metaObject()->className(), node, w, item); if (!read) // the factory doesn't support this property neither item->storeUnknownProperty(node); } } else { // we have a normal property, let's load it QVariant val(readPropertyValue(node.firstChild(), w, name)); if (name == "geometry" && dynamic_cast(w)) { //fix geometry if needed - this is top level form widget QRect r(val.toRect()); if (r.left() < 0) //negative X! r.moveLeft(0); if (r.top() < 0) //negative Y! r.moveTop(0); val = r; } QByteArray realName; if (isQt3NameProperty) { realName = "objectName"; } else { realName = name.toLatin1(); } subwidget->setProperty(realName, val); // int count = w->metaObject()->findProperty(name, true); // const QMetaProperty *meta = w->metaObject()->property(count, true); // if(meta && meta->isEnumType()) { // val = w->property(name.toLatin1()); //update: we want a numeric value of enum // } item->addModifiedProperty(realName, val); } } else if (tag == "widget") { // a child widget if (item->container()) // we are a Container loadWidget(item->container(), node); else loadWidget(container, node, w); } else if (tag == "spacer") { loadWidget(container, node, w); } else if (tag == "grid") { // first, see if it is flow layout QString layoutName; for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { if ( child.toElement().tagName() == "property" && child.toElement().attribute("name") == "customLayout") { layoutName = child.toElement().text(); break; } } if (layoutName == "HFlow") { #ifdef KEXI_NO_FLOWLAYOUT #ifdef __GNUC__ #warning Port Kexi flow layout! #else #pragma WARNING( Port Kexi flow layout! ) #endif #else item->container()->m_layType = Form::HFlow; KexiFlowLayout *layout = new KexiFlowLayout(item->widget()); layout->setOrientation(Qt::Horizontal); item->container()->m_layout = (QLayout*)layout; #endif } else if (layoutName == "VFlow") { #ifdef KEXI_NO_FLOWLAYOUT #ifdef __GNUC__ #warning "Port Kexi flow layout!" #endif #else item->container()->m_layType = Form::VFlow; KexiFlowLayout *layout = new KexiFlowLayout(item->widget()); layout->setOrientation(Qt::Vertical); item->container()->m_layout = (QLayout*)layout; #endif } else { // grid layout item->container()->setLayoutType(Form::Grid); QGridLayout *layout = new QGridLayout(item->widget()); item->container()->setLayout((QLayout*)layout); } readChildNodes(item, container, node, w); } else if (tag == "vbox") { item->container()->setLayoutType(Form::VBox); QVBoxLayout *layout = new QVBoxLayout(item->widget()); item->container()->setLayout((QLayout*)layout); readChildNodes(item, container, node, w); } else if (tag == "hbox") { item->container()->setLayoutType(Form::HBox); QHBoxLayout *layout = new QHBoxLayout(item->widget()); item->container()->setLayout((QLayout*)layout); readChildNodes(item, container, node, w); } else {// unknown tag, we let the Factory handle it if (w->metaObject()->className() == QString::fromLatin1("CustomWidget")) item->storeUnknownProperty(node); else { bool read = container->form()->library()->readSpecialProperty( w->metaObject()->className(), node, w, item); if (!read) // the factory doesn't suport this property neither item->storeUnknownProperty(node); } } } } ///////////////////////////////////////////////////////////////////////////// ///////////// Helper functions ////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// void FormIO::addIncludeFileName(const QString &include, QDomDocument &domDoc) { if (include.isEmpty()) return; QDomElement includes; QDomElement uiEl = domDoc.firstChildElement("UI"); if (uiEl.firstChildElement("includehints").isNull()) { includes = domDoc.createElement("includehints"); uiEl.appendChild(includes); } else { includes = uiEl.firstChildElement("includehints"); } // Check if this include has already been saved, and return if it is the case for (QDomNode n = includes.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.toElement().text() == include) return; } QDomElement includeHint = domDoc.createElement("includehint"); includes.appendChild(includeHint); QDomText includeText = domDoc.createTextNode(include); includeHint.appendChild(includeText); } QString FormIO::saveImage(QDomDocument &domDoc, const QPixmap &pixmap) { QDomElement images = domDoc.firstChildElement("images"); if (images.isNull()) { images = domDoc.createElement("images"); QDomElement ui = domDoc.firstChildElement("UI"); ui.appendChild(images); } int count = images.childNodes().count(); QDomElement image = domDoc.createElement("image"); QString name = "image" + QString::number(count); image.setAttribute("name", name); const QImage img(pixmap.toImage()); QByteArray ba; QBuffer buf(&ba); buf.open(QIODevice::WriteOnly | QIODevice::Text); const QByteArray format(img.depth() > 1 ? "XPM" : "XBM"); QImageWriter imageWriter(&buf, format); imageWriter.write(img); buf.close(); const QByteArray bazip = qCompress(ba); const int len = bazip.size(); QDomElement data = domDoc.createElement("data"); data.setAttribute("format", QString(format + ".GZ")); data.setAttribute("length", ba.size()); static const char hexchars[] = "0123456789abcdef"; QString content; for (int i = 4; i < len; i++) { uchar s = (uchar) bazip[i]; content += hexchars[s >> 4]; content += hexchars[s & 0x0f]; } data.appendChild(domDoc.createTextNode(content)); image.appendChild(data); images.appendChild(image); return name; } QPixmap FormIO::loadImage(QDomDocument domDoc, const QString& name) { QDomElement images = domDoc.firstChildElement("UI").firstChildElement("images"); if (images.isNull()) return 0; QDomElement image; for (QDomNode n = images.firstChild(); !n.isNull(); n = n.nextSibling()) { if ((n.toElement().tagName() == "image") && (n.toElement().attribute("name") == name)) { image = n.toElement(); break; } } QPixmap pix; QString data(image.firstChildElement("data").text()); const int lengthOffset = 4; int baSize = data.length() / 2 + lengthOffset; uchar *ba = new uchar[baSize]; for (int i = lengthOffset; i < baSize; ++i) { char h = data[2 * (i-lengthOffset)].toLatin1(); char l = data[2 * (i-lengthOffset) + 1].toLatin1(); uchar r = 0; if (h <= '9') r += h - '0'; else r += h - 'a' + 10; r = r << 4; if (l <= '9') r += l - '0'; else r += l - 'a' + 10; ba[i] = r; } QString format = image.firstChildElement("data").attribute("format", "PNG"); if ((format == "XPM.GZ") || (format == "XBM.GZ")) { int len = image.attribute("length").toInt(); if (len < data.length() * 5) len = data.length() * 5; // qUncompress() expects the first 4 bytes to be the expected length of // the uncompressed data ba[0] = (len & 0xff000000) >> 24; ba[1] = (len & 0x00ff0000) >> 16; ba[2] = (len & 0x0000ff00) >> 8; ba[3] = (len & 0x000000ff); QByteArray baunzip = qUncompress(ba, baSize); pix.loadFromData((const uchar*)baunzip.data(), baunzip.size(), format.left(format.indexOf('.')).toLatin1()); } else pix.loadFromData((const uchar*)ba + lengthOffset, baSize - lengthOffset, format.toLatin1()); delete[] ba; return pix; } #include "formIO.moc" diff --git a/kexi/formeditor/utils.cpp b/kexi/formeditor/utils.cpp index 4b4974b3dd0..6d95a881154 100644 --- a/kexi/formeditor/utils.cpp +++ b/kexi/formeditor/utils.cpp @@ -1,318 +1,318 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2007-2009 JarosÅ‚aw Staniek 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 #include #include #include #include #include #include #include #include #include #include #include "form.h" #include "formIO.h" #include "objecttree.h" #include "utils.h" using namespace KFormDesigner; void KFormDesigner::removeChildrenFromList(QWidgetList &list) { QSet toRemove; foreach (QWidget *w, list) { // If any widget in the list is a child of this widget, we remove it from the list foreach (QWidget *widg, list) { if ((w != widg) && (w->findChild(widg->objectName()))) { kDebug() << "Removing the widget " << widg->objectName() << "which is a child of " << w->objectName(); toRemove.insert(widg); } } } QSet all(list.toSet()); all.subtract(toRemove); list = all.toList(); } void KFormDesigner::setRecursiveCursor(QWidget *w, Form *form) { ObjectTreeItem *tree = form->objectTree()->lookup(w->objectName()); if (tree && ((tree->modifiedProperties()->contains("cursor")) || !tree->children()->isEmpty()) && !w->inherits("QLineEdit") && !w->inherits("QTextEdit") ) //fix weird behaviour return; // if the user has set a cursor for this widget or this is a container, don't change it //2.0 if (w->testAttribute(Qt::WA_SetCursor)) w->setCursor(Qt::ArrowCursor); const QList list(w->findChildren()); foreach(QWidget *widget, list) { widget->setCursor(Qt::ArrowCursor); } } QSize KFormDesigner::getSizeFromChildren(QWidget *w, const char *inheritClass) { int tmpw = 0, tmph = 0; const QList list(w->findChildren()); foreach(QWidget *widget, list) { if (widget->inherits(inheritClass)) { tmpw = qMax(tmpw, widget->geometry().right()); tmph = qMax(tmph, widget->geometry().bottom()); } } return QSize(tmpw, tmph) + QSize(10, 10); } // ----------------- class HorizontalWidgetList::LessThan { public: LessThan(QWidget *topLevelWidget) : m_topLevelWidget(topLevelWidget) { } bool operator()(QWidget *w1, QWidget *w2) { return w1->mapTo(m_topLevelWidget, QPoint(0, 0)).x() - w2->mapTo(m_topLevelWidget, QPoint(0, 0)).x(); } QWidget *m_topLevelWidget; }; HorizontalWidgetList::HorizontalWidgetList(QWidget *topLevelWidget) : CustomSortableWidgetList() , m_lessThan(new LessThan(topLevelWidget)) { } HorizontalWidgetList::HorizontalWidgetList(const HorizontalWidgetList& list) : CustomSortableWidgetList(list) , m_lessThan(new LessThan(list.m_lessThan->m_topLevelWidget)) { } HorizontalWidgetList::~HorizontalWidgetList() { delete m_lessThan; } void HorizontalWidgetList::sort() { qSort(begin(), end(), *m_lessThan); } // ----------------- class VerticalWidgetList::LessThan { public: LessThan(QWidget *topLevelWidget) : m_topLevelWidget(topLevelWidget) { } bool operator()(QWidget *w1, QWidget *w2) { int y1, y2; QObject *page1 = 0; TabWidget *tw1 = KFormDesigner::findParent( w1, "KFormDesigner::TabWidget", page1); if (tw1) // special case y1 = w1->mapTo(m_topLevelWidget, QPoint(0, 0)).y() + tw1->tabBarHeight() - 2 - 2; else y1 = w1->mapTo(m_topLevelWidget, QPoint(0, 0)).y(); QObject *page2 = 0; TabWidget *tw2 = KFormDesigner::findParent( w2, "KFormDesigner::TabWidget", page2); if (tw1 && tw2 && tw1 == tw2 && page1 != page2) { // this sorts widgets by tabs there're put in return tw1->indexOf(static_cast(page1)) < tw2->indexOf(static_cast(page2)); } if (tw2) // special case y2 = w2->mapTo(m_topLevelWidget, QPoint(0, 0)).y() + tw2->tabBarHeight() - 2 - 2; else y2 = w2->mapTo(m_topLevelWidget, QPoint(0, 0)).y(); kDebug() << w1->objectName() << ": " << y1 << " " << " | " << w2->objectName() << ": " << y2; //kDebug() << w1->name() << ": " << w1->mapTo(m_topLevelWidget, QPoint(0,0)) << " " << w1->y() //<< " | " << w2->name() << ":" /*<< w2->mapFrom(m_topLevelWidget, QPoint(0,w2->y()))*/ << " " << w2->y(); return y1 < y2; } QWidget *m_topLevelWidget; }; VerticalWidgetList::VerticalWidgetList(QWidget *topLevelWidget) : CustomSortableWidgetList() , m_lessThan(new LessThan(topLevelWidget)) { } VerticalWidgetList::VerticalWidgetList(const VerticalWidgetList& list) : CustomSortableWidgetList(list) , m_lessThan(new LessThan(list.m_lessThan->m_topLevelWidget)) { } VerticalWidgetList::~VerticalWidgetList() { delete m_lessThan; } void VerticalWidgetList::sort() { qSort(begin(), end(), *m_lessThan); } // ---- QMimeData *KFormDesigner::deepCopyOfClipboardData() { //QClipboard *cb = QApplication::clipboard(); QMimeData *data = new QMimeData(); foreach(const QString& format, data->formats()) { data->setData(format, data->data(format)); } return data; } void KFormDesigner::copyToClipboard(const QString& xml) { kDebug() << xml; QMimeData *data = new QMimeData(); data->setText(xml); data->setData(KFormDesigner::mimeType(), xml.toUtf8()); QClipboard *cb = QApplication::clipboard(); cb->setMimeData(data); } void KFormDesigner::widgetsToXML(QDomDocument& doc, QHash& containers, QHash& parents, const Form& form, const QWidgetList &list) { containers.clear(); parents.clear(); doc = QDomDocument("UI"); doc.appendChild(doc.createElement("UI")); QDomElement parent = doc.firstChildElement("UI"); QWidgetList topLevelList(list); KFormDesigner::removeChildrenFromList(topLevelList); foreach (QWidget *w, topLevelList) { ObjectTreeItem *item = form.objectTree()->lookup(w->objectName()); if (!item) return; Container *c = form.parentContainer(item->widget()); if (!c) return; // We need to store both parentContainer and parentWidget as they may be different (eg for TabWidget page) containers.insert( item->name().toLatin1(), - c->widget()->objectName().toLatin1().constData() + c->widget()->objectName().toLatin1() ); parents.insert( item->name().toLatin1(), item->parent()->name().toLatin1() ); FormIO::saveWidget(item, parent, doc); #ifdef KFD_SIGSLOTS form.connectionBuffer()->saveAllConnectionsForWidget( item->widget()->objectName(), doc); #endif } FormIO::cleanClipboard(parent); } //----------------------------- class ActionGroup::Private { public: Private() {} QHash actions; }; ActionGroup::ActionGroup( QObject * parent ) : QActionGroup(parent) , d( new Private ) { } ActionGroup::~ActionGroup() { delete d; } void ActionGroup::addAction(QAction* action) { QActionGroup::addAction(action); d->actions.insert(action->objectName(), action); } QAction *ActionGroup::action(const QString& name) const { return d->actions.value(name); } //----------------------------- int KFormDesigner::alignValueToGrid(int value, int gridSize) { if ((value % gridSize * 2) < gridSize) return value / gridSize * gridSize; return (value / gridSize + 1) * gridSize; } //----------------------------- void KFormDesigner::paintWidgetFrame(QPainter& p, const QRect& geometry) { QColor c1(Qt::white); c1.setAlpha(100); QColor c2(Qt::black); c2.setAlpha(100); QRect r(geometry); r.setWidth(r.width()-1); r.setHeight(r.height()-1); QPen pen1(c1, 1, Qt::DashLine); QPen pen2(c2, 1, Qt::DashLine); p.setPen(pen1); p.drawRect(r); p.setPen(pen2); p.drawRect(r); } #include "utils.moc" diff --git a/kexi/kexidb/drivers/odbc/odbcdriver.cpp b/kexi/kexidb/drivers/odbc/odbcdriver.cpp index 98e77f1ba87..9706335bd43 100644 --- a/kexi/kexidb/drivers/odbc/odbcdriver.cpp +++ b/kexi/kexidb/drivers/odbc/odbcdriver.cpp @@ -1,489 +1,489 @@ /* This file is part of the KDE project Copyright (C) 2009 Sharan Rao This program 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 program 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 program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "odbcdriver.h" #include "odbccursor.h" #include "odbcconnection.h" #include "odbcconnection_p.h" #include "odbctypeinfoqueryunit.h" #include "odbcspecialcolumnsqueryunit.h" #include #include #include #include #include #include #include #include using namespace KexiDB; class ODBCDriver::ODBCDatabaseProperties { public: /** * Returns the single instance of this class */ static ODBCDatabaseProperties* getInstance(); /** * Returns the behaviour structure for the given dbname, else returns null * \param dbName The name of the DBMS for which the bevaiour needs to be retrieved * \return The value of the driver behaviour for the given dbName, propName */ DriverBehaviour* driverBehaviour(const QByteArray& dbName); ~ODBCDatabaseProperties(); protected: ODBCDatabaseProperties(); /** * Stores behaviour for each database */ QHash m_databaseBehaviourHash; static ODBCDatabaseProperties* s_instance; }; ODBCDriver::ODBCDatabaseProperties* ODBCDriver::ODBCDatabaseProperties::s_instance = 0; ODBCDriver::ODBCDatabaseProperties* ODBCDriver::ODBCDatabaseProperties::getInstance() { if ( !s_instance ) { s_instance = new ODBCDatabaseProperties; } return s_instance; } ODBCDriver::ODBCDatabaseProperties::~ODBCDatabaseProperties() { QHashIterator i(m_databaseBehaviourHash); while (i.hasNext()) { i.next(); delete i.value(); } } DriverBehaviour* ODBCDriver::ODBCDatabaseProperties::driverBehaviour(const QByteArray& dbName) { QHash::iterator it = m_databaseBehaviourHash.find( dbName.toLower() ); if ( it == m_databaseBehaviourHash.end() ) { return 0; } return it.value(); } ODBCDriver::ODBCDatabaseProperties::ODBCDatabaseProperties() { // fill the database properties map with whatever we know // for MySQL DriverBehaviour* mysqlBehaviour = new DriverBehaviour; mysqlBehaviour->ROW_ID_FIELD_NAME = "LAST_INSERT_ID()"; mysqlBehaviour->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE = true; m_databaseBehaviourHash.insert( "mysql", mysqlBehaviour ); // insert mysql entry into hash // for pg DriverBehaviour* pgBehaviour = new DriverBehaviour; pgBehaviour->UNSIGNED_TYPE_KEYWORD = ""; pgBehaviour->ROW_ID_FIELD_NAME = "oid"; pgBehaviour->SPECIAL_AUTO_INCREMENT_DEF = false; pgBehaviour->AUTO_INCREMENT_TYPE = "SERIAL"; pgBehaviour->AUTO_INCREMENT_FIELD_OPTION = ""; pgBehaviour->AUTO_INCREMENT_PK_FIELD_OPTION = "PRIMARY KEY"; m_databaseBehaviourHash.insert( "postgresql", pgBehaviour ); // insert pg entry into hash // for sybase DriverBehaviour* sybaseBehaviour = new DriverBehaviour; sybaseBehaviour->ROW_ID_FIELD_NAME = "@@IDENTITY"; sybaseBehaviour->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE = true ; // for Sybase ASA this field is "DEFAULT AUTOINCREMENT" // for MSSQL and Sybase ASE it's IDENTITY sybaseBehaviour->AUTO_INCREMENT_FIELD_OPTION = "IDENTITY"; sybaseBehaviour->AUTO_INCREMENT_PK_FIELD_OPTION = "IDENTITY PRIMARY KEY "; m_databaseBehaviourHash.insert( "sybase", sybaseBehaviour ); // insert sybase behaviour into hash // for sqlite DriverBehaviour* sqliteBehaviour = new DriverBehaviour; sqliteBehaviour->SPECIAL_AUTO_INCREMENT_DEF = true; sqliteBehaviour->AUTO_INCREMENT_FIELD_OPTION = ""; //not available sqliteBehaviour->AUTO_INCREMENT_TYPE = "INTEGER"; sqliteBehaviour->AUTO_INCREMENT_PK_FIELD_OPTION = "PRIMARY KEY"; sqliteBehaviour->AUTO_INCREMENT_REQUIRES_PK = true; sqliteBehaviour->ROW_ID_FIELD_NAME = "OID"; m_databaseBehaviourHash.insert( "sqlite", sqliteBehaviour ); // insert sqlite behaviour into hash // for oracle DriverBehaviour* oracleBehaviour = new DriverBehaviour; oracleBehaviour->ROW_ID_FIELD_NAME="ROW_ID"; // oracleBehaviour->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE=true; I don't think so oracleBehaviour->UNSIGNED_TYPE_KEYWORD=""; //Autoincrement oracleBehaviour->SPECIAL_AUTO_INCREMENT_DEF = false; oracleBehaviour->AUTO_INCREMENT_FIELD_OPTION=""; oracleBehaviour->AUTO_INCREMENT_PK_FIELD_OPTION="PRIMARY KEY"; oracleBehaviour->AUTO_INCREMENT_TYPE = ""; m_databaseBehaviourHash.insert( "oracle", oracleBehaviour ); // insert oracle behaviour into hash // default DriverBehaviour* defaultBehaviour = new DriverBehaviour; m_databaseBehaviourHash.insert( "default", defaultBehaviour ); // insert default behaviour into hash } /*! * Constructor sets database features and * maps the types in KexiDB::Field::Type to the ODBC types. * */ ODBCDriver::ODBCDriver(QObject *parent, const QStringList &args) : Driver(parent, args), m_driverInfoUpdated( false ), o_d( ODBCDatabaseProperties::getInstance() ) { // KexiDBDrvDbg << "ODBCDriver::ODBCDriver()"; d->isFileDriver = false; d->features = IgnoreTransactions | CursorForward; // TODO beh->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY = false; beh->USING_DATABASE_REQUIRED_TO_CONNECT = false; } ODBCDriver::~ODBCDriver() { } KexiDB::Connection* ODBCDriver::drv_createConnection(ConnectionData &conn_data) { KexiDB::Connection* connection = new ODBCConnection(this, conn_data); return connection; } bool ODBCDriver::isSystemDatabaseName(const QString& name) const { // TODO. somehow query the data source to find out. return Driver::isSystemObjectName(name); } bool ODBCDriver::drv_isSystemFieldName(const QString&) const { return false; } QString ODBCDriver::escapeString(const QString& str) const { //! TODO: Find if we get any info about escaping strings return QString::fromLatin1("'") + QString(str).replace("\'", "\\''") + QString::fromLatin1("'"); } QString ODBCDriver::escapeBLOB(const QByteArray& array) const { //! TODO: Find if we get any info about escaping blobs ( wasn't there something in SQLGetInfo ? ) return KexiDB::escapeBLOB(array, KexiDB::BLOBEscape0xHex); } QByteArray ODBCDriver::escapeString(const QByteArray& str) const { //! TODO: Find if we get any info about escaping strings return QByteArray("'") + QByteArray(str) .replace("\'", "\\''") + QByteArray("'"); } /*! Add back-ticks to an identifier, and replace any back-ticks within * the name with single quotes. */ QString ODBCDriver::drv_escapeIdentifier(const QString& str) const { return str; } QByteArray ODBCDriver::drv_escapeIdentifier(const QByteArray& str) const { return str; } bool ODBCDriver::updateDriverInfo(ODBCConnection* connection ) { if ( m_driverInfoUpdated ) { return true; } // Before I forget/for future use, let me note down what's required to be done here // 1. Get the information about all the types - use the odbctypeinfoqueryunit - done // 2. Get the quotation marks for identifier - sqlgetinfo ? - done // 3. initialize the driver specific keywords - hmm - done // 4. check if select 1 subquery is supported - done // 5. Get the rowid column name - suspended // 6. Get the rowid/auto increment behavioural parameters // executes step 1 if ( !populateTypeInfo(connection) ) return false; // executes steps 2,3,4 if ( !populateGeneralInfo(connection ) ) { return false; } // execute step 5. // if ( !populateROWID( connection ) ) { // //return false; // } if ( !populateBehaviourInfo( m_dbmsName ) ) { KexiDBDrvWarn <<"Loaded default behaviour for database "<open() || !cursor->moveFirst() ) { connection->deleteCursor( cursor ); return false; } while ( !cursor->eof() ) { // get all the values QString typeName = cursor->value( 0 ).toString(); switch( cursor->value(1 ).toInt() ) { case SQL_TINYINT: setTypeName(Field::Byte, typeName); break; case SQL_SMALLINT: setTypeName(Field::ShortInteger, typeName); break; case SQL_INTEGER: setTypeName(Field::Integer, typeName); break; case SQL_BIGINT: setTypeName(Field::BigInteger, typeName); break; case SQL_BIT: setTypeName(Field::Boolean, typeName); break; case SQL_TYPE_DATE: setTypeName(Field::Date, typeName); break; case SQL_TYPE_TIMESTAMP: setTypeName(Field::DateTime, typeName); break; case SQL_TYPE_TIME: setTypeName(Field::Time, typeName); break; case SQL_FLOAT: setTypeName(Field::Float, typeName); break; case SQL_DOUBLE: setTypeName(Field::Double, typeName); break; case SQL_VARCHAR: setTypeName(Field::Text, typeName); break; case SQL_LONGVARCHAR: setTypeName(Field::LongText, typeName); break; case SQL_VARBINARY: setTypeName(Field::BLOB, typeName); break; default: break; } if (!cursor->moveNext() && cursor->error()) { connection->deleteCursor(cursor); return false; } } return connection->deleteCursor( cursor ); } bool ODBCDriver::populateROWID(ODBCConnection* connection ) { // pass true, as we want *some* table. If there are no tables which already exist // lets at least get the kexidb system tables QStringList tableNames = connection->tableNames(true); if ( tableNames.size() == 0 ) { return false; } QString tableName = tableNames.first(); // get any existing table name // the ODBC function which retrieves special columns needs some table to work on // as we are inerested only in pseudo columns like ROWID, any table would do ODBCSpecialColumnsQueryUnit* queryUnit = new ODBCSpecialColumnsQueryUnit( connection , tableName ); Cursor* cursor = new ODBCCursor( connection, queryUnit ); if ( !cursor->open() || !cursor->moveFirst() ) { connection->deleteCursor( cursor ); return false; } // we need exactly one pseudo-column. So if the pseudo column count is greater than that, quit :( unsigned int pseudoColumnCount = 0; QString columnName; while ( !cursor->eof() ) { // eigth column is the pseudo column indicator ( cursor indexes by 0, hence we use 7 ) // see http://msdn.microsoft.com/en-us/library/ms714602(VS.85).aspx if ( cursor->value( 7 ).toInt() != ( int )SQL_PC_PSEUDO ) continue; // else we have a pseudo column ! columnName = cursor->value( 0 ).toString(); pseudoColumnCount++; } if ( pseudoColumnCount == 1 ) { beh->ROW_ID_FIELD_NAME = columnName; } return true; } void ODBCDriver::setTypeName( Field::Type type, const QString& typeName) { // explaining this is a bit tricky // this is mainly done for mysql. now, mysql is, as we all know, harassing. // it returns 4 datatypes for SQL_INTEGER and some other data types // int, int unsigned, int auto_increment, int unsigned auto_increment // ( yes, all with SQL_INTEGER, decorated with other values for unsigned and autoincrement ) // if I hadn't done this the typename would have gone as int unsigned auto_increment if ( d->typeNames[type].isEmpty() ) { // the typenames vector had been allocated using resize which calls default ctor d->typeNames[type] = typeName; // for QString, which is QString() } } bool ODBCDriver::populateGeneralInfo(ODBCConnection* connection) { SQLCHAR identifierQuote[5]; SQLSMALLINT stringLen; SQLRETURN returnStatus; SQLCHAR stringVal[1024]; // get the database name returnStatus = SQLGetInfo( connection->d->connectionHandle, SQL_DBMS_NAME, stringVal, sizeof(stringVal), &stringLen ); if ( !SQL_SUCCEEDED(returnStatus) ) { ODBCConnection::extractError( connection, connection->d->connectionHandle , SQL_HANDLE_DBC ); return false; } m_dbmsName = QString::fromLatin1( ( const char* )stringVal ); // Get the quotation marks for identifier returnStatus = SQLGetInfo( connection->d->connectionHandle, SQL_IDENTIFIER_QUOTE_CHAR, identifierQuote, sizeof( identifierQuote ), &stringLen ); if ( !SQL_SUCCEEDED(returnStatus ) ) { ODBCConnection::extractError( connection, connection->d->connectionHandle, SQL_HANDLE_DBC ); return false; } beh->QUOTATION_MARKS_FOR_IDENTIFIER = QString::fromLatin1( ( const char* )identifierQuote )[0]; SQLCHAR sqlKeywords[1000]; returnStatus = SQLGetInfo( connection->d->connectionHandle, SQL_KEYWORDS, sqlKeywords, sizeof( sqlKeywords ), &stringLen ); if ( !SQL_SUCCEEDED(returnStatus ) ) { ODBCConnection::extractError( connection, connection->d->connectionHandle, SQL_HANDLE_DBC ); return false; } QStringList keywordList = QString::fromLatin1( ( const char* )sqlKeywords ).split( "," ); // Get the Keywords char** keywords; keywords = new char*[keywordList.size() + 1]; // TODO TODO convert the keywordList to a character array int i = 0; foreach( const QString& keyword, keywordList ) { - QByteArray ba = keyword.toAscii(); + QByteArray ba = keyword.toLatin1(); // we want ownership as the bytearray is going to be destroyed. // we don't want ownership as the KexiUtils::StaticSetOfStrings, is not going to delete thiss // TODO: find a solution :) keywords[i] = new char[keyword.length() + 1]; strcpy( keywords[i], ba.constData() ); i++; } keywords[i] = 0; initDriverSpecificKeywords( ( const char** )keywords); // get whether subqueries are supported. Only checking for EXISTS SQLUINTEGER subqueries; returnStatus = SQLGetInfo( connection->d->connectionHandle, SQL_SUBQUERIES, ( SQLPOINTER )&subqueries , 0 , &stringLen ); if ( !SQL_SUCCEEDED(returnStatus ) ) { ODBCConnection::extractError( connection, connection->d->connectionHandle, SQL_HANDLE_DBC ); return false; } beh->SELECT_1_SUBQUERY_SUPPORTED = ( bool )( subqueries & SQL_SQ_EXISTS ); return true; } bool ODBCDriver::populateBehaviourInfo(const QString& dbName) { bool defaultBehaviourLoaded = false; KexiDBDrvDbg <<"Loading behaviour for :"<< dbName; - DriverBehaviour* db = o_d->driverBehaviour(dbName.toAscii()); + DriverBehaviour* db = o_d->driverBehaviour(dbName.toLatin1()); if ( !db ) { db = o_d->driverBehaviour( "default" ); defaultBehaviourLoaded = true; } // unsigned keyword; beh->UNSIGNED_TYPE_KEYWORD = db->UNSIGNED_TYPE_KEYWORD; // rowid related beh->ROW_ID_FIELD_NAME = db->ROW_ID_FIELD_NAME; beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE = db->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE; // auto increment related beh->SPECIAL_AUTO_INCREMENT_DEF = db->SPECIAL_AUTO_INCREMENT_DEF; beh->AUTO_INCREMENT_TYPE = db->AUTO_INCREMENT_TYPE; beh->AUTO_INCREMENT_FIELD_OPTION = db->AUTO_INCREMENT_FIELD_OPTION; beh->AUTO_INCREMENT_PK_FIELD_OPTION = db->AUTO_INCREMENT_PK_FIELD_OPTION; beh->AUTO_INCREMENT_REQUIRES_PK = db->AUTO_INCREMENT_REQUIRES_PK; return !defaultBehaviourLoaded; } QString ODBCDriver::getQueryForOID() { // will only work for sybase/mysql if ( beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE ) return "Select " + beh->ROW_ID_FIELD_NAME; return QString(); } #if 0 // replaced by KPluginLoader::pluginVersion() // to make the compiler shut up :). DatabaseVersionInfo ODBCDriver::version() const { return KEXIDB_VERSION; } #endif #include "odbcdriver.moc" diff --git a/kexi/kexidb/drivers/odbc/odbcspecialcolumnsqueryunit.cpp b/kexi/kexidb/drivers/odbc/odbcspecialcolumnsqueryunit.cpp index 1e951f1623d..8cb9759b84d 100644 --- a/kexi/kexidb/drivers/odbc/odbcspecialcolumnsqueryunit.cpp +++ b/kexi/kexidb/drivers/odbc/odbcspecialcolumnsqueryunit.cpp @@ -1,47 +1,47 @@ /* This file is part of the KDE project Copyright (C) 2009 Sharan Rao This program 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 program 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 program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "odbcspecialcolumnsqueryunit.h" // kexi includes #include "odbcconnection_p.h" // odbc includes #include using namespace KexiDB; ODBCSpecialColumnsQueryUnit::ODBCSpecialColumnsQueryUnit( QObject* parent , const QString& tableName) : ODBCQueryUnit( parent ), m_tableName( tableName ) { } SQLRETURN ODBCSpecialColumnsQueryUnit::execute() { - QByteArray ba = m_tableName.toAscii(); + QByteArray ba = m_tableName.toLatin1(); const char* tableName = ba.constData(); return SQLSpecialColumns(m_cursorData->statementHandle, SQL_BEST_ROWID, NULL, 0, // catalog name NULL, 0, // schema name (SQLCHAR* )tableName , m_tableName.length(), // table name SQL_SCOPE_SESSION, // scope of the special column SQL_NO_NULLS // nullable ); } diff --git a/kexi/kexidb/drivers/odbc/odbctablesqueryunit.cpp b/kexi/kexidb/drivers/odbc/odbctablesqueryunit.cpp index 6f9f3ffb0c9..68f67098c26 100644 --- a/kexi/kexidb/drivers/odbc/odbctablesqueryunit.cpp +++ b/kexi/kexidb/drivers/odbc/odbctablesqueryunit.cpp @@ -1,52 +1,52 @@ /* This file is part of the KDE project Copyright (C) 2009 Sharan Rao This program 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 program 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 program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "odbctablesqueryunit.h" // kexi includes #include "odbcconnection_p.h" using namespace KexiDB; ODBCTablesQueryUnit::ODBCTablesQueryUnit( QObject* parent ) : ODBCQueryUnit( parent ), m_tablesFilter( QString() ) { } void ODBCTablesQueryUnit::setFilter( const QString& filter ) { m_tablesFilter = filter; } SQLRETURN ODBCTablesQueryUnit::execute() { QByteArray tableNameBA; const char* tableName = 0; if ( !m_tablesFilter.isEmpty() ) { - tableNameBA = m_tablesFilter.toAscii(); + tableNameBA = m_tablesFilter.toLatin1(); tableName = tableNameBA.constData(); } return SQLTables(m_cursorData->statementHandle, NULL, 0, // catalog name NULL, 0, // schema name (SQLCHAR* )tableName , tableName? m_tablesFilter.length() : 0, // table name (SQLCHAR*)"TABLE", SQL_NTS ); } diff --git a/kexi/kexidb/drivers/pqxx/pqxxdriver.cpp b/kexi/kexidb/drivers/pqxx/pqxxdriver.cpp index 8a27c7383a2..d3fc3cf49e2 100644 --- a/kexi/kexidb/drivers/pqxx/pqxxdriver.cpp +++ b/kexi/kexidb/drivers/pqxx/pqxxdriver.cpp @@ -1,205 +1,205 @@ /* This file is part of the KDE project Copyright (C) 2003 Adam Pigg This program 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 program 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 program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include "pqxxdriver.h" #include "pqxxconnection.h" #include #include using namespace KexiDB; K_EXPORT_KEXIDB_DRIVER(pqxxSqlDriver, "pqxxsql") //================================================================================== // pqxxSqlDriver::pqxxSqlDriver(QObject *parent, const QVariantList &args) : Driver(parent, args) { d->isFileDriver = false; d->features = SingleTransactions | CursorForward | CursorBackward; //! @todo enable this when kexidb supports multiple: d->features = MultipleTransactions | CursorForward | CursorBackward; beh->UNSIGNED_TYPE_KEYWORD = ""; beh->ROW_ID_FIELD_NAME = "oid"; beh->SPECIAL_AUTO_INCREMENT_DEF = false; beh->AUTO_INCREMENT_TYPE = "SERIAL"; beh->AUTO_INCREMENT_FIELD_OPTION = ""; beh->AUTO_INCREMENT_PK_FIELD_OPTION = "PRIMARY KEY"; beh->ALWAYS_AVAILABLE_DATABASE_NAME = "template1"; beh->QUOTATION_MARKS_FOR_IDENTIFIER = '"'; initDriverSpecificKeywords(keywords); //predefined properties d->properties["client_library_version"] = "";//TODO d->properties["default_server_encoding"] = ""; //TODO d->typeNames[Field::Byte] = "SMALLINT"; d->typeNames[Field::ShortInteger] = "SMALLINT"; d->typeNames[Field::Integer] = "INTEGER"; d->typeNames[Field::BigInteger] = "BIGINT"; d->typeNames[Field::Boolean] = "BOOLEAN"; d->typeNames[Field::Date] = "DATE"; d->typeNames[Field::DateTime] = "TIMESTAMP"; d->typeNames[Field::Time] = "TIME"; d->typeNames[Field::Float] = "REAL"; d->typeNames[Field::Double] = "DOUBLE PRECISION"; d->typeNames[Field::Text] = "CHARACTER VARYING"; d->typeNames[Field::LongText] = "TEXT"; d->typeNames[Field::BLOB] = "BYTEA"; //_internalWork = new pqxx::nontransaction(_internalConn); } //================================================================================== //Override the default implementation to allow for NUMERIC type natively QString pqxxSqlDriver::sqlTypeName(int id_t, int p) const { if (id_t == Field::Null) return "NULL"; if (id_t == Field::Float || id_t == Field::Double) { if (p > 0) { return "NUMERIC"; } else { return d->typeNames[id_t]; } } else { return d->typeNames[id_t]; } } //================================================================================== // pqxxSqlDriver::~pqxxSqlDriver() { // delete d; } //================================================================================== // KexiDB::Connection* pqxxSqlDriver::drv_createConnection(ConnectionData &conn_data) { return new pqxxSqlConnection(this, conn_data); } //================================================================================== // bool pqxxSqlDriver::isSystemObjectName(const QString& n) const { return Driver::isSystemObjectName(n); } //================================================================================== // bool pqxxSqlDriver::drv_isSystemFieldName(const QString&) const { return false; } //================================================================================== // bool pqxxSqlDriver::isSystemDatabaseName(const QString& n) const { return n.toLower() == "template1" || n.toLower() == "template0"; } //================================================================================== // QString pqxxSqlDriver::escapeString(const QString& str) const { //Cannot use pqxx or libpq escape functions as they require a db connection //to escape using the char encoding of the database //see http://www.postgresql.org/docs/8.1/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING /* return QString::fromLatin1("'") - + QString::fromAscii(_internalWork->esc(std::string(str.toAscii().constData())).c_str()) + + QString::fromLatin1(_internalWork->esc(std::string(str.toLatin1().constData())).c_str()) + QString::fromLatin1("'"); */ //TODO Optimize // return QString::fromLatin1("'") + QString(str) /*.replace('\\', "\\\\")*/ // .replace('\'', "\\''") // .replace('"', "\\\"") // + QString::fromLatin1("'"); return QString::fromLatin1("E'") + QString(str).replace("'", "\"\"").replace("\\", "\\\\") + QString::fromLatin1("'"); } //================================================================================== // QByteArray pqxxSqlDriver::escapeString(const QByteArray& str) const { //Cannot use pqxx or libpq escape functions as they require a db connection //to escape using the char encoding of the database //see http://www.postgresql.org/docs/8.1/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING /* return QByteArray("'") + QByteArray(_internalWork->esc(str).c_str()) + QByteArray("'");*/ // return QByteArray("'") + QByteArray(str) /*.replace('\\', "\\\\")*/ // .replace('\'', "\\''") // .replace('"', "\\\"") // + QByteArray("'"); return QByteArray("E'") + QByteArray(str).replace("'", "\"\"").replace("\\", "\\\\") + QByteArray("'"); } //================================================================================== // QString pqxxSqlDriver::drv_escapeIdentifier(const QString& str) const { return QByteArray(str.toLatin1()).replace('"', "\"\""); } //================================================================================== // QByteArray pqxxSqlDriver::drv_escapeIdentifier(const QByteArray& str) const { return QByteArray(str).replace('"', "\"\""); } //================================================================================== // QString pqxxSqlDriver::escapeBLOB(const QByteArray& array) const { return KexiDB::escapeBLOB(array, KexiDB::BLOBEscapeOctal); } QString pqxxSqlDriver::valueToSQL(uint ftype, const QVariant& v) const { if (ftype == Field::Boolean) { // use SQL compliant TRUE or FALSE as described here // http://www.postgresql.org/docs/8.0/interactive/datatype-boolean.html // 1 or 0 does not work return v.toInt() == 0 ? QString::fromLatin1("FALSE") : QString::fromLatin1("TRUE"); } return Driver::valueToSQL(ftype, v); } #include "pqxxdriver.moc" diff --git a/kexi/kexidb/drivers/sybase/sybaseconnection.cpp b/kexi/kexidb/drivers/sybase/sybaseconnection.cpp index 27d8b19f077..71a9a474a94 100644 --- a/kexi/kexidb/drivers/sybase/sybaseconnection.cpp +++ b/kexi/kexidb/drivers/sybase/sybaseconnection.cpp @@ -1,238 +1,238 @@ /* This file is part of the KDE project Copyright (C) 2007 Sharan Rao This program 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 program 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 program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include "sybasedriver.h" #include "sybaseconnection.h" #include "sybaseconnection_p.h" #include "sybasecursor.h" #include "sybasepreparedstatement.h" #include using namespace KexiDB; //-------------------------------------------------------------------------- SybaseConnection::SybaseConnection(Driver *driver, ConnectionData &conn_data) : Connection(driver, conn_data) , d(new SybaseConnectionInternal(this)) { } SybaseConnection::~SybaseConnection() { destroy(); } bool SybaseConnection::drv_connect(KexiDB::ServerVersionInfo& version) { const bool ok = d->db_connect(*data()); if (!ok) return false; // we can retrieve the server name and the server version using global variables // @@servername // @@version QString serverVersionString; if (!querySingleString("Select @@servername" , version.string)) { KexiDBDrvDbg << "Couldn't fetch server name"; } if (!querySingleString("Select @@version", serverVersionString)) { KexiDBDrvDbg << "Couldn't fetch server version"; } QRegExp versionRe("(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)"); if (versionRe.exactMatch(serverVersionString)) { version.major = versionRe.cap(1).toInt(); version.minor = versionRe.cap(2).toInt(); version.release = versionRe.cap(3).toInt(); } return true; } bool SybaseConnection::drv_disconnect() { return d->db_disconnect(); } Cursor* SybaseConnection::prepareQuery(const QString& statement, uint cursor_options) { return new SybaseCursor(this, statement, cursor_options); } Cursor* SybaseConnection::prepareQuery(QuerySchema& query, uint cursor_options) { return new SybaseCursor(this, query, cursor_options); } bool SybaseConnection::drv_getDatabasesList(QStringList &list) { KexiDBDrvDbg << "SybaseConnection::drv_getDatabasesList()"; // select * from master..sysdatabases ? // todo: verify. return queryStringList("Select name from master..sysdatabases", list) ; } bool SybaseConnection::drv_createDatabase(const QString &dbName) { KexiDBDrvDbg << "SybaseConnection::drv_createDatabase: " << dbName; // mysql_create_db deprecated, use SQL here. if (drv_executeSQL("CREATE DATABASE " + dbName)) { // set allow_nulls_by_default option to true QString allowNullsQuery = QString("sp_dboption %1, allow_nulls_by_default, true").arg(dbName); - if (drv_executeSQL(allowNullsQuery.toLatin1().data())) + if (drv_executeSQL(allowNullsQuery)) return true; } d->storeResult(); return false; } bool SybaseConnection::drv_useDatabase(const QString &dbName, bool *cancelled, MessageHandler* msgHandler) { Q_UNUSED(cancelled); Q_UNUSED(msgHandler); //TODO is here escaping needed? return d->useDatabase(dbName) ; } bool SybaseConnection::drv_closeDatabase() { // here we disconenct the connection return true; } bool SybaseConnection::drv_dropDatabase(const QString &dbName) { return drv_executeSQL("drop database " + driver()->escapeString(dbName)); } bool SybaseConnection::drv_executeSQL(const QString& statement) { return d->executeSQL(statement); } quint64 SybaseConnection::drv_lastInsertRowID() { int rowId; querySingleNumber("Select @@IDENTITY", rowId); return (qint64)rowId; } int SybaseConnection::serverResult() { return d->res; } QString SybaseConnection::serverResultName() { return QString(); } void SybaseConnection::drv_clearServerResult() { if (!d) return; d->res = 0; } QString SybaseConnection::serverErrorMsg() { return d->errmsg; } bool SybaseConnection::drv_containsTable(const QString &tableName) { bool success=false; return resultExists(QString("select name from sysobjects where type='U' and name=%1") .arg(driver()->escapeString(tableName)), success) && success; } bool SybaseConnection::drv_getTablesList(QStringList &list) { return queryStringList("Select name from sysobjects where type='U'", list); } PreparedStatement::Ptr SybaseConnection::prepareStatement(PreparedStatement::StatementType type, FieldList& fields) { return KSharedPtr(new SybasePreparedStatement(type, *d, fields)); } bool KexiDB::SybaseConnection::drv_beforeInsert(const QString& table, FieldList& fields) { if (fields.autoIncrementFields()->isEmpty()) return true; // explicit insertion into IDENTITY fields !! return drv_executeSQL(QString("SET IDENTITY_INSERT %1 ON").arg(escapeIdentifier(table))); } bool KexiDB::SybaseConnection::drv_afterInsert(const QString& table, FieldList& fields) { // should we instead just set a flag when an identity_insert has taken place and only check for that // flag here ? if (fields.autoIncrementFields()->isEmpty()) return true; // explicit insertion into IDENTITY fields has taken place. Turn off IDENTITY_INSERT return drv_executeSQL(QString("SET IDENTITY_INSERT %1 OFF").arg(escapeIdentifier(table))); } bool KexiDB::SybaseConnection::drv_beforeUpdate(const QString& table, FieldList& fields) { if (fields.autoIncrementFields()->isEmpty()) return true; // explicit update of IDENTITY fields has taken place. return drv_executeSQL(QString("SET IDENTITY_UPDATE %1 ON").arg(escapeIdentifier(table))); } bool KexiDB::SybaseConnection::drv_afterUpdate(const QString& table, FieldList& fields) { // should we instead just set a flag when an identity_update has taken place and only check for that // flag here ? if (fields.autoIncrementFields()->isEmpty()) return true; // explicit insertion into IDENTITY fields has taken place. Turn off IDENTITY_INSERT return drv_executeSQL(QString("SET IDENTITY_UPDATE %1 OFF").arg(escapeIdentifier(table))); } #include "sybaseconnection.moc" diff --git a/kexi/kexidb/drivers/xbase/xbaseexport.cpp b/kexi/kexidb/drivers/xbase/xbaseexport.cpp index 7f64a6d50a8..46301f52ef1 100644 --- a/kexi/kexidb/drivers/xbase/xbaseexport.cpp +++ b/kexi/kexidb/drivers/xbase/xbaseexport.cpp @@ -1,461 +1,461 @@ /* This file is part of the KDE project Copyright (C) 2008 Sharan Rao This program 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 program 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 program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "xbaseexport.h" #include #include #include #include #include #include #include #include #include #include #include "xbase.h" using namespace KexiDB; class KexiDB::xBaseExportPrivate { public: xBaseExportPrivate() { } //! Converts kexidb field types to xbase types char type(KexiDB::Field::Type fieldType); //! Appends record to xbase table bool appendRecord(const QString& sourceTableName , KexiDB::RecordData* recordData); //! Returns max fieldlengths for xBase table int fieldLength(KexiDB::Field* f ); //! converts QVariant data to a format understood by xBase QByteArray fieldData(QVariant data, char type); //! Creates xBase indexes for the table bool createIndexes(const QString& sourceTableName, KexiDB::TableSchema* tableSchema); xbXBase xbase; QHash tableNamePathMap; }; char xBaseExportPrivate::type(KexiDB::Field::Type fieldType) { char xBaseType = '\0'; switch( fieldType ) { case KexiDB::Field::Text: case KexiDB::Field::LongText: xBaseType = XB_CHAR_FLD; break; case KexiDB::Field::Boolean: xBaseType = XB_LOGICAL_FLD; break; case KexiDB::Field::Float: case KexiDB::Field::Double: xBaseType = XB_FLOAT_FLD; case KexiDB::Field::ShortInteger: case KexiDB::Field::Integer: case KexiDB::Field::BigInteger: xBaseType = XB_NUMERIC_FLD; break; case KexiDB::Field::DateTime: case KexiDB::Field::Date: case KexiDB::Field::Time: xBaseType = XB_DATE_FLD; break; case KexiDB::Field::BLOB: xBaseType = XB_MEMO_FLD; break; default: xBaseType = '\0'; } return xBaseType; } bool xBaseExportPrivate::appendRecord( const QString& sourceTableName , KexiDB::RecordData* recordData ) { // kDebug()<debugString(); QString pathName = tableNamePathMap.value( sourceTableName ); - QByteArray pathNameBa = pathName.toAscii(); + QByteArray pathNameBa = pathName.toLatin1(); xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() ); int returnCode; table->BlankRecord(); for (int i=0;i < recordData->size();++i) { char fieldType = table->GetFieldType(i); QByteArray stringData = fieldData(recordData->value(i), fieldType); if (fieldType == XB_MEMO_FLD) { #ifdef XB_MEMO_FIELDS // we use size()+1 as size to accommodate `\0` table->UpdateMemoData(i, stringData.size()+1, stringData.constData(), F_SETLKW ); #else kDebug()<<"XB_MEMO_FIELDS support disabled during compilation of XBase libraries"; #endif } else { if ((returnCode = table->PutField( i, stringData.constData())) != XB_NO_ERROR ) { switch(returnCode) { case XB_INVALID_FIELDNO: kDebug()<<"Invalid field number "<AppendRecord()) != XB_NO_ERROR) { kDebug() << "\nxBase Error " << returnCode << " appending data record."; return false; } // // for debugging purposes only // for ( uint i=0; i< (uint)recordData->size(); ++i ) { // kDebug()<GetField(i); // } return true; } int xBaseExportPrivate::fieldLength(KexiDB::Field* f ) { if ( f->type() == KexiDB::Field::Text ) { return f->maxLength(); } // return the max possible (string)length of the types // see http://linux.techass.com/projects/xdb/xbasedocs/xbase_c3.html switch(type( f->type())) { case XB_CHAR_FLD: return 254; case XB_LOGICAL_FLD: return 1; case XB_FLOAT_FLD: case XB_NUMERIC_FLD: return 17; case XB_DATE_FLD: return 8; case XB_MEMO_FLD: return 10; default: return 0; } } QByteArray xBaseExportPrivate::fieldData(QVariant data, char type) { switch(type) { case XB_CHAR_FLD: case XB_FLOAT_FLD: case XB_NUMERIC_FLD: return data.toString().toUtf8(); case XB_LOGICAL_FLD: if (data.toBool()) { - return QString( "t" ).toAscii(); + return QByteArray(1, 't'); } else - return QString( "f" ).toAscii(); + return QByteArray(1, 'f'); case XB_DATE_FLD: - return data.toDate().toString("yyyyMMdd").toAscii(); + return data.toDate().toString("yyyyMMdd").toLatin1(); case XB_MEMO_FLD: return data.toByteArray(); default: return QByteArray(); } } bool xBaseExportPrivate::createIndexes(const QString& sourceTableName, KexiDB::TableSchema* tableSchema) { QString pathName = tableNamePathMap.value( sourceTableName ); - QByteArray pathNameBa = pathName.toAscii(); + QByteArray pathNameBa = pathName.toLatin1(); xbDbf* table = xbase.GetDbfPtr( pathNameBa.constData() ); uint fieldCount = tableSchema->fieldCount(); QString dirName = QFileInfo( pathName ).path(); for (uint i=0; i< (uint)fieldCount ; ++i) { KexiDB::Field* f = tableSchema->field(i); int returnCode; QString fieldName = f->name(); QString indexName = dirName + QDir::separator() + sourceTableName + '_' + fieldName + ".ndx"; - QByteArray indexNameBa = indexName.toAscii(); + QByteArray indexNameBa = indexName.toLatin1(); QByteArray fieldNameBa = fieldName.toLatin1(); xbNdx index(table); if (f->isUniqueKey() || f->isPrimaryKey()) { if ((returnCode = index.CreateIndex(indexNameBa.constData(), fieldNameBa.constData(), XB_UNIQUE, XB_OVERLAY)) != XB_NO_ERROR ) { kDebug()<<"Couldn't create unique index for fieldName "<isIndexed() ) { if ((returnCode = index.CreateIndex(indexNameBa.constData(), fieldNameBa.constData(), XB_NOT_UNIQUE, XB_OVERLAY)) != XB_NO_ERROR ) { kDebug()<<"Couldn't create index for fieldName "<clearStatus(); KexiDB::DriverManager drvManager; if (!m_migrateData) { kDebug()<<"Migration Data not set yet !!"; result->setStatus(&drvManager, i18n("Data not set for migration")); return false; } KexiDB::Driver *sourceDriver = drvManager.driver( m_migrateData->source->driverName); if (!sourceDriver) { result->setStatus(&drvManager, i18n("Could not export back to destination database")); return false; } // connect to destination database if (!dest_connect()) { kDebug()<<"Couldn't connect to destination database"; if (result) result->setStatus(i18n("Could not connect to data source \"%1\".", m_migrateData->destination->connectionData()->serverInfoString()), ""); return false; } KexiDB::Connection* sourceConn = sourceDriver->createConnection(*(m_migrateData->source)); if (!sourceConn || sourceDriver->error()) { kDebug()<<"Export failed"; return false; } if (!sourceConn->connect()) { kDebug()<<"Export failed.Could not connect"; return false; } if (!sourceConn->useDatabase(m_migrateData->sourceName)) { kDebug()<<"Couldn't use database "<sourceName; return false; } QStringList tables = sourceConn->tableNames(); // Check if there are any tables if (tables.isEmpty()) { kDebug() << "There were no tables to export"; if (result) result->setStatus( i18n("No tables to export found in data source \"%1\".", m_migrateData->source->serverInfoString()), ""); return false; } tables.sort(); // -- read table schemas and create them in memory (only for non-KexiDB-compat tables) foreach (const QString& tableCaption, tables) { if (dest_isSystemObjectName( tableCaption )) { return false; } KexiDB::TableSchema *tableSchema = sourceConn->tableSchema( tableCaption ); if (!dest_createTable(tableCaption, tableSchema)) { if (result) result->setStatus(i18n("Could not create table in destination \"%1\". Error reading table \"%2\".", m_migrateData->destination->connectionData()->serverInfoString(), tableCaption), ""); return false; } if (m_migrateData->keepData) { if (!dest_copyTable(tableCaption, sourceConn, tableSchema)) { kDebug() << "Failed to copy table " << tableCaption; if (result) result->setStatus(sourceConn, i18n("Could not copy table \"%1\" to destination database.", tableCaption)); } } } if (dest_disconnect()) { bool ok = false; if (sourceConn) ok = sourceConn->disconnect(); return ok; } // Finally: error handling if (result && result->error()) result->setStatus(sourceConn, i18n("Could not export data to \"%1\".", m_migrateData->source->serverInfoString())); dest_disconnect(); if (sourceConn) { sourceConn->disconnect(); } return false; } bool xBaseExport::dest_connect() { return true; } bool xBaseExport::dest_disconnect() { QList pathNameList = d->tableNamePathMap.values(); foreach(const QString& pathName, pathNameList) { - QByteArray ba = pathName.toAscii(); + QByteArray ba = pathName.toLatin1(); xbDbf* tablePtr = d->xbase.GetDbfPtr(ba.constData()); tablePtr->CloseDatabase(); // delete tablePtr ? } return true; } bool xBaseExport::dest_createTable(const QString& originalName, KexiDB::TableSchema* tableSchema) { // Algorithm // 1. For each fields in the table schema. // 2. Create a xbSchema entry and add it to xbSchema array. // 3. End for // 4. Create table in overlay mode ( overwrite ) uint fieldCount = tableSchema->fieldCount(); const int arrayLength = fieldCount + 1; // and extra space for the `null` xbSchema xBaseTableSchema[arrayLength];// = new xbSchema[fieldCount+1][4]; uint i = 0; for (i = 0; i < fieldCount ; ++i) { KexiDB::Field* f = tableSchema->field(i); QByteArray ba = f->name().toLatin1(); //! TODO Fieldname can only be 11 characters - strcpy(xBaseTableSchema[i].FieldName, ba.data()); + strcpy(xBaseTableSchema[i].FieldName, ba); xBaseTableSchema[i].Type = d->type(f->type()); xBaseTableSchema[i].FieldLen = d->fieldLength( f ); //! TODO Check semantics xBaseTableSchema[i].NoOfDecs = ( xBaseTableSchema[i].Type != XB_CHAR_FLD )? f->scale() : 0 ; } // last member should be all 0 strcpy( xBaseTableSchema[i].FieldName , "" ); xBaseTableSchema[i].Type = 0; xBaseTableSchema[i].FieldLen = 0; xBaseTableSchema[i].NoOfDecs = 0; const KexiDB::ConnectionData* connData = m_migrateData->destination->connectionData(); QString dirName = connData->fileName(); // this includes the forward slash after the dir name QString pathName = dirName + originalName + ".dbf"; d->tableNamePathMap[originalName] = pathName; - QByteArray pathNameBa = pathName.toAscii(); + QByteArray pathNameBa = pathName.toLatin1(); xbDbf* xBaseTable = new xbDbf( &d->xbase ); xBaseTable->SetVersion( 4 ); // create dbase IV style files xbShort returnCode; if (( returnCode = xBaseTable->CreateDatabase( pathNameBa.constData() , xBaseTableSchema, XB_OVERLAY )) != XB_NO_ERROR ) { kDebug()<<"Error creating table "<createIndexes(originalName, tableSchema)) { return false; } return true; } bool xBaseExport::dest_copyTable(const QString& srcTableName, KexiDB::Connection *srcConn, KexiDB::TableSchema* /*srcTable*/) { // Algorithm // 1. pick each row // 2. Insert it into the xBase table // using the tableSchema as argument automatically appends rowid // info to the recordData which we don't want. Hence we use SQL query KexiDB::Cursor* cursor = srcConn->executeQuery(QString( "Select * from %1" ).arg(srcTableName)); if (!cursor) return false; if (!cursor->moveFirst() && cursor->error()) return false; while (!cursor->eof()) { KexiDB::RecordData *record = cursor->storeCurrentRow(); if (!record) { return false; } if (!d->appendRecord(srcTableName, record)) { kDebug()<<"Couldn't append record"; return false; } if (!cursor->moveNext() && cursor->error()) { return false; } } return true; } bool xBaseExport::dest_isSystemObjectName( const QString& /* objectName */ ) { return false; } diff --git a/kexi/tests/newapi/parser_test.h b/kexi/tests/newapi/parser_test.h index 2a631f8ecfa..4dab18bf61d 100644 --- a/kexi/tests/newapi/parser_test.h +++ b/kexi/tests/newapi/parser_test.h @@ -1,61 +1,61 @@ /* This file is part of the KDE project Copyright (C) 2003-2004 JarosÅ‚aw Staniek 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. */ #ifndef PARSER_TEST_H #define PARSER_TEST_H int parserTest(const QString &st, const QStringList ¶ms) { int r = 0; if (!conn->useDatabase(db_name)) { conn->debugError(); return 1; } KexiDB::Parser parser(conn); const bool ok = parser.parse(st); KexiDB::QuerySchema *q = parser.query(); QList variantParams; foreach(const QString param, params) variantParams.append(param.toLocal8Bit()); if (ok && q) { cout << q->debugString().toLatin1().constData() << '\n'; - cout << "-STATEMENT:\n" << conn->selectStatement(*q, variantParams).toLatin1().data() << '\n'; + cout << "-STATEMENT:\n" << conn->selectStatement(*q, variantParams).toLatin1().constData() << '\n'; } else { KexiDB::ParserError err = parser.error(); kDebug() << QString("Error = %1\ntype = %2\nat = %3").arg(err.error()) .arg(err.type()).arg(err.at()); r = 1; } delete q; q = 0; if (!conn->closeDatabase()) { conn->debugError(); return 1; } return r; } #endif diff --git a/krita/image/kis_count_visitor.cpp b/krita/image/kis_count_visitor.cpp index b97165b435d..4dbc77b97ae 100644 --- a/krita/image/kis_count_visitor.cpp +++ b/krita/image/kis_count_visitor.cpp @@ -1,42 +1,42 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_count_visitor.h" #include "kis_image.h" bool KisCountVisitor::inList(KisNode* node) { foreach(const QString& nodeType, m_nodeTypes) { - if (node->inherits(nodeType.toAscii())) + if (node->inherits(nodeType.toLatin1())) return true; } return false; } bool KisCountVisitor::check(KisNode * node) { if (m_nodeTypes.isEmpty() || inList(node)) { if (m_properties.isEmpty() || node->check(m_properties)) { m_count++; } } visitAll(node); return true; } diff --git a/krita/image/kis_node.cpp b/krita/image/kis_node.cpp index c3fabefc6fb..f7f6f38beb2 100644 --- a/krita/image/kis_node.cpp +++ b/krita/image/kis_node.cpp @@ -1,364 +1,364 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_node.h" #include #include #include #include #include #include #include #include "kis_global.h" #include "kis_node_graph_listener.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_node_progress_proxy.h" #include "kis_safe_read_list.h" typedef KisSafeReadList KisSafeReadNodeList; /** *The link between KisProjection ans KisImageUpdater *uses queued signals with an argument of KisNodeSP type, *so we should register it beforehand */ struct KisNodeSPStaticRegistrar { KisNodeSPStaticRegistrar() { qRegisterMetaType("KisNodeSP"); } }; static KisNodeSPStaticRegistrar __registrar; struct KisNode::Private { public: Private() : graphListener(0) , nodeProgressProxy(0) { } KisNodeWSP parent; KisNodeGraphListener *graphListener; KisSafeReadNodeList nodes; KisNodeProgressProxy *nodeProgressProxy; }; KisNode::KisNode() : m_d(new Private()) { m_d->parent = 0; m_d->graphListener = 0; } KisNode::KisNode(const KisNode & rhs) : KisBaseNode(rhs) , m_d(new Private()) { m_d->parent = 0; m_d->graphListener = 0; KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, rhs.m_d->nodes) { KisNodeSP child = (*iter)->clone(); child->createNodeProgressProxy(); m_d->nodes.append(child); child->setParent(this); } } KisNode::~KisNode() { delete m_d->nodeProgressProxy; m_d->nodes.clear(); delete m_d; } QRect KisNode::needRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); return rect; } QRect KisNode::changeRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); return rect; } QRect KisNode::accessRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); return rect; } bool KisNode::accept(KisNodeVisitor &v) { return v.visit(this); } void KisNode::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } int KisNode::graphSequenceNumber() const { return m_d->graphListener ? m_d->graphListener->graphSequenceNumber() : -1; } KisNodeGraphListener *KisNode::graphListener() const { return m_d->graphListener; } void KisNode::setGraphListener(KisNodeGraphListener *graphListener) { m_d->graphListener = graphListener; KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, m_d->nodes) { KisNodeSP child = (*iter); child->setGraphListener(graphListener); } } KisNodeSP KisNode::parent() const { if (m_d->parent.isValid()) { return m_d->parent; } else { return 0; } } KisBaseNodeSP KisNode::parentCallback() const { return parent(); } void KisNode::baseNodeChangedCallback() { if(m_d->graphListener) { m_d->graphListener->nodeChanged(this); } } void KisNode::setParent(KisNodeWSP parent) { m_d->parent = parent; } KisNodeSP KisNode::firstChild() const { if (!m_d->nodes.isEmpty()) return m_d->nodes.first(); else return 0; } KisNodeSP KisNode::lastChild() const { if (!m_d->nodes.isEmpty()) return m_d->nodes.last(); else return 0; } KisNodeSP KisNode::prevSibling() const { if (!parent()) return 0; int i = parent()->index(const_cast(this)); return parent()->at(i - 1); } KisNodeSP KisNode::nextSibling() const { if (!parent()) return 0; return parent()->at(parent()->index(const_cast(this)) + 1); } quint32 KisNode::childCount() const { return m_d->nodes.size(); } KisNodeSP KisNode::at(quint32 index) const { if (!m_d->nodes.isEmpty() && index < (quint32)m_d->nodes.size()) { return m_d->nodes.at(index); } return 0; } int KisNode::index(const KisNodeSP node) const { if (m_d->nodes.contains(node)) { return m_d->nodes.indexOf(node); } return -1; } QList KisNode::childNodes(const QStringList & nodeTypes, const KoProperties & properties) const { QList nodes; KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, m_d->nodes) { if (properties.isEmpty() || (*iter)->check(properties)) { bool rightType = true; if(!nodeTypes.isEmpty()) { rightType = false; foreach(const QString &nodeType, nodeTypes) { - if ((*iter)->inherits(nodeType.toAscii())) { + if ((*iter)->inherits(nodeType.toLatin1())) { rightType = true; break; } } } if(rightType) { nodes.append(*iter); } } } return nodes; } bool KisNode::add(KisNodeSP newNode, KisNodeSP aboveThis) { Q_ASSERT(newNode); if (!newNode) return false; if (aboveThis && aboveThis->parent().data() != this) return false; if (!allowAsChild(newNode)) return false; if (newNode->parent()) return false; if (m_d->nodes.contains(newNode)) return false; newNode->prepareForAddition(); newNode->createNodeProgressProxy(); int idx = 0; if (aboveThis != 0) { idx = this->index(aboveThis) + 1; if (m_d->graphListener) m_d->graphListener->aboutToAddANode(this, idx); m_d->nodes.insert(idx, newNode); } else { if (m_d->graphListener) m_d->graphListener->aboutToAddANode(this, idx); m_d->nodes.prepend(newNode); } newNode->setParent(this); newNode->setGraphListener(m_d->graphListener); newNode->initAfterAddition(); if (m_d->graphListener) m_d->graphListener->nodeHasBeenAdded(this, idx); return true; } bool KisNode::remove(quint32 index) { if (index < childCount()) { KisNodeSP removedNode = at(index); removedNode->prepareForRemoval(); removedNode->setGraphListener(0); if (m_d->graphListener) m_d->graphListener->aboutToRemoveANode(this, index); removedNode->setParent(0); // after calling aboutToRemoveANode or then the model get broken according to TT's modeltest m_d->nodes.removeAt(index); if (m_d->graphListener) m_d->graphListener->nodeHasBeenRemoved(this, index); return true; } return false; } bool KisNode::remove(KisNodeSP node) { return node->parent().data() == this ? remove(index(node)) : false; } KisNodeProgressProxy* KisNode::nodeProgressProxy() const { if (m_d->nodeProgressProxy) { return m_d->nodeProgressProxy; } else if (parent()) { return parent()->nodeProgressProxy(); } return 0; } void KisNode::createNodeProgressProxy() { if (!m_d->nodeProgressProxy) { m_d->nodeProgressProxy = new KisNodeProgressProxy(this); } } void KisNode::setDirty() { setDirty(extent()); } void KisNode::setDirty(const QVector &rects) { foreach(const QRect &rc, rects) { setDirty(rc); } } void KisNode::setDirty(const QRegion ®ion) { setDirty(region.rects()); } void KisNode::setDirty(const QRect & rect) { if(m_d->graphListener) { m_d->graphListener->requestProjectionUpdate(this, rect); } } #include "kis_node.moc" diff --git a/krita/image/tests/kis_action_recorder_test.cpp b/krita/image/tests/kis_action_recorder_test.cpp index 65d97d0fc9a..ddf879ae42f 100644 --- a/krita/image/tests/kis_action_recorder_test.cpp +++ b/krita/image/tests/kis_action_recorder_test.cpp @@ -1,112 +1,112 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 * MERCHANTABILITY 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 "kis_action_recorder_test.h" #include "testutil.h" #include #include #include #include #include #include #include #include "kis_types.h" #include "kis_image.h" #include "kis_paint_layer.h" #include "recorder/kis_action_recorder.h" #include "recorder/kis_macro.h" #include #include #include #ifndef FILES_DATA_DIR #error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita" #endif void KisActionRecorderTest::testCreation() { const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8(); KisImage image(0, 512, 512, colorSpace, "paintop registry test"); KisActionRecorder test(); } void KisActionRecorderTest::testFiles() { TestUtil::TestProgressBar progressProxy; KoProgressUpdater progressUpdater(&progressProxy); progressUpdater.start(100); QDir dirSources(QString(FILES_DATA_DIR) + "/actionrecorder/sources"); foreach(QFileInfo sourceFileInfo, dirSources.entryInfoList()) { if (!sourceFileInfo.isHidden() && !sourceFileInfo.isDir()) { qDebug() << "handling " << sourceFileInfo.fileName(); QFileInfo resultFileInfo(QString(FILES_DATA_DIR) + "/actionrecorder/results/" + sourceFileInfo.fileName() + ".png"); QVERIFY2(resultFileInfo.exists(), - QString("Result file %1 not found").arg(resultFileInfo.fileName()).toAscii().data()); + QString("Result file %1 not found").arg(resultFileInfo.fileName()).toLatin1()); // Replay // Create an image and the document QDomDocument domDoc; const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, 200, 200, cs, ""); KoColor white(Qt::white, cs); KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8); paintLayer1->paintDevice()->setDefaultPixel(white.data()); image->addNode(paintLayer1); // Load recorded action QString err; int line, col; QFile file(sourceFileInfo.absoluteFilePath()); QVERIFY(file.open(QIODevice::ReadOnly)); QVERIFY(domDoc.setContent(&file, &err, &line, &col)); file.close(); QDomElement docElem = domDoc.documentElement(); QVERIFY(!docElem.isNull() && docElem.tagName() == "RecordedActions"); // Unserialise KisMacro m; m.fromXML(docElem, 0); // Play KoUpdater *updater = progressUpdater.startSubtask(); KisMacroPlayer player(&m, KisPlayInfo(image, paintLayer1), updater); player.start(); player.wait(); QImage sourceImage = image->convertToQImage(0, 0, 200, 200, 0); // load what we should have get from the hard drive QImage resultImage(resultFileInfo.absoluteFilePath()); resultImage = resultImage.convertToFormat(QImage::Format_ARGB32); QPoint pt; if(!TestUtil::compareQImages(pt, sourceImage, resultImage, 40)) { sourceImage.save("action_recorder_source_" + sourceFileInfo.fileName() + ".png"); resultImage.save("action_recorder_result_" + sourceFileInfo.fileName() + ".png"); qCritical() << "Failed to play action:" << sourceFileInfo.fileName() << "image differs at point" << pt; QFAIL("Images do not coincide"); } } } } QTEST_KDEMAIN(KisActionRecorderTest, GUI) #include "kis_action_recorder_test.moc" diff --git a/krita/image/tests/kis_convolution_painter_test.cpp b/krita/image/tests/kis_convolution_painter_test.cpp index c1dc0245b23..82881227a54 100644 --- a/krita/image/tests/kis_convolution_painter_test.cpp +++ b/krita/image/tests/kis_convolution_painter_test.cpp @@ -1,310 +1,310 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 * MERCHANTABILITY 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 "kis_convolution_painter_test.h" #include #include #include #include #include #include #include "kis_types.h" #include "kis_paint_device.h" #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_paint_device.h" #include #include "testutil.h" KisPaintDeviceSP initAsymTestDevice(QRect &imageRect, int &pixelSize, QByteArray &initialData) { KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); pixelSize = dev->pixelSize(); imageRect = QRect(0,0,5,5); initialData.resize(25 * pixelSize); quint8 *ptr = (quint8*) initialData.data(); for(int i = 0; i < 25; i++) { KoColor pixel(QColor(i,i,i,255), dev->colorSpace()); memcpy(ptr, pixel.data(), pixelSize); ptr += pixelSize; } dev->writeBytes((const quint8*)initialData.constData(), imageRect); return dev; } Matrix initSymmFilter(qreal &offset, qreal &factor) { Matrix filter; filter(0,0) = 1.0 / 21; filter(0,1) = 3.0 / 21; filter(0,2) = 1.0 / 21; filter(1,0) = 3.0 / 21; filter(1,1) = 5.0 / 21; filter(1,2) = 3.0 / 21; filter(2,0) = 1.0 / 21; filter(2,1) = 3.0 / 21; filter(2,2) = 1.0 / 21; offset = 0.0; factor = 1.0; return filter; } Matrix initAsymmFilter(qreal &offset, qreal &factor) { Matrix filter; filter(0,0) = -1.0; filter(1,0) = -2.0; filter(2,0) = -1.0; filter(0,1) = 0; filter(1,1) = 0; filter(2,1) = 0; filter(0,2) = 1.0; filter(1,2) = 2.0; filter(2,2) = 1.0; offset = 0.5; factor = 1.0; return filter; } void printPixel(QString prefix, int pixelSize, quint8 *data) { QString str = prefix; for(int i = 0; i < pixelSize; i++) { str += " "; str += QString::number(data[i]); } qDebug() << str; } void KisConvolutionPainterTest::testIdentityConvolution() { QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(qimage, 0, 0, 0); KisConvolutionKernelSP kernel = new KisConvolutionKernel(3, 3, 0, 0); kernel->data()[0] = 0; kernel->data()[1] = 0; kernel->data()[2] = 0; kernel->data()[3] = 0; kernel->data()[4] = 1; kernel->data()[5] = 0; kernel->data()[6] = 0; kernel->data()[7] = 0; kernel->data()[8] = 0; KisConvolutionPainter gc(dev); gc.beginTransaction(0); gc.applyMatrix(kernel, dev, QPoint(0, 0), QPoint(0, 0), QSize(qimage.width(), qimage.height())); gc.deleteTransaction(); QImage resultImage = dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, qimage, resultImage)) { resultImage.save("identity_convolution.png"); - QFAIL(QString("Identity kernel did change image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Identity kernel did change image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisConvolutionPainterTest::testSymmConvolution() { qreal offset = 0.0; qreal factor = 1.0; Matrix filter = initSymmFilter(offset, factor); QRect imageRect; int pixelSize = 0; QByteArray initialData; KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(filter, offset, factor); KisConvolutionPainter gc(dev); gc.beginTransaction(0); gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(), imageRect.size()); gc.deleteTransaction(); QByteArray resultData(initialData.size(), 0); dev->readBytes((quint8*)resultData.data(), imageRect); QCOMPARE(resultData, initialData); } void KisConvolutionPainterTest::testAsymmConvolutionImp(QBitArray channelFlags) { qreal offset = 0.0; qreal factor = 1.0; Matrix filter = initAsymmFilter(offset, factor); QRect imageRect; int pixelSize = -1; QByteArray initialData; KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(filter, offset, factor); KisConvolutionPainter gc(dev); gc.beginTransaction(0); gc.setChannelFlags(channelFlags); gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(), imageRect.size()); gc.deleteTransaction(); QByteArray resultData(initialData.size(), 0); dev->readBytes((quint8*)resultData.data(), imageRect); QRect filteredRect = imageRect.adjusted(1, 1, -1, -1); KoColor filteredPixel(QColor(120,120,120,128), dev->colorSpace()); quint8 *srcPtr = (quint8*) initialData.data(); quint8 *resPtr = (quint8*) resultData.data(); for(int row = 0; row < imageRect.height(); row++) { for(int col = 0; col < imageRect.width(); col++) { bool isFiltered = filteredRect.contains(col, row); KoColor referencePixel(dev->colorSpace()); for(int j = 0; j < pixelSize; j++) { referencePixel.data()[j] = isFiltered && channelFlags[j] ? filteredPixel.data()[j] : srcPtr[j]; } if(memcmp(resPtr, referencePixel.data(), pixelSize)) { printPixel("Actual: ", pixelSize, resPtr); printPixel("Expected:", pixelSize, referencePixel.data()); QFAIL("Failed to filter area"); } srcPtr += pixelSize; resPtr += pixelSize; } } } void KisConvolutionPainterTest::testAsymmAllChannels() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipRed() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[2] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipGreen() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[1] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipBlue() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[0] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipAlpha() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[3] = false; testAsymmConvolutionImp(channelFlags); } // #include void KisConvolutionPainterTest::benchmarkConvolution() { QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QRect imageRect(QPoint(), referenceImage.size()); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(referenceImage, 0, 0, 0); qreal offset = 0.0; qreal factor = 1.0; Matrix filter = initAsymmFilter(offset, factor); int diameter = 1; for (int i = 0; i < 10; i++) { KisCircleMaskGenerator* kas = new KisCircleMaskGenerator(diameter, 1.0, 5, 5, 2); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas); KisConvolutionPainter gc(dev); QTime timer; timer.start(); // CALLGRIND_START_INSTRUMENTATION; gc.beginTransaction(0); gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(), imageRect.size()); gc.deleteTransaction(); // CALLGRIND_STOP_INSTRUMENTATION; qDebug() << "Diameter:" << diameter << "time:" << timer.elapsed(); if(diameter < 10) { diameter += 2; } else { diameter += 8; } } } QTEST_KDEMAIN(KisConvolutionPainterTest, GUI) #include "kis_convolution_painter_test.moc" diff --git a/krita/image/tests/kis_cs_conversion_test.cpp b/krita/image/tests/kis_cs_conversion_test.cpp index dc5b7b4c4c4..f9c9b16ca11 100644 --- a/krita/image/tests/kis_cs_conversion_test.cpp +++ b/krita/image/tests/kis_cs_conversion_test.cpp @@ -1,105 +1,105 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_cs_conversion_test.h" #include #include #include #include #include #include #include "kis_painter.h" #include "kis_types.h" #include "kis_paint_device.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_datamanager.h" #include "kis_global.h" #include "testutil.h" #include "kis_transaction.h" #include "kis_image.h" void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs) { QString profile1("no profile"); QString profile2("no profile"); if (srcCs->profile()) profile1 = srcCs->profile()->name(); if (dstCs->profile()) profile2 = dstCs->profile()->name(); QWARN(QString("Failed %1 %2 -> %3 %4 %5") .arg(srcCs->name()) .arg(profile1) .arg(dstCs->name()) .arg(profile2) .arg(reason) - .toAscii()); + .toLatin1()); } void KisCsConversionTest::testColorSpaceConversion() { QTime t; t.start(); QList colorSpaces = TestUtil::allColorSpaces(); int failedColorSpaces = 0; QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); foreach(const KoColorSpace * srcCs, colorSpaces) { foreach(const KoColorSpace * dstCs, colorSpaces) { KisPaintDeviceSP dev = new KisPaintDevice(srcCs); dev->convertFromQImage(image, 0); dev->move(10, 10); // Unalign with tile boundaries dev->convertTo(dstCs); if (dev->exactBounds() != QRect(10, 10, image.width(), image.height())) { logFailure("bounds", srcCs, dstCs); failedColorSpaces++; } if (dev->pixelSize() != dstCs->pixelSize()) { logFailure("pixelsize", srcCs, dstCs); failedColorSpaces++; } if (!(*dev->colorSpace() == *dstCs)) { logFailure("dest cs", srcCs, dstCs); failedColorSpaces++; } } } qDebug() << colorSpaces.size() * colorSpaces.size() << "conversions" << " done in " << t.elapsed() << "ms"; if (failedColorSpaces > 0) { - QFAIL(QString("Failed conversions %1, see log for details.").arg(failedColorSpaces).toAscii()); + QFAIL(QString("Failed conversions %1, see log for details.").arg(failedColorSpaces).toLatin1()); } } QTEST_KDEMAIN(KisCsConversionTest, GUI) #include "kis_cs_conversion_test.moc" diff --git a/krita/image/tests/kis_filter_job_test.cpp b/krita/image/tests/kis_filter_job_test.cpp index 0b779ef41e4..d158af3b58d 100644 --- a/krita/image/tests/kis_filter_job_test.cpp +++ b/krita/image/tests/kis_filter_job_test.cpp @@ -1,92 +1,92 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 * MERCHANTABILITY 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 "kis_filter_job_test.h" #include #include #include #include #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "kis_selection.h" #include "kis_processing_information.h" #include "filter/kis_filter.h" #include "testutil.h" #include "kis_threaded_applicator.h" #include "filter/kis_filter_job.h" #include "testutil.h" #include "kis_fill_painter.h" void KisFilterJobTest::testCreation() { KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr up = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisFilterJobFactory factory(f, kfc); ThreadWeaver::Job* job = factory.createJob(0, dev, QRect(0, 0, 2000, 2000), up); Q_ASSERT(job); delete job; } void KisFilterJobTest::testInWeaver() { KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); TestUtil::TestProgressBar * bar = new TestUtil::TestProgressBar(); KoProgressUpdater* pu = new KoProgressUpdater(bar); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(qimage, 0, 0, 0); KisFilterJobFactory factory(f, kfc); KisThreadedApplicator applicator(dev, QRect(0, 0, 2000, 2000), &factory, pu); applicator.execute(); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, inverted, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtermasktest2.png"); - QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } delete pu; delete bar; } QTEST_KDEMAIN(KisFilterJobTest, GUI) #include "kis_filter_job_test.moc" diff --git a/krita/image/tests/kis_filter_mask_test.cpp b/krita/image/tests/kis_filter_mask_test.cpp index 2cbd248a12f..12796263b38 100644 --- a/krita/image/tests/kis_filter_mask_test.cpp +++ b/krita/image/tests/kis_filter_mask_test.cpp @@ -1,125 +1,125 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_filter_mask_test.h" #include #include #include "kis_selection.h" #include "filter/kis_filter.h" #include "filter/kis_filter_configuration.h" #include "kis_filter_mask.h" #include "filter/kis_filter_registry.h" #include "kis_group_layer.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_types.h" #include "kis_image.h" #include "testutil.h" #define IMAGE_WIDTH 1000 #define IMAGE_HEIGHT 1000 void KisFilterMaskTest::testCreation() { KisFilterMaskSP mask = new KisFilterMask(); } #define initImage(image, layer, device, mask) do { \ image = new KisImage(0, IMAGE_WIDTH, IMAGE_HEIGHT, 0, "tests"); \ layer = new KisPaintLayer(image, 0, 100, device); \ image->addNode(layer); \ image->addNode(mask, layer); \ } while(0) void KisFilterMaskTest::testProjectionNotSelected() { KisImageSP image; KisPaintLayerSP layer; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png"); KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); KisFilterMaskSP mask = new KisFilterMask(); mask->setFilter(kfc); // Check basic apply(). Shouldn't do anything, since nothing is selected yet. KisPaintDeviceSP projection = new KisPaintDevice(cs); initImage(image, layer, projection, mask); projection->convertFromQImage(qimage, 0, 0, 0); mask->initSelection(0, layer); mask->createNodeProgressProxy(); mask->select(qimage.rect(), MIN_SELECTED); mask->apply(projection, QRect(0, 0, qimage.width(), qimage.height())); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, qimage, projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtermasktest1.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFilterMaskTest::testProjectionSelected() { KisImageSP image; KisPaintLayerSP layer; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png"); KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); KisFilterMaskSP mask = new KisFilterMask(); mask->setFilter(kfc); mask->createNodeProgressProxy(); KisPaintDeviceSP projection = new KisPaintDevice(cs); initImage(image, layer, projection, mask); projection->convertFromQImage(qimage, 0, 0, 0); mask->initSelection(0, layer); mask->select(qimage.rect(), MAX_SELECTED); mask->apply(projection, QRect(0, 0, qimage.width(), qimage.height())); QCOMPARE(mask->exactBounds(), QRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT)); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, inverted, projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtermasktest2.png"); - QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } QTEST_KDEMAIN(KisFilterMaskTest, GUI) #include "kis_filter_mask_test.moc" diff --git a/krita/image/tests/kis_filter_test.cpp b/krita/image/tests/kis_filter_test.cpp index d7b66b7cf66..b9377de1a18 100644 --- a/krita/image/tests/kis_filter_test.cpp +++ b/krita/image/tests/kis_filter_test.cpp @@ -1,244 +1,244 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 * MERCHANTABILITY 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 "kis_filter_test.h" #include #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "kis_selection.h" #include "kis_processing_information.h" #include "filter/kis_filter.h" #include "testutil.h" #include "kis_threaded_applicator.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include class TestFilter : public KisFilter { public: TestFilter() : KisFilter(KoID("test", "test"), KoID("test", "test"), "TestFilter") { } using KisFilter::process; void process(KisPaintDeviceSP src, const QRect& size, const KisFilterConfiguration* config, KoUpdater* progressUpdater) const { Q_UNUSED(src); Q_UNUSED(size); Q_UNUSED(config); Q_UNUSED(progressUpdater); } }; void KisFilterTest::testCreation() { TestFilter test; } void KisFilterTest::testWithProgressUpdater() { TestUtil::TestProgressBar * bar = new TestUtil::TestProgressBar(); KoProgressUpdater* pu = new KoProgressUpdater(bar); KoUpdaterPtr updater = pu->startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(qimage, 0, 0, 0); KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc, updater); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, inverted, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtertest.png"); - QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } delete pu; delete bar; } void KisFilterTest::testSingleThreaded() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(qimage, 0, 0, 0); KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, inverted, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtertest.png"); - QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFilterTest::testDifferentSrcAndDst() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png"); KisPaintDeviceSP src = new KisPaintDevice(cs); KisPaintDeviceSP dst = new KisPaintDevice(cs); KisSelectionSP sel = new KisSelection(new KisSelectionDefaultBounds(src)); sel->getOrCreatePixelSelection()->invert(); // select everything sel->updateProjection(); src->convertFromQImage(qimage, 0, 0, 0); KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); f->process(src, dst, sel, QRect(QPoint(0,0), qimage.size()), kfc); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, inverted, dst->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { dst->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtertest.png"); - QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFilterTest::testOldDataApiAfterCopy() { QRect updateRect(0,0,63,63); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); quint8 *whitePixel = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::white, whitePixel); cs->setOpacity(whitePixel, OPACITY_OPAQUE_U8, 1); KisPaintDeviceSP tmp = new KisPaintDevice(cs); KisPaintDeviceSP src = new KisPaintDevice(cs); src->fill(0, 0, 50, 50, whitePixel); /** * Make a full copy here to catch the bug. * Buggy memento manager would make a commit * that is not good. */ KisPaintDeviceSP dst = new KisPaintDevice(*src); /** * This would write go to a new revision in a buggy * memento manager */ dst->clear(updateRect); KisFilterSP f = KisFilterRegistry::instance()->value("invert"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); /** * This filter reads from oldRawData, so if we have some * weirdness with transactions it will read from old and non-cleared * version of the device and we will see a black square instead * of empty device in tmp */ f->process(dst, tmp, 0, updateRect, kfc); /** * In theory, both devices: dst and tmp must be empty by now */ KisPaintDeviceSP reference = new KisPaintDevice(cs); QImage refImage = reference->convertToQImage(0,0,0,63,63); QImage dstImage = dst->convertToQImage(0,0,0,63,63); QImage tmpImage = tmp->convertToQImage(0,0,0,63,63); QPoint pt; QVERIFY(TestUtil::compareQImages(pt, refImage, dstImage)); QVERIFY(TestUtil::compareQImages(pt, refImage, tmpImage)); } void KisFilterTest::testBlurFilterApplicationRect() { QRect filterRect(10,10,40,40); QRect src1Rect(5,5,50,50); QRect src2Rect(0,0,60,60); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); quint8 *whitePixel = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::white, whitePixel); cs->setOpacity(whitePixel, OPACITY_OPAQUE_U8, 1); KisPaintDeviceSP src1 = new KisPaintDevice(cs); src1->fill(src1Rect.left(),src1Rect.top(),src1Rect.width(),src1Rect.height(), whitePixel); KisPaintDeviceSP src2 = new KisPaintDevice(cs); src2->fill(src2Rect.left(),src2Rect.top(),src2Rect.width(),src2Rect.height(), whitePixel); KisPaintDeviceSP dst1 = new KisPaintDevice(cs); KisPaintDeviceSP dst2 = new KisPaintDevice(cs); KisFilterSP f = KisFilterRegistry::instance()->value("blur"); Q_ASSERT(f); KisFilterConfiguration * kfc = f->defaultConfiguration(0); Q_ASSERT(kfc); f->process(src1, dst1, 0, filterRect, kfc); f->process(src2, dst2, 0, filterRect, kfc); KisPaintDeviceSP reference = new KisPaintDevice(cs); reference->fill(filterRect.left(),filterRect.top(),filterRect.width(),filterRect.height(), whitePixel); QImage refImage = reference->convertToQImage(0,10,10,40,40); QImage dst1Image = dst1->convertToQImage(0,10,10,40,40); QImage dst2Image = dst2->convertToQImage(0,10,10,40,40); //dst1Image.save("DST1.png"); //dst2Image.save("DST2.png"); QPoint pt; QVERIFY(TestUtil::compareQImages(pt, refImage, dst1Image)); QVERIFY(TestUtil::compareQImages(pt, refImage, dst2Image)); } QTEST_KDEMAIN(KisFilterTest, GUI) #include "kis_filter_test.moc" diff --git a/krita/image/tests/kis_fixed_paint_device_test.cpp b/krita/image/tests/kis_fixed_paint_device_test.cpp index 8b48ee8f29b..8def87b3d34 100644 --- a/krita/image/tests/kis_fixed_paint_device_test.cpp +++ b/krita/image/tests/kis_fixed_paint_device_test.cpp @@ -1,273 +1,273 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_fixed_paint_device_test.h" #include #include #include #include #include #include #include "kis_painter.h" #include "kis_types.h" #include "kis_paint_device.h" #include "kis_fixed_paint_device.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_datamanager.h" #include "kis_global.h" #include "testutil.h" #include "kis_transaction.h" #include "kis_image.h" void KisFixedPaintDeviceTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev = new KisFixedPaintDevice(cs); QVERIFY(dev->bounds() == QRect()); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->pixelSize() == cs->pixelSize()); dev->setRect(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); dev->initialize(); QVERIFY(dev->data() != 0); quint8* data = dev->data(); for (uint i = 0; i < 100 * 100 * cs->pixelSize(); ++i) { QVERIFY(data[i] == 0); } } void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs) { QString profile1("no profile"); QString profile2("no profile"); if (srcCs->profile()) profile1 = srcCs->profile()->name(); if (dstCs->profile()) profile2 = dstCs->profile()->name(); QWARN(QString("Failed %1 %2 -> %3 %4 %5") .arg(srcCs->name()) .arg(profile1) .arg(dstCs->name()) .arg(profile2) .arg(reason) - .toAscii()); + .toLatin1()); } void KisFixedPaintDeviceTest::testColorSpaceConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8(); const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(srcCs); dev->convertFromQImage(image, 0); dev->convertTo(dstCs); QVERIFY(dev->bounds() == QRect(0, 0, image.width(), image.height())); QVERIFY(dev->pixelSize() == dstCs->pixelSize()); QVERIFY(*dev->colorSpace() == *dstCs); } void KisFixedPaintDeviceTest::testRoundtripQImageConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->convertFromQImage(image, 0); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_fixed_paint_device_test_test_roundtrip_qimage.png"); result.save("kis_fixed_paint_device_test_test_roundtrip_result.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltFixed() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); // Without opacity KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result, 1)) { fdev->convertToQImage(0).save("kis_fixed_paint_device_test_test_blt_fixed_expected.png"); result.save("kis_fixed_paint_device_test_test_blt_fixed_result.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltFixedOpacity() { // blt a semi-transparent image on a white paint device QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png"); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) { checkResult.save("kis_fixed_paint_device_test_test_blt_fixed_opactiy_expected.png"); result.save("kis_fixed_paint_device_test_test_blt_fixed_opacity_result.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testSilly() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->initialize(); dev->initialize(); } void KisFixedPaintDeviceTest::testClear() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->clear(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data() + (50 * 50 * cs->pixelSize())) == OPACITY_TRANSPARENT_U8); } void KisFixedPaintDeviceTest::testFill() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); quint8* red = new quint8[cs->pixelSize()]; memcpy(red, KoColor(Qt::red, cs).data(), cs->pixelSize()); cs->setOpacity(red, quint8(128), 1); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->fill(0, 0, 100, 100, red); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data()) == 128); QVERIFY(memcmp(dev->data(), red, cs->pixelSize()) == 0); //Compare fill will normal paint device dev = new KisFixedPaintDevice(cs); dev->setRect(QRect(0, 0, 150, 150)); dev->initialize(); dev->fill(50, 50, 50, 50, red); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->fill(50, 50, 50, 50, red); QImage image = dev->convertToQImage(0); QImage checkImage = dev2->convertToQImage(0, 0, 0, 150, 150); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, checkImage)) { image.save("kis_fixed_paint_device_filled_result.png"); checkImage.save("kis_fixed_paint_device_filled_result_expected.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } delete[] red; } void KisFixedPaintDeviceTest::testBltFixedSmall() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "fixed_blit_small.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); // Without opacity KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 51, 51); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_fixed_paint_device_test_blt_small_image.png"); result.save("kis_fixed_paint_device_test_blt_small_result.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltPerformance() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); QTime t; t.start(); int x; for (x = 0; x < 1000; ++x) { KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); } qDebug() << x << "blits" << " done in " << t.elapsed() << "ms"; } QTEST_KDEMAIN(KisFixedPaintDeviceTest, GUI) #include "kis_fixed_paint_device_test.moc" diff --git a/krita/image/tests/kis_paint_device_test.cpp b/krita/image/tests/kis_paint_device_test.cpp index 8554727bd77..69c50d659c8 100644 --- a/krita/image/tests/kis_paint_device_test.cpp +++ b/krita/image/tests/kis_paint_device_test.cpp @@ -1,698 +1,698 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_paint_device_test.h" #include #include #include #include #include #include #include "kis_painter.h" #include "kis_types.h" #include "kis_paint_device.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_datamanager.h" #include "kis_global.h" #include "testutil.h" #include "kis_transaction.h" #include "kis_image.h" void KisPaintDeviceTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->objectName().isEmpty()); dev = new KisPaintDevice(cs); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->x() == 0); QVERIFY(dev->y() == 0); QVERIFY(dev->pixelSize() == cs->pixelSize()); QVERIFY(dev->channelCount() == cs->channelCount()); QVERIFY(dev->dataManager() != 0); KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test"); KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125); dev = new KisPaintDevice(layer.data(), cs); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->x() == 0); QVERIFY(dev->y() == 0); QVERIFY(dev->pixelSize() == cs->pixelSize()); QVERIFY(dev->channelCount() == cs->channelCount()); QVERIFY(dev->dataManager() != 0); // Let the layer go out of scope and see what happens { KisPaintLayerSP l2 = new KisPaintLayer(image, "blabla", 250); dev = new KisPaintDevice(l2.data(), cs); } } void KisPaintDeviceTest::testStore() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); KoStore * readStore = KoStore::createStore(QString(FILES_DATA_DIR) + QDir::separator() + "store_test.kra", KoStore::Read); readStore->open("built image/layers/layer0"); QVERIFY(dev->read(readStore)); readStore->close(); delete readStore; QVERIFY(dev->exactBounds() == QRect(0, 0, 100, 100)); KoStore * writeStore = KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Write); writeStore->open("built image/layers/layer0"); QVERIFY(dev->write(writeStore)); writeStore->close(); delete writeStore; KisPaintDeviceSP dev2 = new KisPaintDevice(cs); readStore = KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Read); readStore->open("built image/layers/layer0"); QVERIFY(dev2->read(readStore)); readStore->close(); delete readStore; QVERIFY(dev2->exactBounds() == QRect(0, 0, 100, 100)); QPoint pt; if (!TestUtil::comparePaintDevices(pt, dev, dev2)) { - QFAIL(QString("Loading a saved image is not pixel perfect, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toAscii()); + QFAIL(QString("Loading a saved image is not pixel perfect, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1()); } } void KisPaintDeviceTest::testGeometry() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* pixel = cs->allocPixelBuffer(1); cs->fromQColor(Qt::white, pixel); dev->fill(0, 0, 512, 512, pixel); QCOMPARE(dev->exactBounds(), QRect(0, 0, 512, 512)); QCOMPARE(dev->extent(), QRect(0, 0, 512, 512)); dev->move(10, 10); QCOMPARE(dev->exactBounds(), QRect(10, 10, 512, 512)); QCOMPARE(dev->extent(), QRect(10, 10, 512, 512)); dev->crop(50, 50, 50, 50); QCOMPARE(dev->exactBounds(), QRect(50, 50, 50, 50)); QCOMPARE(dev->extent(), QRect(10, 10, 128, 128)); QColor c; dev->clear(QRect(50, 50, 50, 50)); dev->pixel(80, 80, &c); QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8); dev->fill(0, 0, 512, 512, pixel); dev->pixel(80, 80, &c); QVERIFY(c == Qt::white); QVERIFY(c.alpha() == OPACITY_OPAQUE_U8); dev->clear(); dev->pixel(80, 80, &c); QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8); QVERIFY(dev->extent().isEmpty()); QVERIFY(dev->exactBounds().isEmpty()); } void KisPaintDeviceTest::testClear() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->extent() == QRect(2147483647, 2147483647, 0, 0)); QVERIFY(dev->exactBounds() == QRect(2147483647, 2147483647, 0, 0)); dev->clear(); QVERIFY(dev->extent() == QRect(2147483647, 2147483647, 0, 0)); QVERIFY(dev->exactBounds() == QRect(2147483647, 2147483647, 0, 0)); dev->clear(QRect(100, 100, 100, 100)); // XXX: This is strange! QVERIFY(dev->extent() == QRect(64, 64, 192, 192)); QVERIFY(dev->exactBounds() == QRect(64, 64, 192, 192)); dev->clear(); QVERIFY(dev->extent() == QRect(2147483647, 2147483647, 0, 0)); QVERIFY(dev->exactBounds() == QRect(2147483647, 2147483647, 0, 0)); } void KisPaintDeviceTest::testCrop() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* pixel = cs->allocPixelBuffer(1); cs->fromQColor(Qt::white, pixel); dev->fill(-14, 8, 433, 512, pixel); QVERIFY(dev->exactBounds() == QRect(-14, 8, 433, 512)); // Crop inside dev->crop(50, 50, 150, 150); QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150)); // Crop outside, pd should not grow dev->crop(0, 0, 1000, 1000); QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150)); } void KisPaintDeviceTest::testRoundtripReadWrite() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); dev->convertFromQImage(image, 0); quint8* bytes = cs->allocPixelBuffer(image.width() * image.height()); memset(bytes, 0, image.width() * image.height() * dev->pixelSize()); dev->readBytes(bytes, image.rect()); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->writeBytes(bytes, image.rect()); QVERIFY(dev2->exactBounds() == image.rect()); dev2->convertToQImage(0, 0, 0, image.width(), image.height()).save("readwrite.png"); QPoint pt; if (!TestUtil::comparePaintDevices(pt, dev, dev2)) { - QFAIL(QString("Failed round trip using readBytes and writeBytes, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toAscii()); + QFAIL(QString("Failed round trip using readBytes and writeBytes, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1()); } } void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs) { QString profile1("no profile"); QString profile2("no profile"); if (srcCs->profile()) profile1 = srcCs->profile()->name(); if (dstCs->profile()) profile2 = dstCs->profile()->name(); QWARN(QString("Failed %1 %2 -> %3 %4 %5") .arg(srcCs->name()) .arg(profile1) .arg(dstCs->name()) .arg(profile2) .arg(reason) - .toAscii()); + .toLatin1()); } void KisPaintDeviceTest::testColorSpaceConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8(); const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16(); KisPaintDeviceSP dev = new KisPaintDevice(srcCs); dev->convertFromQImage(image, 0); dev->move(10, 10); // Unalign with tile boundaries KUndo2Command* cmd = dev->convertTo(dstCs); QVERIFY(dev->exactBounds() == QRect(10, 10, image.width(), image.height())); QVERIFY(dev->pixelSize() == dstCs->pixelSize()); QVERIFY(*dev->colorSpace() == *dstCs); delete cmd; } void KisPaintDeviceTest::testRoundtripConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_paint_device_test_test_roundtrip_qimage.png"); result.save("kis_paint_device_test_test_roundtrip_result.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisPaintDeviceTest::testFastBitBlt() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dstDev = new KisPaintDevice(cs); KisPaintDeviceSP srcDev = new KisPaintDevice(cs); srcDev->convertFromQImage(image, 0); QRect cloneRect(100,100,200,200); QPoint errpoint; QVERIFY(dstDev->fastBitBltPossible(srcDev)); dstDev->fastBitBlt(srcDev, cloneRect); QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) { - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Test Rough version dstDev->clear(); dstDev->fastBitBltRough(srcDev, cloneRect); srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) { - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } srcDev->move(10,10); QVERIFY(!dstDev->fastBitBltPossible(srcDev)); } void KisPaintDeviceTest::testMakeClone() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP srcDev = new KisPaintDevice(cs); srcDev->convertFromQImage(image, 0); srcDev->move(10,10); const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->lab16(); KisPaintDeviceSP dstDev = new KisPaintDevice(weirdCS); dstDev->move(1000,1000); QVERIFY(!dstDev->fastBitBltPossible(srcDev)); QRect cloneRect(100,100,200,200); QPoint errpoint; dstDev->makeCloneFrom(srcDev, cloneRect); QVERIFY(*dstDev->colorSpace() == *srcDev->colorSpace()); QCOMPARE(dstDev->pixelSize(), srcDev->pixelSize()); QCOMPARE(dstDev->x(), srcDev->x()); QCOMPARE(dstDev->y(), srcDev->y()); QCOMPARE(dstDev->exactBounds(), cloneRect); QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); if (!TestUtil::compareQImages(errpoint, dstImage, srcImage)) { - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisPaintDeviceTest::testThumbnail() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); { KisPaintDeviceSP thumb = dev->createThumbnailDevice(50, 50); QRect rc = thumb->exactBounds(); QVERIFY(rc.width() <= 50); QVERIFY(rc.height() <= 50); } { QImage thumb = dev->createThumbnail(50, 50); QVERIFY(!thumb.isNull()); QVERIFY(thumb.width() <= 50); QVERIFY(thumb.height() <= 50); image.save("kis_paint_device_test_test_thumbnail.png"); } } void KisPaintDeviceTest::testCaching() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* whitePixel = cs->allocPixelBuffer(1); cs->fromQColor(Qt::white, whitePixel); quint8* blackPixel = cs->allocPixelBuffer(1); cs->fromQColor(Qt::black, blackPixel); dev->fill(0, 0, 512, 512, whitePixel); QImage thumb1 = dev->createThumbnail(50, 50); QRect exactBounds1 = dev->exactBounds(); dev->fill(0, 0, 768, 768, blackPixel); QImage thumb2 = dev->createThumbnail(50, 50); QRect exactBounds2 = dev->exactBounds(); dev->move(10, 10); QImage thumb3 = dev->createThumbnail(50, 50); QRect exactBounds3 = dev->exactBounds(); dev->crop(50, 50, 50, 50); QImage thumb4 = dev->createThumbnail(50, 50); QRect exactBounds4 = dev->exactBounds(); QVERIFY(thumb1 != thumb2); QVERIFY(thumb2 == thumb3); // Cache miss, but image is the same QVERIFY(thumb3 != thumb4); QVERIFY(thumb4 != thumb1); QCOMPARE(exactBounds1, QRect(0,0,512,512)); QCOMPARE(exactBounds2, QRect(0,0,768,768)); QCOMPARE(exactBounds3, QRect(10,10,768,768)); QCOMPARE(exactBounds4, QRect(50,50,50,50)); } void KisPaintDeviceTest::testRegion() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* whitePixel = cs->allocPixelBuffer(1); cs->fromQColor(Qt::white, whitePixel); dev->fill(0, 0, 10, 10, whitePixel); dev->fill(70, 70, 10, 10, whitePixel); dev->fill(129, 0, 10, 10, whitePixel); dev->fill(0, 1030, 10, 10, whitePixel); QRegion referenceRegion; referenceRegion += QRect(0,0,64,64); referenceRegion += QRect(64,64,64,64); referenceRegion += QRect(128,0,64,64); referenceRegion += QRect(0,1024,64,64); QCOMPARE(dev->exactBounds(), QRect(0,0,139,1040)); QCOMPARE(dev->region(), referenceRegion); } void KisPaintDeviceTest::testPixel() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QColor c = Qt::red; quint8 opacity = 125; c.setAlpha(opacity); dev->setPixel(5, 5, c); QColor c2; dev->pixel(5, 5, &c2); QVERIFY(c == c2); QVERIFY(opacity == c2.alpha()); } void KisPaintDeviceTest::testPlanarReadWrite() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* pixel = cs->allocPixelBuffer(1); cs->fromQColor(QColor(255, 200, 155, 100), pixel); dev->fill(0, 0, 5000, 5000, pixel); delete[] pixel; QColor c1; dev->pixel(5, 5, &c1); QVector planes = dev->readPlanarBytes(500, 500, 100, 100); QVector swappedPlanes; QCOMPARE((int)planes.size(), (int)dev->channelCount()); for (int i = 0; i < 100*100; i++) { // BGRA encoded QVERIFY(planes.at(2)[i] == 255); QVERIFY(planes.at(1)[i] == 200); QVERIFY(planes.at(0)[i] == 155); QVERIFY(planes.at(3)[i] == 100); } for (uint i = 1; i < dev->channelCount() + 1; ++i) { swappedPlanes.append(planes[dev->channelCount() - i]); } dev->writePlanarBytes(swappedPlanes, 0, 0, 100, 100); dev->convertToQImage(0, 0, 0, 5000, 5000).save("planar.png"); qDeleteAll(planes); swappedPlanes.clear(); dev->pixel(5, 5, &c1); QVERIFY(c1.red() == 200); QVERIFY(c1.green() == 255); QVERIFY(c1.blue() == 100); QVERIFY(c1.alpha() == 155); dev->pixel(75, 50, &c1); QVERIFY(c1.red() == 200); QVERIFY(c1.green() == 255); QVERIFY(c1.blue() == 100); QVERIFY(c1.alpha() == 155); } void KisPaintDeviceTest::testBltPerformance() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP fdev = new KisPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); QTime t; t.start(); int x; for (x = 0; x < 1000; ++x) { KisPainter gc(dev); gc.bitBlt(QPoint(0, 0), fdev, image.rect()); } qDebug() << x << "blits" << " done in " << t.elapsed() << "ms"; } void KisPaintDeviceTest::testDeviceDuplication() { QRect fillRect(0,0,64,64); quint8 fillPixel[4]={255,255,255,255}; QRect clearRect(10,10,20,20); QImage referenceImage; QImage resultImage; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP device = new KisPaintDevice(cs); // qDebug()<<"FILLING"; device->fill(fillRect.left(), fillRect.top(), fillRect.width(), fillRect.height(),fillPixel); referenceImage = device->convertToQImage(0); KisTransaction transaction1(0, device); // qDebug()<<"CLEARING"; device->clear(clearRect); transaction1.revert(); resultImage = device->convertToQImage(0); QVERIFY(resultImage == referenceImage); KisPaintDeviceSP clone = new KisPaintDevice(*device); KisTransaction transaction(0, clone); // qDebug()<<"CLEARING"; clone->clear(clearRect); transaction.revert(); resultImage = clone->convertToQImage(0); QVERIFY(resultImage == referenceImage); } void KisPaintDeviceTest::testSharedDataManager() { QRect fillRect(0,0,100,100); quint8 fillPixel[4]={255,255,255,255}; QRect clearRect(10,10,20,20); QImage srcImage; QImage dstImage; const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP srcDevice = new KisPaintDevice(cs); srcDevice->setX(10); srcDevice->setY(20); srcDevice->fill(fillRect.left(), fillRect.top(), fillRect.width(), fillRect.height(),fillPixel); KisPaintDeviceSP dstDevice = new KisPaintDevice(srcDevice->dataManager(), srcDevice); QVERIFY(srcDevice->extent() == dstDevice->extent()); QVERIFY(srcDevice->exactBounds() == dstDevice->exactBounds()); QVERIFY(srcDevice->defaultBounds() == dstDevice->defaultBounds()); QVERIFY(srcDevice->x() == dstDevice->x()); QVERIFY(srcDevice->y() == dstDevice->y()); srcImage = srcDevice->convertToQImage(0); dstImage = dstDevice->convertToQImage(0); QVERIFY(srcImage == dstImage); srcDevice->clear(clearRect); srcImage = srcDevice->convertToQImage(0); dstImage = dstDevice->convertToQImage(0); QVERIFY(srcImage == dstImage); } void KisPaintDeviceTest::testTranslate() { QRect fillRect(0,0,64,64); quint8 fillPixel[4]={255,255,255,255}; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP device = new KisPaintDevice(cs); device->fill(fillRect.left(), fillRect.top(), fillRect.width(), fillRect.height(),fillPixel); device->setX(-10); device->setY(10); QCOMPARE(device->exactBounds(), QRect(-10,10,64,64)); QCOMPARE(device->extent(), QRect(-10,10,64,64)); } void KisPaintDeviceTest::testOpacity() { // blt a semi-transparent image on a white paint device QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP fdev = new KisPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); KisPainter gc(dev); gc.bitBlt(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png"); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) { checkResult.save("kis_paint_device_test_test_blt_fixed_opactiy_expected.png"); result.save("kis_paint_device_test_test_blt_fixed_opacity_result.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisPaintDeviceTest::testExactBoundsWeirdNullAlphaCase() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->exactBounds().isEmpty()); dev->fill(QRect(10,10,10,10), KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), QRect(10,10,10,10)); const quint8 weirdPixelData[4] = {0,10,0,0}; KoColor weirdColor(weirdPixelData, cs); dev->setPixel(6,6,weirdColor); // such weird pixels should not change our opinion about // device's size QCOMPARE(dev->exactBounds(), QRect(10,10,10,10)); } void KisPaintDeviceTest::benchmarkExactBoundsNullDefaultPixel() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->exactBounds().isEmpty()); QRect fillRect(60,60, 1930, 1930); dev->fill(fillRect, KoColor(Qt::white, cs)); QRect measuredRect; QBENCHMARK { // invalidate the cache dev->setDirty(); measuredRect = dev->exactBounds(); } QCOMPARE(measuredRect, fillRect); } QTEST_KDEMAIN(KisPaintDeviceTest, GUI) #include "kis_paint_device_test.moc" diff --git a/krita/image/tests/kis_paint_layer_test.cpp b/krita/image/tests/kis_paint_layer_test.cpp index 573ed5f603a..bb91011bf87 100644 --- a/krita/image/tests/kis_paint_layer_test.cpp +++ b/krita/image/tests/kis_paint_layer_test.cpp @@ -1,105 +1,105 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_paint_layer_test.h" #include #include #include #include #include #include "kis_group_layer.h" #include "kis_types.h" #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_transparency_mask.h" #include "testutil.h" #include "kis_selection.h" #include "kis_fill_painter.h" #include "kis_pixel_selection.h" #include void KisPaintLayerTest::testProjection() { QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, qimage.width(), qimage.height(), cs, "merge test"); KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8); layer->paintDevice()->convertFromQImage(qimage, 0, 0, 0); image->addNode(layer.data()); // Make sure the projection and the paint device are the same -- we don't have masks yet QVERIFY(layer->paintDevice().data() == layer->projection().data()); KisTransparencyMaskSP transparencyMask = new KisTransparencyMask(); transparencyMask->initSelection(0, layer); transparencyMask->selection()->getOrCreatePixelSelection()->invert(); image->addNode(transparencyMask.data(), layer.data()); // Now there are masks. Verify that Q_ASSERT(layer->hasEffectMasks()); // And now we're going to update the projection, but nothing is dirty yet layer->updateProjection(qimage.rect()); // Which also means that the projection is no longer the paint device QVERIFY(layer->paintDevice().data() != layer->projection().data()); // Now the machinery will start to roll layer->setDirty(qimage.rect()); // And now we're going to update the projection, but nothing is dirty yet layer->updateProjection(qimage.rect()); // Which also means that the projection is no longer the paint device QVERIFY(layer->paintDevice().data() != layer->projection().data()); // And the projection is no longer 0, because while we've updated it, nothing is dirty, // so nothing gets updated QVERIFY(layer->projection().data() != 0); // The selection is initially empty, so after an update, all pixels are still visible layer->updateProjection(qimage.rect()); // We've inverted the mask, so now nothing is seen KisRectConstIteratorSP it = layer->projection()->createRectConstIteratorNG(0, 0, qimage.width(), qimage.height()); do { QVERIFY(cs->opacityU8(it->oldRawData()) == OPACITY_TRANSPARENT_U8); } while (it->nextPixel()); // Now fill the layer with some opaque pixels transparencyMask->select(qimage.rect()); transparencyMask->setDirty(qimage.rect()); image->waitForDone(); layer->projection()->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("aaa.png"); // Nothing is transparent anymore, so the projection and the paint device should be identical again QPoint errpoint; if (!TestUtil::compareQImages(errpoint, qimage, layer->projection()->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } QTEST_KDEMAIN(KisPaintLayerTest, GUI) #include "kis_paint_layer_test.moc" diff --git a/krita/image/tests/kis_selection_test.cpp b/krita/image/tests/kis_selection_test.cpp index 27049c803e0..dd297b1dfec 100644 --- a/krita/image/tests/kis_selection_test.cpp +++ b/krita/image/tests/kis_selection_test.cpp @@ -1,270 +1,270 @@ /* * Copyright (c) 2007 Sven Langkamp * * 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 * MERCHANTABILITY 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 "kis_selection_test.h" #include #include #include #include #include #include "kis_datamanager.h" #include "kis_pixel_selection.h" #include "kis_selection.h" #include "kis_fill_painter.h" #include "kis_mask.h" #include "kis_image.h" #include "kis_transparency_mask.h" #include "testutil.h" void KisSelectionTest::testSelectionComponents() { KisSelectionSP selection = new KisSelection(); QVERIFY(selection->hasPixelSelection() == false); QVERIFY(selection->hasShapeSelection() == false); QVERIFY(selection->pixelSelection() == 0); QVERIFY(selection->shapeSelection() == 0); KisPixelSelectionSP pixelSelection = selection->getOrCreatePixelSelection(); QVERIFY(selection->pixelSelection() == pixelSelection); QVERIFY(selection->hasPixelSelection() == true); /* KisMaskSP mask = new KisTransparencyMask(); mask->select(QRect(0, 0, 100, 100)); QCOMPARE(mask->selection()->selectedRect(), QRect(0,0,128, 128)); QCOMPARE(mask->selection()->selectedExactRect(), QRect(0, 0, 100, 100)); selection = new KisSelection(0, mask); selection->updateProjection(); QVERIFY(selection->hasPixelSelection() == true); QCOMPARE(selection->selectedRect(), QRect(0,0,128, 128)); QCOMPARE(selection->selectedExactRect(), QRect(0, 0, 100, 100)); */ } void KisSelectionTest::testSelectionActions() { KisPixelSelectionSP pixelSelection = new KisPixelSelection(); KisSelectionSP selection = new KisSelection(); QVERIFY(selection->hasPixelSelection() == false); QVERIFY(selection->hasShapeSelection() == false); selection->setPixelSelection(pixelSelection); pixelSelection->select(QRect(0, 0, 20, 20)); KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); tmpSel->select(QRect(10, 0, 20, 20)); pixelSelection->applySelection(tmpSel, SELECTION_ADD); QCOMPARE(pixelSelection->selectedExactRect(), QRect(0, 0, 30, 20)); selection->updateProjection(); QCOMPARE(selection->selectedExactRect(), QRect(0, 0, 30, 20)); pixelSelection->clear(); pixelSelection->select(QRect(0, 0, 20, 20)); pixelSelection->applySelection(tmpSel, SELECTION_SUBTRACT); selection->updateProjection(); QCOMPARE(selection->selectedExactRect(), QRect(0, 0, 10, 20)); pixelSelection->clear(); selection->updateProjection(); pixelSelection->select(QRect(0, 0, 20, 20)); pixelSelection->applySelection(tmpSel, SELECTION_INTERSECT); selection->updateProjection(); QCOMPARE(selection->selectedExactRect(), QRect(10, 0, 10, 20)); } void KisSelectionTest::testInvertSelection() { KisSelectionSP selection = new KisSelection(); KisPixelSelectionSP pixelSelection = selection->getOrCreatePixelSelection(); pixelSelection->select(QRect(20, 20, 20, 20)); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 30, 30), MAX_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MIN_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 512, 512), MIN_SELECTED); pixelSelection->invert(); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 100, 100), MAX_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 22, 22), MIN_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MAX_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 512, 512), MAX_SELECTED); pixelSelection->convertToQImage(0, 0, 0, 100, 100).save("yyy.png"); // XXX: This should happen automatically selection->updateProjection(); selection->projection()->convertToQImage(0, 0, 0, 100, 100).save("zzz.png"); QCOMPARE(selection->selectedExactRect(), QRect(qint32_MIN/2, qint32_MIN/2, qint32_MAX, qint32_MAX)); QCOMPARE(selection->selectedRect(), QRect(qint32_MIN/2, qint32_MIN/2, qint32_MAX, qint32_MAX)); QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 100, 100), MAX_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 22, 22), MIN_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 10, 10), MAX_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 0, 0), MAX_SELECTED); QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 512, 512), MAX_SELECTED); } void KisSelectionTest::testInvertSelectionSemi() { KisSelectionSP selection = new KisSelection(); KisPixelSelectionSP pixelSelection = selection->getOrCreatePixelSelection(); quint8 selectedness = 42; pixelSelection->select(QRect(20, 20, 20, 20), selectedness); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 30, 30), selectedness); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MIN_SELECTED); pixelSelection->invert(); quint8 invertedSelectedness = MAX_SELECTED - selectedness; QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 30, 30), invertedSelectedness); QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MAX_SELECTED); selection->updateProjection(); QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 30, 30), invertedSelectedness); QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 0, 0), MAX_SELECTED); } void KisSelectionTest::testUpdateSelectionProjection() { KisSelectionSP selection = new KisSelection(); QVERIFY(selection->selectedExactRect().isNull()); // Now fill the layer with some opaque pixels KisFillPainter gc(selection->getOrCreatePixelSelection()); gc.fillRect(QRect(0, 0, 100, 100), KoColor(QColor(0, 0, 0, 0), KoColorSpaceRegistry::instance()->rgb8()), MAX_SELECTED); gc.end(); QVERIFY(selection->pixelSelection()->selectedExactRect() == QRect(0, 0, 100, 100)); selection->updateProjection(); QCOMPARE(selection->pixelSelection()->selectedExactRect(), QRect(0, 0, 100, 100)); QCOMPARE(selection->selectedExactRect(), QRect(0, 0, 100, 100)); } void KisSelectionTest::testUpdatePixelSelection() { KisSelectionSP selection = new KisSelection(); KisPixelSelectionSP pSel = selection->getOrCreatePixelSelection(); pSel->select(QRect(0, 0, 348, 212)); QVERIFY(selection->pixelSelection()->selectedExactRect() == QRect(0, 0, 348, 212)); selection->updateProjection(QRect(0, 0, 348, 212)); for (int i = 0; i < 212; ++i) { for (int j = 0; j < 348; ++j) { QVERIFY(selection->selected(j, i) == MAX_SELECTED); } } } void KisSelectionTest::testCopy() { KisSelectionSP sel = new KisSelection(); sel->getOrCreatePixelSelection()->select(QRect(10, 10, 200, 200), 128); KisSelectionSP sel2 = new KisSelection(*sel.data()); QCOMPARE(sel2->selectedExactRect(), sel->selectedExactRect()); QPoint errpoint; if (!TestUtil::comparePaintDevices(errpoint, sel->projection(), sel2->projection())) { sel2->projection()->convertToQImage(0, 0, 0, 200, 200).save("merge_visitor6.png"); QFAIL(QString("Failed to copy selection, first different pixel: %1,%2 ") .arg(errpoint.x()) .arg(errpoint.y()) - .toAscii()); + .toLatin1()); } } void KisSelectionTest::testSelectionExactBounds() { QRect referenceImageRect(0,0,1000,1000); QRect referenceDeviceRect(10,10,1000,1000); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, referenceImageRect.width(), referenceImageRect.height(), cs, "stest"); KisPaintDeviceSP device = new KisPaintDevice(cs); device->fill(referenceDeviceRect, KoColor(Qt::white, cs)); QCOMPARE(device->exactBounds(), referenceDeviceRect); KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(device, image)); quint8 defaultPixel = MAX_SELECTED; selection->projection()->setDefaultPixel(&defaultPixel); QCOMPARE(selection->selectedExactRect(), referenceImageRect | referenceDeviceRect); } void KisSelectionTest::testSetParentNodeAfterCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, 100, 100, cs, "stest"); KisSelectionSP selection = new KisSelection(); KisPixelSelectionSP pixelSelection = selection->getOrCreatePixelSelection(); QCOMPARE(selection->parentNode(), KisNodeWSP(0)); QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(0)); pixelSelection = new KisPixelSelection(); pixelSelection->setParentNode(image->root()); selection->setPixelSelection(pixelSelection); QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(0)); selection->setParentNode(image->root()); QCOMPARE(selection->parentNode(), KisNodeWSP(image->root())); QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(image->root())); } void KisSelectionTest::testSetParentNodeBeforeCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, 100, 100, cs, "stest"); KisSelectionSP selection = new KisSelection(); selection->setParentNode(image->root()); KisPixelSelectionSP pixelSelection = selection->getOrCreatePixelSelection(); QCOMPARE(selection->parentNode(), KisNodeWSP(image->root())); QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(image->root())); pixelSelection = new KisPixelSelection(); selection->setPixelSelection(pixelSelection); QCOMPARE(selection->parentNode(), KisNodeWSP(image->root())); QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(image->root())); } QTEST_KDEMAIN(KisSelectionTest, NoGUI) #include "kis_selection_test.moc" diff --git a/krita/image/tests/kis_transform_worker_test.cpp b/krita/image/tests/kis_transform_worker_test.cpp index 2c5bf9ab25d..3f602ad4d0f 100644 --- a/krita/image/tests/kis_transform_worker_test.cpp +++ b/krita/image/tests/kis_transform_worker_test.cpp @@ -1,856 +1,856 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 * MERCHANTABILITY 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 "kis_transform_worker_test.h" #include #include #include #include #include #include #include "kis_types.h" #include "kis_image.h" #include "kis_filter_strategy.h" #include "kis_paint_device.h" #include "kis_transform_worker.h" #include "testutil.h" #include "kis_transaction.h" #include "kis_random_accessor_ng.h" void KisTransformWorkerTest::testCreation() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisTransformWorker tw(dev, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.5, 0, 0, updater, filter, true); } void KisTransformWorkerTest::testMirrorX() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); KisTransformWorker::mirrorX(dev2); KisTransformWorker::mirrorX(dev2); KisTransformWorker::mirrorX(dev2); QImage result = dev2->convertToQImage(0, 0, 0, image.width(), image.height()); image = image.mirrored(true, false); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { // They are the same, but should be mirrored image.save("mirror_test_1_source.png"); result.save("mirror_test_1_result.png"); - QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testMirrorY() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); KisTransformWorker::mirrorY(dev2); KisTransformWorker::mirrorY(dev2); KisTransformWorker::mirrorY(dev2); QImage result = dev2->convertToQImage(0, 0, 0, image.width(), image.height()); image = image.mirrored(false, true ); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { // They are the same, but should be mirrored image.save("mirror_test_2_source.png"); result.save("mirror_test_2_result.png"); - QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testMirrorTransactionX() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); KisTransaction t("mirror", dev2); KisTransformWorker::mirrorX(dev2); t.end(); QImage result = dev2->convertToQImage(0, 0, 0, image.width(), image.height()); image = image.mirrored(true, false); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { // They are the same, but should be mirrored image.save("mirror_test_3_source.png"); result.save("mirror_test_3_result.png"); - QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testMirrorTransactionY() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); KisTransaction t("mirror", dev2); KisTransformWorker::mirrorY(dev2); t.end(); QImage result = dev2->convertToQImage(0, 0, 0, image.width(), image.height()); image = image.mirrored(false, true); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { // They are the same, but should be mirrored image.save("mirror_test_4_source.png"); result.save("mirror_test_4_result.png"); - QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testScaleUp() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 2.4, 2.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QCOMPARE(rc.width(), qRound(image.width() * 2.4)); QCOMPARE(rc.height(), qRound(image.height() * 2.4)); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "test_scaleup_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("test_scaleup_source.png"); result.save("test_scaleup_result.png"); - QFAIL(QString("Failed to scale the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to scale the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testXScaleUp() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() == image.width() * 2); QVERIFY(rc.height() == image.height()); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaleupx_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("test_x_scaleup_source.png"); result.save("test_x_scaleup_result.png"); - QFAIL(QString("Failed to scale up the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to scale up the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testYScaleUp() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() == image.width()); QVERIFY(rc.height() == image.height() * 2); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaleupy_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("test_y_scaleup_source.png"); result.save("test_y_scaleup_result.png"); - QFAIL(QString("Failed to scale up the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to scale up the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testIdentity() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() ==image.width()); QVERIFY(rc.height() == image.height()); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("test_identity_source.png"); result.save("test_identity_result.png"); - QFAIL(QString("Failed to apply identity transformation to image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to apply identity transformation to image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testScaleDown() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 0.123, 0.123, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() == qRound(image.width() * 0.123)); QVERIFY(rc.height() == qRound(image.height() * 0.123)); // KisTransaction t2("test", dev); // KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y()); // for(int x = rc.x(); x < rc.width(); ++x) { // for(int y = rc.y(); y < rc.height(); ++y) { // ac->moveTo(x, y); // cs->setOpacity(ac->rawData(), 0.5, 1); // } // } // t2.end(); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "test_scaledown_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("test_scaledown_source.png"); result.save("test_scaledown_result.png"); - QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testXScaleDown() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 0.123, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() == qRound(image.width() * 0.123)); QVERIFY(rc.height() == image.height() - 1); // the height is reduced by 1 because in the source image // at the bottom line most pixels (except 1 or 2) are // entirely transparent. // when scaling down the image by ~ 1/10, the few non-tranparent // pixels disappear when "mixed" with the transparent ones // around // KisTransaction t2("test", dev); // KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y()); // for(int x = rc.x(); x < rc.width(); ++x) { // for(int y = rc.y(); y < rc.height(); ++y) { // ac->moveTo(x, y); // cs->setOpacity(ac->rawData(), 0.5, 1); // } // } // t2.end(); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaledownx_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("scaledownx_source.png"); result.save("scaledownx_result.png"); - QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testYScaleDown() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 1.0, 0.123, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() == image.width()); QVERIFY(rc.height() == qRound(image.height() * 0.123)); // KisTransaction t2("test", dev); // KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y()); // for(int x = rc.x(); x < rc.width(); ++x) { // for(int y = rc.y(); y < rc.height(); ++y) { // ac->moveTo(x, y); // cs->setOpacity(ac->rawData(), 0.5, 1); // } // } // t2.end(); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaledowny_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("scaledowny_source.png"); result.save("scaledowny_result.png"); - QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testXShear() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 1.0, 1.0, 1.0, 0.0, 300., 200., 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() == 959); QVERIFY(rc.height() == image.height()); // KisTransaction t2("test", dev); // KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y()); // for(int x = rc.x(); x < rc.width(); ++x) { // for(int y = rc.y(); y < rc.height(); ++y) { // ac->moveTo(x, y); // cs->setOpacity(ac->rawData(), 0.5, 1); // } // } // t2.end(); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "shearx_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("shearx_source.png"); result.save("shearx_result.png"); - QFAIL(QString("Failed to shear the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to shear the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testYShear() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 1.0, 1.0, 0.0, 1.0, 300., 200., 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QVERIFY(rc.width() == image.width()); QVERIFY(rc.height() == 959); // KisTransaction t2("test", dev); // KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y()); // for(int x = rc.x(); x < rc.width(); ++x) { // for(int y = rc.y(); y < rc.height(); ++y) { // ac->moveTo(x, y); // cs->setOpacity(ac->rawData(), 0.5, 1); // } // } // t2.end(); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "sheary_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("sheary_source.png"); result.save("sheary_result.png"); - QFAIL(QString("Failed to shear the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to shear the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } bool fuzzyCompareRects(const QRectF rc1, const QRectF rc2, qreal accuracy) { bool result = qAbs(rc1.x() - rc2.x()) < accuracy && qAbs(rc1.y() - rc2.y()) < accuracy && qAbs(rc1.width() - rc2.width()) < 2 * accuracy && qAbs(rc1.height() - rc2.height()) < 2 * accuracy; if(!result) { qDebug() << "Failed to fuzzy compare rects"; qDebug() << "\t" << ppVar(accuracy); qDebug() << "\t" << "actual " << rc1; qDebug() << "\t" << "expected" << rc2; qDebug() << "+---------------------------+"; } return result; } void KisTransformWorkerTest::testMatrices() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); KisFilterStrategy *filter = new KisBoxFilterStrategy(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QRect fillRect(0,0,300,200); KoColor fillColor(Qt::white, cs); dev->fill(fillRect, fillColor); qreal scaleX = 1.5, scaleY = 1.5; qreal shearX = 1, shearY = 1.33; qreal shearOrigX = 150, shearOrigY = 100; qreal angle = M_PI/6; qreal transX = 77, transY = 33; KisTransaction t("test", dev); KisTransformWorker tw(dev, scaleX, scaleY, shearX, shearY, shearOrigX, shearOrigY, angle, transX, transY, updater, filter, true); tw.run(); t.end(); QPolygonF referencePolygon = tw.transform().map(QPolygonF(QRectF(fillRect))); QVERIFY(fuzzyCompareRects(dev->exactBounds(), referencePolygon.boundingRect(), 3)); } void KisTransformWorkerTest::testRotation() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBoxFilterStrategy(); KisTransaction t("test", dev); KisTransformWorker tw(dev, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 34, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); QCOMPARE(rc.width(), 702); QCOMPARE(rc.height(), 629); // KisTransaction t2("test", dev); // KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y()); // for(int x = rc.x(); x < rc.width(); ++x) { // for(int y = rc.y(); y < rc.height(); ++y) { // ac->moveTo(x, y); // cs->setOpacity(ac->rawData(), 0.5, 1); // } // } // t2.end(); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QPoint errpoint; image.load(QString(FILES_DATA_DIR) + QDir::separator() + "rotate_result.png"); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("rotate_source.png"); result.save("rotate_result.png"); - QFAIL(QString("Failed to rotate the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to rotate the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::testRotationSpecialCases() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); KisFilterStrategy *filter = new KisBoxFilterStrategy(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QRect fillRect(0,0,600,300); KoColor fillColor(Qt::white, cs); dev->fill(fillRect, fillColor); qreal scaleX = 0.5, scaleY = 0.5; qreal shearX = 0, shearY = 0; qreal shearOrigX = 0, shearOrigY = 0; qreal angle = M_PI; qreal transX = 300, transY = 150; KisTransaction t("test", dev); KisTransformWorker tw(dev, scaleX, scaleY, shearX, shearY, shearOrigX, shearOrigY, angle, transX, transY, updater, filter, true); tw.run(); t.end(); QCOMPARE(dev->exactBounds(), QRect(0,0,300,150)); } void KisTransformWorkerTest::testScaleUp5times() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); QImage image(QSize(2000,2000), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(Qt::green).rgba()); int checkSize = 20; QImage tile(checkSize * 2, checkSize * 2, QImage::Format_ARGB32_Premultiplied); QPainter pt(&tile); pt.fillRect(tile.rect(), Qt::green); pt.fillRect(0, 0, checkSize, checkSize, Qt::white); pt.fillRect(checkSize, checkSize, checkSize, checkSize, Qt::white); pt.end(); pt.begin(&image); pt.setBrush(QBrush(tile)); pt.drawRect(image.rect()); pt.end(); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); KisFilterStrategy * filter = new KisBicubicFilterStrategy(); KisTransaction t("test", dev); qreal SCALE = 5.0; KisTransformWorker tw(dev, SCALE, SCALE, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, updater, filter, true); tw.run(); t.end(); QRect rc = dev->exactBounds(); #if 0 // here you can check the input and result images image.save("test_scale_2000_2000_input.bmp"); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); result.save("test_scale_2000_2000_" + QString::number(SCALE) + "_result.bmp"); #endif QCOMPARE(rc.width(), qRound(image.width() * SCALE)); QCOMPARE(rc.height(), qRound(image.height() * SCALE)); } void KisTransformWorkerTest::rotateNone() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "transform_rotate_test.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); KisPaintDeviceSP tmpdev1 = new KisPaintDevice(dev2->colorSpace()); tmpdev1->setDefaultPixel(dev2->defaultPixel()); int lastProgressReport = 0; int progressTotalSteps = 0; int progresStep = 0; QRect boundRect = dev2->exactBounds(); KisTransaction t("mirror", dev2); QRect rc = KisTransformWorker::rotateNone(dev2, tmpdev1, boundRect, 0, lastProgressReport, progressTotalSteps, progresStep); t.end(); QImage result = tmpdev1->convertToQImage(0, rc.x(), rc.y(), image.width(), image.height()); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { // They are the same, but should be mirrored image.save("rotate_none_test_1_source.png"); result.save("rotate_none_1_result.png"); - QFAIL(QString("Failed to rotate none the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to rotate none the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::rotate90Left() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "transform_rotate_test.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); KisPaintDeviceSP tmpdev1 = new KisPaintDevice(dev2->colorSpace()); tmpdev1->setDefaultPixel(dev2->defaultPixel()); int lastProgressReport = 0; int progressTotalSteps = 0; int progresStep = 0; QRect boundRect = dev2->exactBounds(); KisTransaction t("rotate left 90", dev2); QRect rc = KisTransformWorker::rotateLeft90(dev2, tmpdev1, boundRect, 0, lastProgressReport, progressTotalSteps, progresStep); t.end(); QImage result = tmpdev1->convertToQImage(0, rc.x(), rc.y(), image.width(), image.height()); QTransform tf; QImage rotatedimage = image.transformed(tf.rotate(270)); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, rotatedimage, result)) { // They are the same, but should be mirrored image.save("rotate_90_left_test_1_source.png"); rotatedimage.save("rotate_90_left_test_1_rotated_source.png"); result.save("rotate_90_left_test_1_result.png"); - QFAIL(QString("Failed to rotate 90 left the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to rotate 90 left the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::rotate90Right() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "transform_rotate_test.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); int lastProgressReport = 0; int progressTotalSteps = 0; int progresStep = 0; QRect boundRect = dev2->exactBounds(); KisPaintDeviceSP tmpdev1 = new KisPaintDevice(dev2->colorSpace()); tmpdev1->setDefaultPixel(dev2->defaultPixel()); KisTransaction t("rotate right 90", dev2); QRect rc = KisTransformWorker::rotateRight90(dev2, tmpdev1, boundRect, 0, lastProgressReport, progressTotalSteps, progresStep); t.end(); QTransform tf; QImage rotatedimage = image.transformed(tf.rotate(90)); QImage result = tmpdev1->convertToQImage(0, rc.x(), rc.y(), image.width(), image.height()); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, rotatedimage, result)) { // They are the same, but should be mirrored image.save("rotate_90_right_test_1_source.png"); rotatedimage.save("rotate_90_right_test_1_rotated_source.png"); result.save("rotate_90_right_1_result.png"); - QFAIL(QString("Failed to rotate 90 right the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to rotate 90 right the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisTransformWorkerTest::rotate180() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "transform_rotate_test.png"); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->convertFromQImage(image, 0); int lastProgressReport = 0; int progressTotalSteps = 0; int progresStep = 0; QRect boundRect = dev2->exactBounds(); KisPaintDeviceSP tmpdev1 = new KisPaintDevice(dev2->colorSpace()); tmpdev1->setDefaultPixel(dev2->defaultPixel()); KisTransaction t("rotate 180", dev2); QRect rc = KisTransformWorker::rotate180(dev2, tmpdev1, boundRect, 0, lastProgressReport, progressTotalSteps, progresStep); t.end(); QImage result = tmpdev1->convertToQImage(0, rc.x(), rc.y(), image.width(), image.height()); QTransform tf; QImage rotatedimage = image.transformed(tf.rotate(180)); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, rotatedimage, result)) { // They are the same, but should be mirrored image.save("rotate_180_1_source.png"); rotatedimage.save("rotate_180_1_rotated_source.png"); result.save("rotate_180_1_result.png"); - QFAIL(QString("Failed to rotate 180 the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to rotate 180 the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } QTEST_KDEMAIN(KisTransformWorkerTest, GUI) #include "kis_transform_worker_test.moc" diff --git a/krita/image/tests/kis_transparency_mask_test.cpp b/krita/image/tests/kis_transparency_mask_test.cpp index 23308bb3a30..41325216d47 100644 --- a/krita/image/tests/kis_transparency_mask_test.cpp +++ b/krita/image/tests/kis_transparency_mask_test.cpp @@ -1,161 +1,161 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 * MERCHANTABILITY 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 "kis_transparency_mask_test.h" #include #include "kis_transparency_mask.h" #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_fill_painter.h" #include "testutil.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include "kis_image.h" #define IMAGE_WIDTH 1000 #define IMAGE_HEIGHT 1000 KisPaintDeviceSP createDevice() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisFillPainter gc(dev); KoColor c(Qt::red, dev->colorSpace()); gc.fillRect(0, 0, 100, 100, c); c = KoColor(Qt::blue, dev->colorSpace()); gc.fillRect(100, 0, 100, 100, c); gc.end(); return dev; } void KisTransparencyMaskTest::testCreation() { KisTransparencyMask test; } #define initImage(image, layer, device, mask) do { \ image = new KisImage(0, IMAGE_WIDTH, IMAGE_HEIGHT, 0, "tests"); \ device = createDevice(); \ layer = new KisPaintLayer(image, "paint1", 100, device); \ mask = new KisTransparencyMask(); \ image->addNode(layer); \ image->addNode(mask, layer); \ } while(0) void KisTransparencyMaskTest::testApply() { QPoint errpoint; KisImageSP image; KisPaintLayerSP layer; KisPaintDeviceSP dev; KisTransparencyMaskSP mask; // Everything is selected initImage(image, layer, dev, mask); mask->initSelection(0, layer); mask->apply(dev, QRect(0, 0, 200, 100)); QImage qimage = dev->convertToQImage(0, 0, 0, 200, 100); if (!TestUtil::compareQImages(errpoint, QImage(QString(FILES_DATA_DIR) + QDir::separator() + "transparency_mask_test_2.png"), qimage)) { - QFAIL(QString("Failed to mask out image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to mask out image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Invert the mask, so that nothing will be selected, then select a rect initImage(image, layer, dev, mask); mask->initSelection(0, layer); mask->selection()->getOrCreatePixelSelection()->invert(); mask->apply(dev, QRect(0, 0, 200, 100)); qimage = dev->convertToQImage(0, 0, 0, 200, 100); if (!TestUtil::compareQImages(errpoint, QImage(QString(FILES_DATA_DIR) + QDir::separator() + "transparency_mask_test_1.png"), qimage)) { - QFAIL(QString("Failed to mask in image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to mask in image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } initImage(image, layer, dev, mask); mask->initSelection(0, layer); mask->selection()->getOrCreatePixelSelection()->invert(); mask->select(QRect(50, 0, 100, 100)); mask->apply(dev, QRect(0, 0, 200, 100)); qimage = dev->convertToQImage(0, 0, 0, 200, 100); if (!TestUtil::compareQImages(errpoint, QImage(QString(FILES_DATA_DIR) + QDir::separator() + "transparency_mask_test_3.png"), qimage)) { - QFAIL(QString("Failed to apply partial mask, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to apply partial mask, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } #include "kis_full_refresh_walker.h" #include "kis_async_merger.h" void KisTransparencyMaskTest::testMoveParentLayer() { KisImageSP image; KisPaintLayerSP layer; KisPaintDeviceSP dev; KisTransparencyMaskSP mask; initImage(image, layer, dev, mask); mask->initSelection(0, layer); mask->selection()->getOrCreatePixelSelection()->invert(); mask->select(QRect(50, 50, 100, 100)); KisFullRefreshWalker walker(image->bounds()); KisAsyncMerger merger; walker.collectRects(layer, image->bounds()); merger.startMerge(walker); // image->projection()->convertToQImage(0, 0,0,300,300).save("proj_before.png"); QRect initialRect(0,0,200,100); QCOMPARE(layer->exactBounds(), initialRect); QCOMPARE(image->projection()->exactBounds(), QRect(50,50,100,50)); layer->setX(100); layer->setY(100); qDebug() << "Sel. rect before:" << mask->selection()->selectedRect(); mask->setX(100); mask->setY(100); qDebug() << "Sel. rect after:" << mask->selection()->selectedRect(); QRect finalRect(100,100,200,100); QCOMPARE(layer->exactBounds(), finalRect); walker.collectRects(layer, initialRect | finalRect); merger.startMerge(walker); // image->projection()->convertToQImage(0, 0,0,300,300).save("proj_after.png"); QCOMPARE(image->projection()->exactBounds(), QRect(150,150,100,50)); } QTEST_KDEMAIN(KisTransparencyMaskTest, GUI) #include "kis_transparency_mask_test.moc" diff --git a/krita/image/tiles3/kis_tiled_data_manager.cc b/krita/image/tiles3/kis_tiled_data_manager.cc index ab89aa25d0b..35102bfcfc8 100644 --- a/krita/image/tiles3/kis_tiled_data_manager.cc +++ b/krita/image/tiles3/kis_tiled_data_manager.cc @@ -1,770 +1,770 @@ /* * Copyright (c) 2004 C. Boemann * (c) 2009 Dmitry Kazakov * (c) 2010 Cyrille Berger * * 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 * MERCHANTABILITY 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 #include #include #include "kis_tile.h" #include "kis_tiled_data_manager.h" #include "kis_tiled_data_manager_p.h" #include "kis_memento_manager.h" #include "swap/kis_legacy_tile_compressor.h" #include "swap/kis_tile_compressor_factory.h" #include #include "kis_global.h" //#include "kis_debug.h" /* The data area is divided into tiles each say 64x64 pixels (defined at compiletime) * The tiles are laid out in a matrix that can have negative indexes. * The matrix grows automatically if needed (a call for writeacces to a tile * outside the current extent) * Even though the matrix has grown it may still not contain tiles at specific positions. * They are created on demand */ KisTiledDataManager::KisTiledDataManager(quint32 pixelSize, const quint8 *defaultPixel) : m_lock(QReadWriteLock::NonRecursive) { /* See comment in destructor for details */ m_mementoManager = new KisMementoManager(); m_hashTable = new KisTileHashTable(m_mementoManager); m_pixelSize = pixelSize; m_defaultPixel = new quint8[m_pixelSize]; setDefaultPixel(defaultPixel); m_extentMinX = qint32_MAX; m_extentMinY = qint32_MAX; m_extentMaxX = qint32_MIN; m_extentMaxY = qint32_MIN; } KisTiledDataManager::KisTiledDataManager(const KisTiledDataManager &dm) : KisShared(), m_lock(QReadWriteLock::NonRecursive) { /* See comment in destructor for details */ /* We do not clone the history of the device, there is no usecase for it */ m_mementoManager = new KisMementoManager(); m_mementoManager->setDefaultTileData(dm.m_hashTable->defaultTileData()); m_hashTable = new KisTileHashTable(*dm.m_hashTable, m_mementoManager); m_pixelSize = dm.m_pixelSize; m_defaultPixel = new quint8[m_pixelSize]; /** * We won't call setDefaultTileData here, as defaultTileDatas * has already been made shared in m_hashTable(dm->m_hashTable) */ memcpy(m_defaultPixel, dm.m_defaultPixel, m_pixelSize); m_extentMinX = dm.m_extentMinX; m_extentMinY = dm.m_extentMinY; m_extentMaxX = dm.m_extentMaxX; m_extentMaxY = dm.m_extentMaxY; } KisTiledDataManager::~KisTiledDataManager() { /** * Here is an explanation why we use hash table and The Memento Manager * dynamically allocated We need to destroy them in that very order. The * reason is that when hash table destroying all her child tiles they all * cry about it to The Memento Manager using a pointer. So The Memento * Manager sould be alive during that destruction. We could use shared * pointers instead, but they create too much overhead. */ delete m_hashTable; delete m_mementoManager; delete[] m_defaultPixel; } void KisTiledDataManager::setDefaultPixel(const quint8 *defaultPixel) { QWriteLocker locker(&m_lock); setDefaultPixelImpl(defaultPixel); } void KisTiledDataManager::setDefaultPixelImpl(const quint8 *defaultPixel) { KisTileData *td = KisTileDataStore::instance()->createDefaultTileData(pixelSize(), defaultPixel); m_hashTable->setDefaultTileData(td); m_mementoManager->setDefaultTileData(td); memcpy(m_defaultPixel, defaultPixel, pixelSize()); } bool KisTiledDataManager::write(KoStore *store) { QReadLocker locker(&m_lock); if (!store) return false; if(CURRENT_VERSION == LEGACY_VERSION) { char str[80]; sprintf(str, "%d\n", m_hashTable->numTiles()); store->write(str, strlen(str)); } else { writeTilesHeader(store, m_hashTable->numTiles()); } KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; KisAbstractTileCompressorSP compressor = KisTileCompressorFactory::create(CURRENT_VERSION); while ((tile = iter.tile())) { compressor->writeTile(tile, store); ++iter; } return true; } bool KisTiledDataManager::read(KoStore *store) { if (!store) return false; clear(); QWriteLocker locker(&m_lock); KisMementoSP nothing = m_mementoManager->getMemento(); QIODevice *stream = store->device(); if (!stream) { m_mementoManager->commit(); return false; } const qint32 maxLineLength = 79; // Legacy magic QByteArray line = stream->readLine(maxLineLength); line = line.trimmed(); quint32 numTiles; qint32 tilesVersion = LEGACY_VERSION; if (line[0] == 'V') { QList lineItems = line.split(' '); QString keyword = lineItems.takeFirst(); Q_ASSERT(keyword == "VERSION"); tilesVersion = lineItems.takeFirst().toInt(); if(!processTilesHeader(stream, numTiles)) return false; } else { numTiles = line.toUInt(); } KisAbstractTileCompressorSP compressor = KisTileCompressorFactory::create(tilesVersion); for (quint32 i = 0; i < numTiles; i++) { compressor->readTile(store, this); } m_mementoManager->commit(); return true; } bool KisTiledDataManager::writeTilesHeader(KoStore *store, quint32 numTiles) { QString buffer; buffer = QString("VERSION %1\n" "TILEWIDTH %2\n" "TILEHEIGHT %3\n" "PIXELSIZE %4\n" "DATA %5\n") .arg(CURRENT_VERSION) .arg(KisTileData::WIDTH) .arg(KisTileData::HEIGHT) .arg(pixelSize()) .arg(numTiles); - store->write(buffer.toAscii()); + store->write(buffer.toLatin1()); return true; } #define takeOneLine(stream, maxLine, keyword, value) \ do { \ QByteArray line = stream->readLine(maxLine); \ line = line.trimmed(); \ QList lineItems = line.split(' '); \ keyword = lineItems.takeFirst(); \ value = lineItems.takeFirst().toInt(); \ } while(0) \ bool KisTiledDataManager::processTilesHeader(QIODevice *stream, quint32 &numTiles) { /** * We assume that there is only one version of this header * possible. In case we invent something new, it'll be quite easy * to modify the behavior */ const qint32 maxLineLength = 25; const qint32 totalNumTests = 4; bool foundDataMark = false; qint32 testsPassed = 0; QString keyword; qint32 value; while(!foundDataMark && stream->canReadLine()) { takeOneLine(stream, maxLineLength, keyword, value); if (keyword == "TILEWIDTH") { if(value != KisTileData::WIDTH) goto wrongString; } else if (keyword == "TILEHEIGHT") { if(value != KisTileData::HEIGHT) goto wrongString; } else if (keyword == "PIXELSIZE") { if((quint32)value != pixelSize()) goto wrongString; } else if (keyword == "DATA") { numTiles = value; foundDataMark = true; } else { goto wrongString; } testsPassed++; } if(testsPassed != totalNumTests) { warnTiles << "Not enough fields of tiles header present" << testsPassed << "of" << totalNumTests; } return testsPassed == totalNumTests; wrongString: warnTiles << "Wrong string in tiles header:" << keyword << value; return false; } void KisTiledDataManager::purge(const QRect& area) { QWriteLocker locker(&m_lock); QList tilesToDelete; { const qint32 tileDataSize = KisTileData::HEIGHT * KisTileData::WIDTH * pixelSize(); const quint8 *defaultData = m_hashTable->defaultTileData()->data(); KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; while ((tile = iter.tile())) { if (tile->extent().intersects(area)) { tile->lockForRead(); if(memcmp(defaultData, tile->data(), tileDataSize) == 0) { tilesToDelete.push_back(tile); } tile->unlock(); } ++iter; } } foreach(KisTileSP tile, tilesToDelete) { m_hashTable->deleteTile(tile); } } quint8* KisTiledDataManager::duplicatePixel(qint32 num, const quint8 *pixel) { const qint32 pixelSize = this->pixelSize(); /* FIXME: Make a fun filling here */ quint8 *dstBuf = new quint8[num * pixelSize]; quint8 *dstIt = dstBuf; for (qint32 i = 0; i < num; i++) { memcpy(dstIt, pixel, pixelSize); dstIt += pixelSize; } return dstBuf; } void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel) { QWriteLocker locker(&m_lock); if (clearPixel == 0) clearPixel = m_defaultPixel; if (clearRect.isEmpty()) return; const qint32 pixelSize = this->pixelSize(); bool pixelBytesAreDefault = !memcmp(clearPixel, m_defaultPixel, pixelSize); bool pixelBytesAreTheSame = true; for (qint32 i = 0; i < pixelSize; ++i) { if (clearPixel[i] != clearPixel[0]) { pixelBytesAreTheSame = false; break; } } qint32 firstColumn = xToCol(clearRect.left()); qint32 lastColumn = xToCol(clearRect.right()); qint32 firstRow = yToRow(clearRect.top()); qint32 lastRow = yToRow(clearRect.bottom()); const quint32 rowStride = KisTileData::WIDTH * pixelSize; // Generate one row quint8 *clearPixelData = 0; quint32 maxRunLength = qMin(clearRect.width(), KisTileData::WIDTH); clearPixelData = duplicatePixel(maxRunLength, clearPixel); KisTileData *td = 0; if (clearRect.width() >= KisTileData::WIDTH && clearRect.height() >= KisTileData::HEIGHT) { if (pixelBytesAreDefault) /** * FIXME: Theoretical race condition * if setDefaultPixel has been called first */ td = m_hashTable->defaultTileData(); else td = KisTileDataStore::instance()->createDefaultTileData(pixelSize, clearPixel); td->acquire(); } for (qint32 row = firstRow; row <= lastRow; ++row) { for (qint32 column = firstColumn; column <= lastColumn; ++column) { QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT, KisTileData::WIDTH, KisTileData::HEIGHT); QRect clearTileRect = clearRect & tileRect; if (clearTileRect == tileRect) { // Clear whole tile m_hashTable->deleteTile(column, row); KisTileSP clearedTile = new KisTile(column, row, td, m_mementoManager); m_hashTable->addTile(clearedTile); updateExtent(column, row); } else { const qint32 lineSize = clearTileRect.width() * pixelSize; qint32 rowsRemaining = clearTileRect.height(); KisTileDataWrapper tw(this, clearTileRect.left(), clearTileRect.top(), KisTileDataWrapper::WRITE); quint8* tileIt = tw.data(); if (pixelBytesAreTheSame) { while (rowsRemaining > 0) { memset(tileIt, *clearPixelData, lineSize); tileIt += rowStride; rowsRemaining--; } } else { while (rowsRemaining > 0) { memcpy(tileIt, clearPixelData, lineSize); tileIt += rowStride; rowsRemaining--; } } } } } if (td) td->release(); delete[] clearPixelData; } void KisTiledDataManager::clear(QRect clearRect, quint8 clearValue) { quint8 *buf = new quint8[pixelSize()]; memset(buf, clearValue, pixelSize()); clear(clearRect, buf); delete[] buf; } void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *clearPixel) { clear(QRect(x, y, w, h), clearPixel); } void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 clearValue) { clear(QRect(x, y, w, h), clearValue); } void KisTiledDataManager::clear() { QWriteLocker locker(&m_lock); m_hashTable->clear(); m_extentMinX = qint32_MAX; m_extentMinY = qint32_MAX; m_extentMaxX = qint32_MIN; m_extentMaxY = qint32_MIN; } template void KisTiledDataManager::bitBltImpl(KisTiledDataManager *srcDM, const QRect &rect) { QWriteLocker locker(&m_lock); if (rect.isEmpty()) return; const qint32 pixelSize = this->pixelSize(); const quint32 rowStride = KisTileData::WIDTH * pixelSize; qint32 firstColumn = xToCol(rect.left()); qint32 lastColumn = xToCol(rect.right()); qint32 firstRow = yToRow(rect.top()); qint32 lastRow = yToRow(rect.bottom()); for (qint32 row = firstRow; row <= lastRow; ++row) { for (qint32 column = firstColumn; column <= lastColumn; ++column) { // this is the only variation in the template KisTileSP srcTile = useOldSrcData ? srcDM->getOldTile(column, row) : srcDM->getTile(column, row, false); QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT, KisTileData::WIDTH, KisTileData::HEIGHT); QRect cloneTileRect = rect & tileRect; if (cloneTileRect == tileRect) { // Clone whole tile m_hashTable->deleteTile(column, row); srcTile->lockForRead(); KisTileData *td = srcTile->tileData(); KisTileSP clonedTile = new KisTile(column, row, td, m_mementoManager); srcTile->unlock(); m_hashTable->addTile(clonedTile); updateExtent(column, row); } else { const qint32 lineSize = cloneTileRect.width() * pixelSize; qint32 rowsRemaining = cloneTileRect.height(); KisTileDataWrapper tw(this, cloneTileRect.left(), cloneTileRect.top(), KisTileDataWrapper::WRITE); srcTile->lockForRead(); // We suppose that the shift in both tiles is the same const quint8* srcTileIt = srcTile->data() + tw.offset(); quint8* dstTileIt = tw.data(); while (rowsRemaining > 0) { memcpy(dstTileIt, srcTileIt, lineSize); srcTileIt += rowStride; dstTileIt += rowStride; rowsRemaining--; } srcTile->unlock(); } } } } template void KisTiledDataManager::bitBltRoughImpl(KisTiledDataManager *srcDM, const QRect &rect) { QWriteLocker locker(&m_lock); if (rect.isEmpty()) return; qint32 firstColumn = xToCol(rect.left()); qint32 lastColumn = xToCol(rect.right()); qint32 firstRow = yToRow(rect.top()); qint32 lastRow = yToRow(rect.bottom()); for (qint32 row = firstRow; row <= lastRow; ++row) { for (qint32 column = firstColumn; column <= lastColumn; ++column) { /** * We are cloning whole tiles here so let's not be so boring * to check any borders :) */ // this is the only variation in the template KisTileSP srcTile = useOldSrcData ? srcDM->getOldTile(column, row) : srcDM->getTile(column, row, false); m_hashTable->deleteTile(column, row); srcTile->lockForRead(); KisTileData *td = srcTile->tileData(); KisTileSP clonedTile = new KisTile(column, row, td, m_mementoManager); srcTile->unlock(); m_hashTable->addTile(clonedTile); updateExtent(column, row); } } } void KisTiledDataManager::bitBlt(KisTiledDataManager *srcDM, const QRect &rect) { bitBltImpl(srcDM, rect); } void KisTiledDataManager::bitBltOldData(KisTiledDataManager *srcDM, const QRect &rect) { bitBltImpl(srcDM, rect); } void KisTiledDataManager::bitBltRough(KisTiledDataManager *srcDM, const QRect &rect) { bitBltRoughImpl(srcDM, rect); } void KisTiledDataManager::bitBltRoughOldData(KisTiledDataManager *srcDM, const QRect &rect) { bitBltRoughImpl(srcDM, rect); } void KisTiledDataManager::setExtent(qint32 x, qint32 y, qint32 w, qint32 h) { setExtent(QRect(x, y, w, h)); } void KisTiledDataManager::setExtent(QRect newRect) { QRect oldRect = extent(); newRect = newRect.normalized(); // Do nothing if the desired size is bigger than we currently are: // that is handled by the autoextending automatically if (newRect.contains(oldRect)) return; QWriteLocker locker(&m_lock); KisTileSP tile; QRect tileRect; { KisTileHashTableIterator iter(m_hashTable); while (!iter.isDone()) { tile = iter.tile(); tileRect = tile->extent(); if (newRect.contains(tileRect)) { //do nothing ++iter; } else if (newRect.intersects(tileRect)) { QRect intersection = newRect & tileRect; intersection.translate(- tileRect.topLeft()); const qint32 pixelSize = this->pixelSize(); tile->lockForWrite(); quint8* data = tile->data(); quint8* ptr; /* FIXME: make it faster */ for (int y = 0; y < KisTileData::HEIGHT; y++) { for (int x = 0; x < KisTileData::WIDTH; x++) { if (!intersection.contains(x, y)) { ptr = data + pixelSize * (y * KisTileData::WIDTH + x); memcpy(ptr, m_defaultPixel, pixelSize); } } } tile->unlock(); ++iter; } else { iter.deleteCurrent(); } } } recalculateExtent(); } void KisTiledDataManager::recalculateExtent() { m_extentMinX = qint32_MAX; m_extentMinY = qint32_MAX; m_extentMaxX = qint32_MIN; m_extentMaxY = qint32_MIN; KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; while ((tile = iter.tile())) { updateExtent(tile->col(), tile->row()); ++iter; } } void KisTiledDataManager::updateExtent(qint32 col, qint32 row) { const qint32 tileMinX = col * KisTileData::WIDTH; const qint32 tileMinY = row * KisTileData::HEIGHT; const qint32 tileMaxX = tileMinX + KisTileData::WIDTH - 1; const qint32 tileMaxY = tileMinY + KisTileData::HEIGHT - 1; m_extentMinX = qMin(m_extentMinX, tileMinX); m_extentMaxX = qMax(m_extentMaxX, tileMaxX); m_extentMinY = qMin(m_extentMinY, tileMinY); m_extentMaxY = qMax(m_extentMaxY, tileMaxY); } void KisTiledDataManager::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const { QReadLocker locker(&m_lock); x = m_extentMinX; y = m_extentMinY; w = (m_extentMaxX >= m_extentMinX) ? m_extentMaxX - m_extentMinX + 1 : 0; h = (m_extentMaxY >= m_extentMinY) ? m_extentMaxY - m_extentMinY + 1 : 0; } QRect KisTiledDataManager::extent() const { qint32 x, y, w, h; extent(x, y, w, h); return QRect(x, y, w, h); } QRegion KisTiledDataManager::region() const { QRegion region; KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; while ((tile = iter.tile())) { region += tile->extent(); ++iter; } return region; } void KisTiledDataManager::setPixel(qint32 x, qint32 y, const quint8 * data) { QWriteLocker locker(&m_lock); KisTileDataWrapper tw(this, x, y, KisTileDataWrapper::WRITE); memcpy(tw.data(), data, pixelSize()); } void KisTiledDataManager::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 width, qint32 height) { QWriteLocker locker(&m_lock); // Actial bytes reading/writing is done in private header writeBytesBody(data, x, y, width, height); } void KisTiledDataManager::readBytes(quint8 *data, qint32 x, qint32 y, qint32 width, qint32 height) const { QReadLocker locker(&m_lock); // Actual bytes reading/writing is done in private header readBytesBody(data, x, y, width, height); } QVector KisTiledDataManager::readPlanarBytes(QVector channelSizes, qint32 x, qint32 y, qint32 width, qint32 height) { QReadLocker locker(&m_lock); // Actial bytes reading/writing is done in private header return readPlanarBytesBody(channelSizes, x, y, width, height); } void KisTiledDataManager::writePlanarBytes(QVector planes, QVector channelSizes, qint32 x, qint32 y, qint32 width, qint32 height) { QWriteLocker locker(&m_lock); // Actial bytes reading/writing is done in private header writePlanarBytesBody(planes, channelSizes, x, y, width, height); } qint32 KisTiledDataManager::numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const { qint32 numColumns; Q_UNUSED(minY); Q_UNUSED(maxY); if (x >= 0) { numColumns = KisTileData::WIDTH - (x % KisTileData::WIDTH); } else { numColumns = ((-x - 1) % KisTileData::WIDTH) + 1; } return numColumns; } qint32 KisTiledDataManager::numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const { qint32 numRows; Q_UNUSED(minX); Q_UNUSED(maxX); if (y >= 0) { numRows = KisTileData::HEIGHT - (y % KisTileData::HEIGHT); } else { numRows = ((-y - 1) % KisTileData::HEIGHT) + 1; } return numRows; } qint32 KisTiledDataManager::rowStride(qint32 x, qint32 y) const { Q_UNUSED(x); Q_UNUSED(y); return KisTileData::WIDTH * pixelSize(); } diff --git a/krita/image/tiles3/swap/kis_tile_compressor_2.cpp b/krita/image/tiles3/swap/kis_tile_compressor_2.cpp index c46843999c1..fbd1a8295e1 100644 --- a/krita/image/tiles3/swap/kis_tile_compressor_2.cpp +++ b/krita/image/tiles3/swap/kis_tile_compressor_2.cpp @@ -1,185 +1,185 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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 * MERCHANTABILITY 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 "kis_tile_compressor_2.h" #include "kis_lzf_compression.h" #include #define TILE_DATA_SIZE(pixelSize) ((pixelSize) * KisTileData::WIDTH * KisTileData::HEIGHT) const QString KisTileCompressor2::m_compressionName = "LZF"; KisTileCompressor2::KisTileCompressor2() { m_compression = new KisLzfCompression(); } KisTileCompressor2::~KisTileCompressor2() { delete m_compression; } void KisTileCompressor2::writeTile(KisTileSP tile, KoStore *store) { const qint32 tileDataSize = TILE_DATA_SIZE(tile->pixelSize()); prepareStreamingBuffer(tileDataSize); qint32 bytesWritten; tile->lockForRead(); compressTileData(tile->tileData(), (quint8*)m_streamingBuffer.data(), m_streamingBuffer.size(), bytesWritten); tile->unlock(); QString header = getHeader(tile, bytesWritten); - store->write(header.toAscii()); + store->write(header.toLatin1()); store->write(m_streamingBuffer.data(), bytesWritten); } void KisTileCompressor2::readTile(KoStore *store, KisTiledDataManager *dm) { const qint32 tileDataSize = TILE_DATA_SIZE(pixelSize(dm)); prepareStreamingBuffer(tileDataSize); QIODevice *stream = store->device(); QByteArray header = stream->readLine(maxHeaderLength()); QList headerItems = header.trimmed().split(','); qint32 x = headerItems.takeFirst().toInt(); qint32 y = headerItems.takeFirst().toInt(); QString compressionName = headerItems.takeFirst(); qint32 dataSize = headerItems.takeFirst().toInt(); Q_ASSERT(headerItems.isEmpty()); Q_ASSERT(compressionName == m_compressionName); qint32 row = yToRow(dm, y); qint32 col = xToCol(dm, x); KisTileSP tile = dm->getTile(col, row, true); stream->read(m_streamingBuffer.data(), dataSize); tile->lockForWrite(); decompressTileData((quint8*)m_streamingBuffer.data(), dataSize, tile->tileData()); tile->unlock(); } void KisTileCompressor2::prepareStreamingBuffer(qint32 tileDataSize) { /** * TODO: delete this buffer! * It is better to use one of other two buffers to store streams */ m_streamingBuffer.resize(tileDataSize + 1); } void KisTileCompressor2::prepareWorkBuffers(qint32 tileDataSize) { const qint32 bufferSize = m_compression->outputBufferSize(tileDataSize); m_linearizationBuffer.resize(tileDataSize); m_compressionBuffer.resize(bufferSize); } void KisTileCompressor2::compressTileData(KisTileData *tileData, quint8 *buffer, qint32 bufferSize, qint32 &bytesWritten) { const qint32 pixelSize = tileData->pixelSize(); const qint32 tileDataSize = TILE_DATA_SIZE(pixelSize); qint32 compressedBytes; #ifdef NDEBUG Q_UNUSED(bufferSize); #else Q_ASSERT(bufferSize >= tileDataSize + 1); #endif prepareWorkBuffers(tileDataSize); KisAbstractCompression::linearizeColors(tileData->data(), (quint8*)m_linearizationBuffer.data(), tileDataSize, pixelSize); compressedBytes = m_compression->compress((quint8*)m_linearizationBuffer.data(), tileDataSize, (quint8*)m_compressionBuffer.data(), m_compressionBuffer.size()); if(compressedBytes < tileDataSize) { buffer[0] = COMPRESSED_DATA_FLAG; memcpy(buffer + 1, m_compressionBuffer.data(), compressedBytes); bytesWritten = compressedBytes + 1; } else { buffer[0] = RAW_DATA_FLAG; memcpy(buffer + 1, tileData->data(), tileDataSize); bytesWritten = tileDataSize + 1; } } void KisTileCompressor2::decompressTileData(quint8 *buffer, qint32 bufferSize, KisTileData *tileData) { const qint32 pixelSize = tileData->pixelSize(); const qint32 tileDataSize = TILE_DATA_SIZE(pixelSize); if(buffer[0] == COMPRESSED_DATA_FLAG) { prepareWorkBuffers(tileDataSize); qint32 bytesWritten; bytesWritten = m_compression->decompress(buffer + 1, bufferSize - 1, (quint8*)m_linearizationBuffer.data(), tileDataSize); Q_ASSERT(bytesWritten == tileDataSize); Q_UNUSED(bytesWritten); // unused-but-set-variable KisAbstractCompression::delinearizeColors((quint8*)m_linearizationBuffer.data(), tileData->data(), tileDataSize, pixelSize); } else { memcpy(tileData->data(), buffer + 1, tileDataSize); } } qint32 KisTileCompressor2::tileDataBufferSize(KisTileData *tileData) { return TILE_DATA_SIZE(tileData->pixelSize()) + 1; } inline qint32 KisTileCompressor2::maxHeaderLength() { static const qint32 QINT32_LENGTH = 11; static const qint32 COMPRESSION_NAME_LENGTH = 5; static const qint32 SEPARATORS_LENGTH = 4; return 3 * QINT32_LENGTH + COMPRESSION_NAME_LENGTH + SEPARATORS_LENGTH; } inline QString KisTileCompressor2::getHeader(KisTileSP tile, qint32 compressedSize) { qint32 x, y; qint32 width, height; tile->extent().getRect(&x, &y, &width, &height); return QString("%1,%2,%3,%4\n").arg(x).arg(y).arg(m_compressionName).arg(compressedSize); } diff --git a/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp b/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp index 6e39ab70139..c2d92505a5f 100644 --- a/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp +++ b/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp @@ -1,297 +1,297 @@ /* * Copyright (c) 2012 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "ocio_display_filter.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_OPENGL static const int LUT3D_EDGE_SIZE = 32; GLuint compileShaderText(GLenum shaderType, const char *text) { GLuint shader; GLint stat; shader = glCreateShader(shaderType); glShaderSource(shader, 1, (const GLchar **) &text, NULL); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; glGetShaderInfoLog(shader, 1000, &len, log); qWarning() << "Failed to compile shader:" << log; return 0; } return shader; } GLuint linkShaders(GLuint fragShader) { if (!fragShader) return 0; GLuint program = glCreateProgram(); if (fragShader) glAttachShader(program, fragShader); glLinkProgram(program); /* check link */ { GLint stat; glGetProgramiv(program, GL_LINK_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; glGetProgramInfoLog(program, 1000, &len, log); return 0; } } return program; } const char * m_fragShaderText = "" "\n" "uniform sampler2D tex1;\n" "uniform sampler3D tex2;\n" "\n" "void main()\n" "{\n" " vec4 col = texture2D(tex1, gl_TexCoord[0].st);\n" " gl_FragColor = OCIODisplay(col, tex2);\n" "}\n"; #endif OcioDisplayFilter::OcioDisplayFilter(QObject *parent) : KisDisplayFilter(parent) , inputColorSpaceName(0) , displayDevice(0) , view(0) , swizzle(RGBA) #ifdef HAVE_OPENGL , m_fragShader(0) , m_program(0) #endif { } void OcioDisplayFilter::filter(quint8 *src, quint8 */*dst*/, quint32 numPixels) { // processes that data _in_ place if (m_processor) { OCIO::PackedImageDesc img(reinterpret_cast(src), numPixels, 1, 4); m_processor->apply(img); } } #ifdef HAVE_OPENGL GLuint OcioDisplayFilter::program() { return m_program; } #endif void OcioDisplayFilter::updateProcessor() { if (!config) { return; } if (!displayDevice) { displayDevice = config->getDefaultDisplay(); } if (!view) { view = config->getDefaultView(displayDevice); } if (!inputColorSpaceName) { inputColorSpaceName = config->getColorSpaceNameByIndex(0); } OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create(); transform->setInputColorSpaceName(inputColorSpaceName); transform->setDisplay(displayDevice); transform->setView(view); // fstop exposure control -- not sure how that translates to our exposure { float gain = powf(2.0f, exposure); const float slope4f[] = { gain, gain, gain, 1.0f }; float m44[16]; float offset4[4]; OCIO::MatrixTransform::Scale(m44, offset4, slope4f); OCIO::MatrixTransformRcPtr mtx = OCIO::MatrixTransform::Create(); mtx->setValue(m44, offset4); transform->setLinearCC(mtx); } // channel swizzle { int channelHot[4]; switch (swizzle) { case LUMINANCE: channelHot[0] = 1; channelHot[1] = 1; channelHot[2] = 1; channelHot[3] = 0; break; case RGBA: channelHot[0] = 1; channelHot[1] = 1; channelHot[2] = 1; channelHot[3] = 1; break; case R: channelHot[0] = 1; channelHot[1] = 0; channelHot[2] = 0; channelHot[3] = 0; break; case G: channelHot[0] = 0; channelHot[1] = 1; channelHot[2] = 0; channelHot[3] = 0; break; case B: channelHot[0] = 0; channelHot[1] = 0; channelHot[2] = 1; channelHot[3] = 0; break; case A: channelHot[0] = 0; channelHot[1] = 0; channelHot[2] = 0; channelHot[3] = 1; default: ; } float lumacoef[3]; config->getDefaultLumaCoefs(lumacoef); float m44[16]; float offset[4]; OCIO::MatrixTransform::View(m44, offset, channelHot, lumacoef); OCIO::MatrixTransformRcPtr swizzle = OCIO::MatrixTransform::Create(); swizzle->setValue(m44, offset); transform->setChannelView(swizzle); } // Post-display transform gamma { float exponent = 1.0f/std::max(1e-6f, static_cast(gamma)); const float exponent4f[] = { exponent, exponent, exponent, exponent }; OCIO::ExponentTransformRcPtr expTransform = OCIO::ExponentTransform::Create(); expTransform->setValue(exponent4f); transform->setDisplayCC(expTransform); } m_processor = config->getProcessor(transform); #ifdef HAVE_OPENGL // check whether we are allowed to use shaders -- though that should // work for everyone these days KisConfig cfg; if (!cfg.useOpenGLShaders()) return; if (!cfg.useOpenGL()) return; if (m_lut3d.size() == 0) { //qDebug() << "generating lut"; glGenTextures(1, &m_lut3dTexID); int num3Dentries = 3 * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE; m_lut3d.fill(0.0, num3Dentries); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_3D, m_lut3dTexID); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, 0, GL_RGB, GL_FLOAT, &m_lut3d.data()[0]); } // Step 1: Create a GPU Shader Description OCIO::GpuShaderDesc shaderDesc; shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); shaderDesc.setFunctionName("OCIODisplay"); shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE); // Step 2: Compute the 3D LUT - QString lut3dCacheID = QString::fromAscii(m_processor->getGpuLut3DCacheID(shaderDesc)); + QString lut3dCacheID = QString::fromLatin1(m_processor->getGpuLut3DCacheID(shaderDesc)); if(lut3dCacheID != m_lut3dcacheid) { //qDebug() << "Computing 3DLut " << m_lut3dcacheid; m_lut3dcacheid = lut3dCacheID; m_processor->getGpuLut3D(&m_lut3d[0], shaderDesc); glBindTexture(GL_TEXTURE_3D, m_lut3dTexID); glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, GL_RGB, GL_FLOAT, &m_lut3d[0]); } // Step 3: Compute the Shader - QString shaderCacheID = QString::fromAscii(m_processor->getGpuShaderTextCacheID(shaderDesc)); + QString shaderCacheID = QString::fromLatin1(m_processor->getGpuShaderTextCacheID(shaderDesc)); if (m_program == 0 || shaderCacheID != m_shadercacheid) { //qDebug() << "Computing Shader " << m_shadercacheid; m_shadercacheid = shaderCacheID; std::ostringstream os; os << m_processor->getGpuShaderText(shaderDesc) << "\n"; os << m_fragShaderText; //qDebug() << "shader" << os.str().c_str(); if (m_fragShader) { glDeleteShader(m_fragShader); } m_fragShader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str()); if (m_program) { glDeleteProgram(m_program); } m_program = linkShaders(m_fragShader); } #endif } diff --git a/krita/plugins/extensions/metadataeditor/kis_entry_editor.cc b/krita/plugins/extensions/metadataeditor/kis_entry_editor.cc index a9df3b4b1d5..be411cb977a 100644 --- a/krita/plugins/extensions/metadataeditor/kis_entry_editor.cc +++ b/krita/plugins/extensions/metadataeditor/kis_entry_editor.cc @@ -1,101 +1,101 @@ /* * Copyright (c) 2007,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "kis_entry_editor.h" #include #include #include #include "kis_meta_data_value.h" #include #include struct KisEntryEditor::Private { QObject* object; QString propertyName; KisMetaData::Store* store; QString key; QString structField; int arrayIndex; KisMetaData::Value value() { KisMetaData::Value value = store->getEntry(key).value(); if (value.type() == KisMetaData::Value::Structure && !structField.isEmpty()) { QMap structure = value.asStructure(); return structure[ structField ]; } else if (value.isArray() && arrayIndex > -1) { QList array = value.asArray(); if (arrayIndex < array.size()) { return array[arrayIndex]; } else { return KisMetaData::Value(); } } return value; } void setValue(const QVariant& variant) { KisMetaData::Value& value = store->getEntry(key).value(); if (value.type() == KisMetaData::Value::Structure && !structField.isEmpty()) { QMap structure = value.asStructure(); value = structure[ structField ]; value.setVariant(variant); value.setStructureVariant(structField, variant); } else if (value.isArray() && arrayIndex > -1) { value.setArrayVariant(arrayIndex, variant); } else { value.setVariant(variant); } } }; KisEntryEditor::KisEntryEditor(QObject* obj, KisMetaData::Store* store, QString key, QString propertyName, QString structField, int arrayIndex) : d(new Private) { Q_ASSERT(obj); Q_ASSERT(store); d->object = obj; d->propertyName = propertyName; d->store = store; d->key = key; d->structField = structField; d->arrayIndex = arrayIndex; valueChanged(); } KisEntryEditor::~KisEntryEditor() { delete d; } void KisEntryEditor::valueChanged() { if (d->store->containsEntry(d->key)) { bool blocked = d->object->blockSignals(true); - d->object->setProperty(d->propertyName.toAscii(), d->value().asVariant()); + d->object->setProperty(d->propertyName.toLatin1(), d->value().asVariant()); d->object->blockSignals(blocked); } } void KisEntryEditor::valueEdited() { - QVariant val = d->object->property(d->propertyName.toAscii()); + QVariant val = d->object->property(d->propertyName.toLatin1()); dbgPlugins << "Value edited: " << d->propertyName << val; d->setValue(val); emit valueHasBeenEdited(); } #include "kis_entry_editor.moc" diff --git a/krita/plugins/extensions/metadataeditor/kis_meta_data_editor.cc b/krita/plugins/extensions/metadataeditor/kis_meta_data_editor.cc index 3da27c5df4a..2e70f009bf5 100644 --- a/krita/plugins/extensions/metadataeditor/kis_meta_data_editor.cc +++ b/krita/plugins/extensions/metadataeditor/kis_meta_data_editor.cc @@ -1,162 +1,162 @@ /* * Copyright (c) 2007,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "kis_meta_data_editor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_entry_editor.h" #include #include "kis_meta_data_model.h" #include struct KisMetaDataEditor::Private { KisMetaData::Store* originalStore; KisMetaData::Store* store; QMultiHash entryEditors; }; KisMetaDataEditor::KisMetaDataEditor(QWidget* parent, KisMetaData::Store* originalStore) : KPageDialog(parent), d(new Private) { d->originalStore = originalStore; d->store = new KisMetaData::Store(*originalStore); QStringList files = KGlobal::dirs()->findAllResources("data", "kritaplugins/metadataeditor/*.rc"); foreach(const QString & file, files) { QFile xmlFile(file); xmlFile.open(QFile::ReadOnly); QString errMsg; int errLine, errCol; QDomDocument document; if (!document.setContent(&xmlFile, false, &errMsg, &errLine, &errCol)) { dbgPlugins << "Error reading XML at line" << errLine << " column" << errCol << " :" << errMsg; } QDomElement rootElement = document.documentElement(); if (rootElement.tagName() != "MetaDataEditor") { dbgPlugins << "Invalid XML file"; } const QString uiFileName = rootElement.attribute("uiFile"); const QString pageName = rootElement.attribute("name"); const QString iconName = rootElement.attribute("icon"); if (uiFileName == "") continue; // Read the ui file QUiLoader loader; QFile uiFile(KStandardDirs::locate("data", "kritaplugins/metadataeditor/" + uiFileName)); uiFile.open(QFile::ReadOnly); QWidget *widget = dynamic_cast(loader.load(&uiFile, this)); if (widget == 0) { dbgPlugins << "Failed to load ui file" << uiFileName; continue; } uiFile.close(); QDomNodeList list = rootElement.childNodes(); const int size = list.size(); for (int i = 0; i < size; ++i) { QDomElement elem = list.item(i).toElement(); if (elem.isNull() || elem.tagName() != "EntryEditor") continue; const QString editorName = elem.attribute("editorName"); const QString schemaUri = elem.attribute("schemaUri"); const QString entryName = elem.attribute("entryName"); const QString editorSignal = '2' + elem.attribute("editorSignal"); const QString propertyName = elem.attribute("propertyName"); const QString structureField = elem.attribute("structureField"); bool ok; int arrayIndex = elem.attribute("arrayIndex", "-1").toInt(&ok); if (!ok) arrayIndex = -1; dbgPlugins << ppVar(editorName) << ppVar(arrayIndex); QWidget* obj = widget->findChild(editorName); if (obj) { const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(schemaUri); if (schema) { if (!d->store->containsEntry(schema, entryName)) { dbgPlugins << " Store does not have yet entry :" << entryName << " in" << schemaUri << " ==" << schema->generateQualifiedName(entryName); } QString key = schema->generateQualifiedName(entryName); KisEntryEditor* ee = new KisEntryEditor(obj, d->store, key, propertyName, structureField, arrayIndex); - connect(obj, editorSignal.toAscii(), ee, SLOT(valueEdited())); + connect(obj, editorSignal.toLatin1(), ee, SLOT(valueEdited())); QList otherEditors = d->entryEditors.values(key); foreach(KisEntryEditor* oe, otherEditors) { connect(ee, SIGNAL(valueHasBeenEdited()), oe, SLOT(valueChanged())); connect(oe, SIGNAL(valueHasBeenEdited()), ee, SLOT(valueChanged())); } d->entryEditors.insert(key, ee); } else { dbgPlugins << "Unknown schema :" << schemaUri; } } else { dbgPlugins << "Unknown object :" << editorName; } } xmlFile.close(); KPageWidgetItem *page = new KPageWidgetItem(widget, pageName); if (!iconName.isEmpty()) { page->setIcon(KIcon(iconName)); } addPage(page); } // Add the list page QTableView* tableView = new QTableView; KisMetaDataModel* model = new KisMetaDataModel(d->store); tableView->setModel(model); tableView->verticalHeader()->setVisible(false); tableView->resizeColumnsToContents(); KPageWidgetItem *page = new KPageWidgetItem(tableView, i18n("List")); page->setIcon(koIcon("format-list-unordered")); addPage(page); } KisMetaDataEditor::~KisMetaDataEditor() { foreach(KisEntryEditor* e, d->entryEditors) { delete e; } delete d->store; delete d; } void KisMetaDataEditor::accept() { KPageDialog::accept(); d->originalStore->copyFrom(d->store); } diff --git a/krita/plugins/extensions/shiva/ShivaGeneratorConfigWidget.cpp b/krita/plugins/extensions/shiva/ShivaGeneratorConfigWidget.cpp index b197538cc4c..a0d36cebc4a 100644 --- a/krita/plugins/extensions/shiva/ShivaGeneratorConfigWidget.cpp +++ b/krita/plugins/extensions/shiva/ShivaGeneratorConfigWidget.cpp @@ -1,65 +1,65 @@ /* * Copyright (c) 2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "ShivaGeneratorConfigWidget.h" #include #include #include #include "filter/kis_filter_configuration.h" #include "QVariantValue.h" #include ShivaGeneratorConfigWidget::ShivaGeneratorConfigWidget(const OpenShiva::Source* _source, QWidget* parent) : KisConfigWidget(parent), m_source(_source), m_widget(new QtShiva::SourceParametersWidget(this)) { m_widget->setSource(m_source); QGridLayout* gridLayout = new QGridLayout(this); gridLayout->addWidget(m_widget, 0, 0, 1, 1); } ShivaGeneratorConfigWidget::~ShivaGeneratorConfigWidget() { } void ShivaGeneratorConfigWidget::setConfiguration(const KisPropertiesConfiguration* config) { QMap map = config->getProperties(); for (QMap::iterator it = map.begin(); it != map.end(); ++it) { - const GTLCore::Metadata::Entry* entry = m_source->metadata()->parameter(it.key().toAscii().data()); + const GTLCore::Metadata::Entry* entry = m_source->metadata()->parameter(it.key().toLatin1().constData()); if (entry && entry->asParameterEntry()) { GTLCore::Value val = qvariantToValue(it.value(), entry->asParameterEntry()->type()); if (val.isValid()) { - m_widget->setParameter(it.key().toAscii().data(), val); + m_widget->setParameter(it.key().toLatin1().constData(), val); } } } } KisPropertiesConfiguration* ShivaGeneratorConfigWidget::configuration() const { KisFilterConfiguration* config = new KisFilterConfiguration(m_source->name().c_str(), 1); for (std::map::const_iterator it = m_widget->parameters().begin(); it != m_widget->parameters().end(); ++it) { config->setProperty(it->first.c_str(), valueToQVariant(it->second)); } return config; } diff --git a/krita/plugins/extensions/shiva/shivafilter.cpp b/krita/plugins/extensions/shiva/shivafilter.cpp index 7ff54485f17..8617005ca51 100644 --- a/krita/plugins/extensions/shiva/shivafilter.cpp +++ b/krita/plugins/extensions/shiva/shivafilter.cpp @@ -1,112 +1,112 @@ /* * Copyright (c) 2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "shivafilter.h" #include #include #include #include #include #include #include "PaintDeviceImage.h" #include "QVariantValue.h" #include #include #include #include #include "UpdaterProgressReport.h" #include extern QMutex* shivaMutex; ShivaFilter::ShivaFilter(OpenShiva::Source* kernel) : KisFilter(KoID(kernel->name().c_str(), kernel->name().c_str()), categoryOther(), kernel->name().c_str()), m_source(kernel) { setColorSpaceIndependence(FULLY_INDEPENDENT); setSupportsPainting(false); setSupportsIncrementalPainting(false); } ShivaFilter::~ShivaFilter() { } KisConfigWidget* ShivaFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, const KisImageWSP image) const { Q_UNUSED(dev); Q_UNUSED(image); return new ShivaGeneratorConfigWidget(m_source, parent); } void ShivaFilter::process(KisPaintDeviceSP dev, const QRect& size, const KisFilterConfiguration* config, KoUpdater* progressUpdater ) const { Q_UNUSED(progressUpdater); QPoint dstTopLeft = size.topLeft(); UpdaterProgressReport* report = 0; if (progressUpdater) { progressUpdater->setRange(0, size.height()); report = new UpdaterProgressReport(progressUpdater); } Q_ASSERT(!dev.isNull()); // Q_ASSERT(config); // TODO support for selection OpenShiva::Kernel kernel; kernel.setSource(*m_source); if (config) { QMap map = config->getProperties(); for (QMap::iterator it = map.begin(); it != map.end(); ++it) { dbgPlugins << it.key() << " " << it.value(); - const GTLCore::Metadata::Entry* entry = kernel.metadata()->parameter(it.key().toAscii().data()); + const GTLCore::Metadata::Entry* entry = kernel.metadata()->parameter(it.key().toLatin1().constData()); if (entry && entry->asParameterEntry()) { GTLCore::Value val = qvariantToValue(it.value(), entry->asParameterEntry()->type()); if(val.isValid()) { - kernel.setParameter(it.key().toAscii().data(), val); + kernel.setParameter(it.key().toLatin1().constData(), val); } } } } kernel.setParameter(OpenShiva::Kernel::IMAGE_WIDTH, float(dev->defaultBounds()->bounds().width())); kernel.setParameter(OpenShiva::Kernel::IMAGE_HEIGHT, float(dev->defaultBounds()->bounds().height())); KisGtlLocker gtlLocker; { dbgPlugins << "Compile: " << m_source->name().c_str(); QMutexLocker l(shivaMutex); kernel.compile(); } if (kernel.isCompiled()) { ConstPaintDeviceImage pdisrc(dev); PaintDeviceImage pdi(dev); std::list< const GTLCore::AbstractImage* > inputs; GTLCore::RegionI region(dstTopLeft.x(), dstTopLeft.y() , size.width(), size.height()); inputs.push_back(&pdisrc); dbgPlugins << "Run: " << m_source->name().c_str() << " " << dstTopLeft << " " << size; kernel.evaluatePixels(region, inputs, &pdi, report ); } } diff --git a/krita/plugins/extensions/shiva/shivafiltersplugin.cpp b/krita/plugins/extensions/shiva/shivafiltersplugin.cpp index cd412cdd992..ab2de0c07ce 100644 --- a/krita/plugins/extensions/shiva/shivafiltersplugin.cpp +++ b/krita/plugins/extensions/shiva/shivafiltersplugin.cpp @@ -1,69 +1,69 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "shivafiltersplugin.h" #include #include #include #include #include #include #include #include "shivafilter.h" #include QMutex* shivaMutex; K_PLUGIN_FACTORY(ShivaPluginFactory, registerPlugin();) K_EXPORT_PLUGIN(ShivaPluginFactory("krita")) ShivaPlugin::ShivaPlugin(QObject *parent, const QVariantList &) : QObject(parent) { m_sourceCollection = new OpenShiva::SourcesCollection(); QStringList kernelModulesDirs = KGlobal::mainComponent().dirs()->findDirs("data", "krita/shiva/kernels/"); dbgPlugins << kernelModulesDirs; foreach(const QString & dir, kernelModulesDirs) { dbgPlugins << "Append : " << dir << " to the list of CTL modules"; - m_sourceCollection->addDirectory(dir.toAscii().data()); + m_sourceCollection->addDirectory(dir.toLatin1().constData()); } { KisFilterRegistry * manager = KisFilterRegistry::instance(); Q_ASSERT(manager); std::list< OpenShiva::Source > kernels = m_sourceCollection->sources(OpenShiva::Source::FilterKernel); dbgPlugins << "Collection has " << kernels.size() << " filters"; foreach(OpenShiva::Source kernel, kernels) { dbgPlugins << kernel.metadataCompilationMessages().toString().c_str() ; if (kernel.outputImageType() == OpenShiva::Source::Image && kernel.inputImageType(0) == OpenShiva::Source::Image) { manager->add(new ShivaFilter(new OpenShiva::Source(kernel))); } } } shivaMutex = new QMutex; } ShivaPlugin::~ShivaPlugin() { // delete m_sourceCollection; // The plugin object get deleted right after creation, m_sourceCollection be staticly deleted } diff --git a/krita/plugins/extensions/shiva/shivagenerator.cpp b/krita/plugins/extensions/shiva/shivagenerator.cpp index 63736635e6c..89c9497d64a 100644 --- a/krita/plugins/extensions/shiva/shivagenerator.cpp +++ b/krita/plugins/extensions/shiva/shivagenerator.cpp @@ -1,109 +1,109 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "shivagenerator.h" #include #include #include #include #include #include #include #include #include #include #include "PaintDeviceImage.h" #include "QVariantValue.h" #include "UpdaterProgressReport.h" #include "GTLCore/CompilationMessages.h" #include extern QMutex* shivaMutex; ShivaGenerator::ShivaGenerator(OpenShiva::Source* kernel) : KisGenerator(KoID(kernel->name().c_str(), kernel->name().c_str()), KoID("basic"), kernel->name().c_str()), m_source(kernel) { setColorSpaceIndependence(FULLY_INDEPENDENT); setSupportsPainting(true); setSupportsIncrementalPainting(false); } KisConfigWidget * ShivaGenerator::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, const KisImageWSP image) const { Q_UNUSED(dev); Q_UNUSED(image); return new ShivaGeneratorConfigWidget(m_source, parent); } void ShivaGenerator::generate(KisProcessingInformation dstInfo, const QSize& size, const KisFilterConfiguration* config, KoUpdater* progressUpdater) const { Q_UNUSED(progressUpdater); KisPaintDeviceSP dst = dstInfo.paintDevice(); QPoint dstTopLeft = dstInfo.topLeft(); UpdaterProgressReport* report = 0; if (progressUpdater) { progressUpdater->setRange(0, size.height()); report = new UpdaterProgressReport(progressUpdater); } Q_ASSERT(!dst.isNull()); // Q_ASSERT(config); // TODO implement the generating of pixel OpenShiva::Kernel kernel; kernel.setSource(*m_source); if (config) { QMap map = config->getProperties(); for (QMap::iterator it = map.begin(); it != map.end(); ++it) { - const GTLCore::Metadata::Entry* entry = kernel.metadata()->parameter(it.key().toAscii().data()); + const GTLCore::Metadata::Entry* entry = kernel.metadata()->parameter(it.key().toLatin1().constData()); if (entry && entry->asParameterEntry()) { GTLCore::Value val = qvariantToValue(it.value(), entry->asParameterEntry()->type()); if(val.isValid()) { - kernel.setParameter(it.key().toAscii().data(), val); + kernel.setParameter(it.key().toLatin1().constData(), val); } } } } kernel.setParameter(OpenShiva::Kernel::IMAGE_WIDTH, float(dstInfo.paintDevice()->defaultBounds()->bounds().width())); kernel.setParameter(OpenShiva::Kernel::IMAGE_HEIGHT, float(dstInfo.paintDevice()->defaultBounds()->bounds().height())); KisGtlLocker gtlLocker; { QMutexLocker l(shivaMutex); kernel.compile(); } if (kernel.isCompiled()) { PaintDeviceImage pdi(dst); std::list< const GTLCore::AbstractImage* > inputs; GTLCore::RegionI region(dstTopLeft.x(), dstTopLeft.y() , size.width(), size.height()); kernel.evaluatePixels(region, inputs, &pdi, report ); } else { dbgPlugins << "Error: " << kernel.compilationMessages().toString().c_str(); } } diff --git a/krita/plugins/extensions/shiva/shivageneratorsplugin.cpp b/krita/plugins/extensions/shiva/shivageneratorsplugin.cpp index 6a6e5f85dde..7df88bbf7ed 100644 --- a/krita/plugins/extensions/shiva/shivageneratorsplugin.cpp +++ b/krita/plugins/extensions/shiva/shivageneratorsplugin.cpp @@ -1,70 +1,70 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "shivageneratorsplugin.h" #include #include #include #include #include #include #include #include "shivagenerator.h" #include QMutex* shivaMutex; K_PLUGIN_FACTORY(ShivaPluginFactory, registerPlugin();) K_EXPORT_PLUGIN(ShivaPluginFactory("krita")) ShivaPlugin::ShivaPlugin(QObject *parent, const QVariantList &) : KParts::Plugin(parent) { m_sourceCollection = new OpenShiva::SourcesCollection(); QStringList kernelModulesDirs = KGlobal::mainComponent().dirs()->findDirs("data", "krita/shiva/kernels/"); dbgPlugins << kernelModulesDirs; foreach(const QString & dir, kernelModulesDirs) { dbgPlugins << "Append : " << dir << " to the list of CTL modules"; - m_sourceCollection->addDirectory(dir.toAscii().data()); + m_sourceCollection->addDirectory(dir.toLatin1().constData()); } { KisGeneratorRegistry * manager = KisGeneratorRegistry::instance(); Q_ASSERT(manager); std::list< OpenShiva::Source > kernels = m_sourceCollection->sources(OpenShiva::Source::GeneratorKernel); dbgPlugins << "Collection has " << kernels.size() << " generators"; foreach(OpenShiva::Source kernel, kernels) { dbgPlugins << kernel.metadataCompilationMessages().toString().c_str(); if (kernel.outputImageType() == OpenShiva::Source::Image || kernel.outputImageType() == OpenShiva::Source::Image4) { manager->add(new ShivaGenerator(new OpenShiva::Source(kernel))); } } } shivaMutex = new QMutex; } ShivaPlugin::~ShivaPlugin() { // delete m_sourceCollection; // The plugin object get deleted right after creation, m_sourceCollection be staticly deleted } diff --git a/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp b/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp index d6886f23e0e..4c98eb792f5 100644 --- a/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp +++ b/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp @@ -1,416 +1,416 @@ /* * This file is part of Krita * * Copyright (c) 2005 C. Boemann * * 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 * MERCHANTABILITY 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 "kis_perchannel_filter.h" #include #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" #include "KoColorTransformation.h" #include "KoCompositeOp.h" #include "KoID.h" #include #include "kis_bookmarked_configuration_manager.h" #include "kis_config_widget.h" #include #include #include #include #include "kis_histogram.h" #include "kis_painter.h" #include "widgets/kis_curve_widget.h" KisPerChannelConfigWidget::KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, const QRect &bounds, Qt::WFlags f) : KisConfigWidget(parent, f), m_histogram(0) { Q_ASSERT(dev); m_page = new WdgPerChannel(this); QHBoxLayout * layout = new QHBoxLayout(this); Q_CHECK_PTR(layout); layout->addWidget(m_page); m_dev = dev; m_activeCh = 0; KisPerChannelFilterConfiguration::initDefaultCurves(m_curves, m_dev->colorSpace()->colorChannelCount()); QList colorChannels; foreach(KoChannelInfo *channel, dev->colorSpace()->channels()) { if (channel->channelType() == KoChannelInfo::COLOR) { colorChannels.append(channel); } } // fill in the channel chooser, in the display order, but store the pixel index as well. QList sortedChannels = KoChannelInfo::displayOrderSorted(colorChannels); foreach(KoChannelInfo *channel, sortedChannels) { QVariant pixelIndex(KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), KoChannelInfo::displayOrderSorted(dev->colorSpace()->channels()))); m_page->cmbChannel->addItem(channel->name(), pixelIndex); } connect(m_page->cmbChannel, SIGNAL(activated(int)), this, SLOT(setActiveChannel(int))); // create the horizontal and vertical gradient labels m_page->hgradient->setPixmap(createGradient(Qt::Horizontal)); m_page->vgradient->setPixmap(createGradient(Qt::Vertical)); // init histogram calculator QList keys = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_dev->colorSpace()); if(keys.size() > 0) { KoHistogramProducerFactory *hpf; hpf = KoHistogramProducerFactoryRegistry::instance()->get(keys.at(0)); m_histogram = new KisHistogram(m_dev, bounds, hpf->generate(), LINEAR); } connect(m_page->curveWidget, SIGNAL(modified()), this, SIGNAL(sigConfigurationItemChanged())); m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, 0, 100); m_page->curveWidget->blockSignals(true); setActiveChannel(0); m_page->curveWidget->blockSignals(false); } KisPerChannelConfigWidget::~KisPerChannelConfigWidget() { delete m_histogram; } inline QPixmap KisPerChannelConfigWidget::createGradient(Qt::Orientation orient /*, int invert (not used yet) */) { int width; int height; int *i, inc, col; int x = 0, y = 0; if (orient == Qt::Horizontal) { i = &x; inc = 1; col = 0; width = 256; height = 1; } else { i = &y; inc = -1; col = 255; width = 1; height = 256; } QPixmap gradientpix(width, height); QPainter p(&gradientpix); p.setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine)); for (; *i < 256; (*i)++, col += inc) { p.setPen(QColor(col, col, col)); p.drawPoint(x, y); } return gradientpix; } inline QPixmap KisPerChannelConfigWidget::getHistogram() { int i; int height = 256; QPixmap pix(256, height); pix.fill(); QPainter p(&pix); p.setPen(QPen(Qt::gray, 1, Qt::SolidLine)); if(m_histogram) { m_histogram->setChannel(m_activeCh); double highest = (double)m_histogram->calculations().getHighest(); qint32 bins = m_histogram->producer()->numberOfBins(); if (m_histogram->getHistogramType() == LINEAR) { double factor = (double)height / highest; for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor)); } } else { double factor = (double)height / (double)log(highest); for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(log((double)m_histogram->getValue(i)) * factor)); } } } return pix; } #define BITS_PER_BYTE 8 #define pwr2(p) (1<curveWidget->curve(); m_activeCh = ch; m_page->curveWidget->setCurve(m_curves[m_activeCh]); m_page->curveWidget->setPixmap(getHistogram()); m_page->cmbChannel->setCurrentIndex(ch); // Getting range accepted by chahhel KoChannelInfo *channel = m_dev->colorSpace()->channels()[m_activeCh]; int order = BITS_PER_BYTE * channel->size(); int maxValue = pwr2(order); int min; int max; m_page->curveWidget->dropInOutControls(); switch (channel->channelValueType()) { case KoChannelInfo::UINT8: case KoChannelInfo::UINT16: case KoChannelInfo::UINT32: m_shift = 0; m_scale = double(maxValue); min = 0; max = maxValue - 1; break; case KoChannelInfo::INT8: case KoChannelInfo::INT16: m_shift = 0.5; m_scale = double(maxValue); min = -maxValue / 2; max = maxValue / 2 - 1; break; case KoChannelInfo::FLOAT16: case KoChannelInfo::FLOAT32: case KoChannelInfo::FLOAT64: default: m_shift = 0; m_scale = 100.0; //Hack Alert: should be changed to float min = 0; max = 100; break; } m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, min, max); } KisPropertiesConfiguration * KisPerChannelConfigWidget::configuration() const { int nCh = m_dev->colorSpace()->colorChannelCount(); KisPerChannelFilterConfiguration * cfg = new KisPerChannelFilterConfiguration(nCh); // updating current state m_curves[m_activeCh] = m_page->curveWidget->curve(); cfg->setCurves(m_curves); return cfg; } void KisPerChannelConfigWidget::setConfiguration(const KisPropertiesConfiguration * config) { const KisPerChannelFilterConfiguration * cfg = dynamic_cast(config); if (!cfg) return; if (cfg->m_curves.size() == 0) { /** * HACK ALERT: our configuration factory generates * default configuration with nTransfers==0. * Catching it here. */ KisPerChannelFilterConfiguration::initDefaultCurves(m_curves, m_dev->colorSpace()->colorChannelCount()); } else if (cfg->m_curves.size() != int(m_dev->colorSpace()->colorChannelCount())) { return; } else { for (int ch = 0; ch < cfg->m_curves.size(); ch++) m_curves[ch] = cfg->m_curves[ch]; } m_page->curveWidget->setCurve(m_curves[m_activeCh]); setActiveChannel(0); } KisPerChannelFilterConfiguration::KisPerChannelFilterConfiguration(int nCh) : KisFilterConfiguration("perchannel", 1) { initDefaultCurves(m_curves, nCh); oldCs = 0; } KisPerChannelFilterConfiguration::~KisPerChannelFilterConfiguration() { } bool KisPerChannelFilterConfiguration::isCompatible(const KisPaintDeviceSP dev) const { if (!oldCs) return false; return *dev->colorSpace() == *oldCs; } void KisPerChannelFilterConfiguration::setCurves(QList &curves) { m_curves.clear(); m_curves = curves; } void KisPerChannelFilterConfiguration::initDefaultCurves(QList &curves, int nCh) { curves.clear(); for (int i = 0; i < nCh; i++) { curves.append(KisCubicCurve()); } } void KisPerChannelFilterConfiguration::fromLegacyXML(const QDomElement& root) { fromXML(root); } void KisPerChannelFilterConfiguration::fromXML(const QDomElement& root) { QList curves; quint16 numTransfers = 0; int version; version = root.attribute("version").toInt(); QDomElement e = root.firstChild().toElement(); QString attributeName; while (!e.isNull()) { if ((attributeName = e.attribute("name")) == "nTransfers") { numTransfers = e.text().toUShort(); } else { QRegExp rx("curve(\\d+)"); if (rx.indexIn(attributeName, 0) != -1) { KisCubicCurve curve; quint16 index = rx.cap(1).toUShort(); index = qMin(index, quint16(curves.count())); if (!e.text().isEmpty()) { curve.fromString(e.text()); } curves.insert(index, curve); } } e = e.nextSiblingElement(); } if (!numTransfers) return; setVersion(version); setCurves(curves); } /** * Inherited from KisPropertiesConfiguration */ //void KisPerChannelFilterConfiguration::fromXML(const QString& s) void KisPerChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const { /** * * 3 * 0,0;0.5,0.5;1,1; * 0,0;1,1; * 0,0;1,1; * * */ root.setAttribute("version", version()); QDomElement t = doc.createElement("param"); QDomText text = doc.createTextNode(QString::number(m_curves.size())); t.setAttribute("name", "nTransfers"); t.appendChild(text); root.appendChild(t); QString paramName; for (int i = 0; i < m_curves.size(); ++i) { - paramName = QString::fromAscii("curve") + QString::number(i); + paramName = QLatin1String("curve") + QString::number(i); t = doc.createElement("param"); t.setAttribute("name", paramName); KisCubicCurve curve = m_curves[i]; text = doc.createTextNode(curve.toString()); t.appendChild(text); root.appendChild(t); } } /** * Inherited from KisPropertiesConfiguration */ //QString KisPerChannelFilterConfiguration::toXML() KisPerChannelFilter::KisPerChannelFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Color Adjustment curves...")) { setSupportsPainting(true); setSupportsIncrementalPainting(false); setColorSpaceIndependence(TO_LAB16); } KisConfigWidget * KisPerChannelFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, const KisImageWSP image) const { return new KisPerChannelConfigWidget(parent, dev, image->bounds()); } KisFilterConfiguration * KisPerChannelFilter::factoryConfiguration(const KisPaintDeviceSP) const { return new KisPerChannelFilterConfiguration(0); } KoColorTransformation* KisPerChannelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const { KisPerChannelFilterConfiguration* configBC = const_cast(dynamic_cast(config)); // Somehow, this shouldn't happen Q_ASSERT(configBC); if (configBC->m_curves.size() != int(cs->colorChannelCount())) { // We got an illegal number of colorchannels.KisFilter return 0; } const quint16** transfers = new const quint16*[configBC->m_curves.size()]; for(int i = 0; i < configBC->m_curves.size(); ++i) { transfers[i] = configBC->m_curves[i].uint16Transfer().constData(); } KoColorTransformation* t = cs->createPerChannelAdjustment(transfers); delete transfers; return t; } #include "kis_perchannel_filter.moc" diff --git a/krita/plugins/filters/tests/kis_all_filter_test.cpp b/krita/plugins/filters/tests/kis_all_filter_test.cpp index 1ee1985d3ba..d77112f2afb 100644 --- a/krita/plugins/filters/tests/kis_all_filter_test.cpp +++ b/krita/plugins/filters/tests/kis_all_filter_test.cpp @@ -1,311 +1,311 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_all_filter_test.h" #include #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "kis_selection.h" #include "kis_processing_information.h" #include "filter/kis_filter.h" #include "kis_threaded_applicator.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include "kis_transaction.h" #include bool compareQImages(QPoint & pt, const QImage & image1, const QImage & image2) { // QTime t; // t.start(); int w1 = image1.width(); int h1 = image1.height(); int w2 = image2.width(); int h2 = image2.height(); if (w1 != w2 || h1 != h2) { qDebug() << w1 << " " << w2 << " " << h1 << " " << h2; pt.setX(-1); pt.setY(-1); return false; } for (int x = 0; x < w1; ++x) { for (int y = 0; y < h1; ++y) { if (image1.pixel(x, y) != image2.pixel(x, y)) { pt.setX(x); pt.setY(y); return false; } } } // qDebug() << "compareQImages time elapsed:" << t.elapsed(); return true; } bool testFilterSrcNotIsDev(KisFilterSP f) { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png"); QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPaintDeviceSP dstdev = new KisPaintDevice(cs); dev->convertFromQImage(qimage, 0, 0, 0); // Get the predefined configuration from a file KisFilterConfiguration * kfc = f->defaultConfiguration(dev); QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //qDebug() << "creating new file for " << f->id(); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << kfc->toXML(); } else { QString s; QTextStream in(&file); s = in.readAll(); //qDebug() << "Read for " << f->id() << "\n" << s; kfc->fromXML(s); } qDebug() << f->id();// << "\n" << kfc->toXML() << "\n"; f->process(dev, dstdev, 0, QRect(QPoint(0,0), qimage.size()), kfc); QPoint errpoint; if (!compareQImages(errpoint, result, dstdev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("src_not_is_dst_lena_%1.png").arg(f->id())); return false; } return true; } bool testFilterNoTransaction(KisFilterSP f) { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png"); QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(qimage, 0, 0, 0); // Get the predefined configuration from a file KisFilterConfiguration * kfc = f->defaultConfiguration(dev); QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //qDebug() << "creating new file for " << f->id(); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << kfc->toXML(); } else { QString s; QTextStream in(&file); s = in.readAll(); //qDebug() << "Read for " << f->id() << "\n" << s; kfc->fromXML(s); } qDebug() << f->id();// << "\n" << kfc->toXML() << "\n"; f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc); QPoint errpoint; if (!compareQImages(errpoint, result, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("no_transactio_lena_%1.png").arg(f->id())); return false; } return true; } bool testFilter(KisFilterSP f) { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png"); QString resultFileName = QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png"; QImage result(resultFileName); if (!QFileInfo(resultFileName).exists()) { qDebug() << resultFileName << " not found"; return false; } KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(qimage, 0, 0, 0); KisTransaction * cmd = new KisTransaction(f->name(), dev); // Get the predefined configuration from a file KisFilterConfiguration * kfc = f->defaultConfiguration(dev); QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //qDebug() << "creating new file for " << f->id(); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << kfc->toXML(); } else { QString s; QTextStream in(&file); s = in.readAll(); //qDebug() << "Read for " << f->id() << "\n" << s; kfc->fromXML(s); } qDebug() << f->id();// << "\n" << kfc->toXML() << "\n"; f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc); QPoint errpoint; delete cmd; if (!compareQImages(errpoint, result, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { qDebug() << errpoint; dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("lena_%1.png").arg(f->id())); return false; } return true; } bool testFilterWithSelections(KisFilterSP f) { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png"); QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(qimage, 0, 0, 0); // Get the predefined configuration from a file KisFilterConfiguration * kfc = f->defaultConfiguration(dev); QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //qDebug() << "creating new file for " << f->id(); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << kfc->toXML(); } else { QString s; QTextStream in(&file); s = in.readAll(); //qDebug() << "Read for " << f->id() << "\n" << s; kfc->fromXML(s); } qDebug() << f->id();// << "\n"; << kfc->toXML() << "\n"; KisSelectionSP sel1 = new KisSelection(new KisSelectionDefaultBounds(dev)); sel1->getOrCreatePixelSelection()->select(qimage.rect()); f->process(dev, dev, sel1, QRect(QPoint(0,0), qimage.size()), kfc); QPoint errpoint; if (!compareQImages(errpoint, result, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) { dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("sel_lena_%1.png").arg(f->id())); return false; } return true; } void KisAllFilterTest::testAllFilters() { QStringList failures; QStringList successes; QList filterList = KisFilterRegistry::instance()->keys(); qSort(filterList); for (QList::Iterator it = filterList.begin(); it != filterList.end(); ++it) { if (testFilter(KisFilterRegistry::instance()->value(*it))) successes << *it; else failures << *it; } qDebug() << "Success: " << successes; if (failures.size() > 0) { - QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toAscii()); + QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1()); } } void KisAllFilterTest::testAllFiltersNoTransaction() { QStringList failures; QStringList successes; QList filterList = KisFilterRegistry::instance()->keys(); qSort(filterList); for (QList::Iterator it = filterList.begin(); it != filterList.end(); ++it) { if (testFilterNoTransaction(KisFilterRegistry::instance()->value(*it))) successes << *it; else failures << *it; } qDebug() << "Success (no transaction): " << successes; if (failures.size() > 0) { - QFAIL(QString("Failed filters (no transaction):\n\t %1").arg(failures.join("\n\t")).toAscii()); + QFAIL(QString("Failed filters (no transaction):\n\t %1").arg(failures.join("\n\t")).toLatin1()); } } void KisAllFilterTest::testAllFiltersSrcNotIsDev() { QStringList failures; QStringList successes; QList filterList = KisFilterRegistry::instance()->keys(); qSort(filterList); for (QList::Iterator it = filterList.begin(); it != filterList.end(); ++it) { if (testFilterSrcNotIsDev(KisFilterRegistry::instance()->value(*it))) successes << *it; else failures << *it; } qDebug() << "Src!=Dev Success: " << successes; if (failures.size() > 0) { - QFAIL(QString("Src!=Dev Failed filters:\n\t %1").arg(failures.join("\n\t")).toAscii()); + QFAIL(QString("Src!=Dev Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1()); } } void KisAllFilterTest::testAllFiltersWithSelections() { QStringList failures; QStringList successes; QList filterList = KisFilterRegistry::instance()->keys(); qSort(filterList); for (QList::Iterator it = filterList.begin(); it != filterList.end(); ++it) { if (testFilterWithSelections(KisFilterRegistry::instance()->value(*it))) successes << *it; else failures << *it; } qDebug() << "Success: " << successes; if (failures.size() > 0) { - QFAIL(QString("Failed filters with selections:\n\t %1").arg(failures.join("\n\t")).toAscii()); + QFAIL(QString("Failed filters with selections:\n\t %1").arg(failures.join("\n\t")).toLatin1()); } } QTEST_KDEMAIN(KisAllFilterTest, GUI) #include "kis_all_filter_test.moc" diff --git a/krita/plugins/filters/tests/kis_crash_filter_test.cpp b/krita/plugins/filters/tests/kis_crash_filter_test.cpp index ccaa5851ff3..5dfcb6049d0 100644 --- a/krita/plugins/filters/tests/kis_crash_filter_test.cpp +++ b/krita/plugins/filters/tests/kis_crash_filter_test.cpp @@ -1,100 +1,100 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 #include "kis_crash_filter_test.h" #include #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "kis_selection.h" #include "kis_processing_information.h" #include "filter/kis_filter.h" #include "kis_threaded_applicator.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include bool applyFilter(const KoColorSpace * cs, KisFilterSP f) { QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); // dev->fill(0, 0, 100, 100, dev->defaultPixel()); dev->convertFromQImage(qimage, 0, 0, 0); // Get the predefined configuration from a file KisFilterConfiguration * kfc = f->defaultConfiguration(dev); QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << "creating new file for " << f->id(); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << kfc->toXML(); } else { QString s; QTextStream in(&file); s = in.readAll(); kfc->fromXML(s); } qDebug() << f->id() << ", " << cs->id() << ", " << cs->profile()->name();// << kfc->toXML() << "\n"; f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc); return true; } bool testFilter(KisFilterSP f) { QList colorSpaces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::AllProfiles); bool ok = false; foreach(const KoColorSpace* colorSpace, colorSpaces) { // XXX: Let's not check the painterly colorspaces right now if (colorSpace->id().startsWith("KS", Qt::CaseInsensitive)) { continue; } ok = applyFilter(colorSpace, f); } return ok; } void KisCrashFilterTest::testCrashFilters() { QStringList failures; QStringList successes; QList filterList = KisFilterRegistry::instance()->keys(); qSort(filterList); for (QList::Iterator it = filterList.begin(); it != filterList.end(); ++it) { if (testFilter(KisFilterRegistry::instance()->value(*it))) successes << *it; else failures << *it; } qDebug() << "Success: " << successes; if (failures.size() > 0) { - QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toAscii()); + QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1()); } } QTEST_KDEMAIN(KisCrashFilterTest, GUI) #include "kis_crash_filter_test.moc" diff --git a/krita/plugins/formats/exr/exr_converter.cc b/krita/plugins/formats/exr/exr_converter.cc index a8cf9a1cc38..ce30e7f5e29 100644 --- a/krita/plugins/formats/exr/exr_converter.cc +++ b/krita/plugins/formats/exr/exr_converter.cc @@ -1,868 +1,868 @@ /* * Copyright (c) 2005 Adrian Page * Copyright (c) 2010 Cyrille Berger * * 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 * MERCHANTABILITY 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 "exr_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include #include #include #include #include exrConverter::exrConverter(KisDoc2 *doc) { m_doc = doc; m_job = 0; m_stop = false; } exrConverter::~exrConverter() { } enum ImageType { IT_UNKNOWN, IT_FLOAT16, IT_FLOAT32, IT_UNSUPPORTED }; ImageType imfTypeToKisType(Imf::PixelType type) { switch (type) { case Imf::UINT: case Imf::NUM_PIXELTYPES: return IT_UNSUPPORTED; case Imf::HALF: return IT_FLOAT16; case Imf::FLOAT: return IT_FLOAT32; default: qFatal("Out of bound enum"); return IT_UNKNOWN; } } const KoColorSpace* kisTypeToColorSpace(QString model, ImageType imageType) { switch (imageType) { case IT_FLOAT16: return KoColorSpaceRegistry::instance()->colorSpace(model, Float16BitsColorDepthID.id(), ""); case IT_FLOAT32: return KoColorSpaceRegistry::instance()->colorSpace(model, Float32BitsColorDepthID.id(), ""); case IT_UNKNOWN: case IT_UNSUPPORTED: return 0; default: qFatal("Out of bound enum"); return 0; } } template struct Rgba { _T_ r; _T_ g; _T_ b; _T_ a; }; struct ExrGroupLayerInfo; struct ExrLayerInfoBase { ExrLayerInfoBase() : colorSpace(0), parent(0) { } const KoColorSpace* colorSpace; QString name; const ExrGroupLayerInfo* parent; }; struct ExrGroupLayerInfo : public ExrLayerInfoBase { ExrGroupLayerInfo() : groupLayer(0) {} KisGroupLayerSP groupLayer; }; struct ExrPaintLayerInfo : public ExrLayerInfoBase { ExrPaintLayerInfo() : imageType(IT_UNKNOWN) { } ImageType imageType; QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is the EXR channel name struct Remap { Remap(const QString& _original, const QString& _current) : original(_original), current(_current) { } QString original; QString current; }; QList< Remap > remappedChannels; ///< this is used to store in the metadata the mapping between exr channel name, and channels used in Krita void updateImageType(ImageType channelType); }; void ExrPaintLayerInfo::updateImageType(ImageType channelType) { if (imageType == IT_UNKNOWN) { imageType = channelType; } else if (imageType != channelType) { imageType = IT_UNSUPPORTED; } } template void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype) { QVector<_T_> pixels(width*height); Q_ASSERT(info.channelMap.contains("G")); dbgFile << "G -> " << info.channelMap["G"]; for (int y = 0; y < height; ++y) { Imf::FrameBuffer frameBuffer; _T_* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; - frameBuffer.insert(info.channelMap["G"].toAscii().data(), + frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), Imf::Slice(ptype, (char *) frameBufferData, sizeof(_T_) * 1, sizeof(_T_) * width)); file.setFrameBuffer(frameBuffer); file.readPixels(ystart + y); _T_ *rgba = pixels.data(); KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); do { // XXX: For now unmultiply the alpha, though compositing will be faster if we // keep it premultiplied. _T_ unmultipliedRed = *rgba; _T_* dst = reinterpret_cast<_T_*>(it->rawData()); *dst = unmultipliedRed; ++rgba; } while (it->nextPixel()); } } template void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype) { typedef Rgba<_T_> Rgba; QVector pixels(width); bool hasAlpha = info.channelMap.contains("A"); for (int y = 0; y < height; ++y) { Imf::FrameBuffer frameBuffer; Rgba* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; - frameBuffer.insert(info.channelMap["R"].toAscii().data(), + frameBuffer.insert(info.channelMap["R"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->r, sizeof(Rgba) * 1, sizeof(Rgba) * width)); - frameBuffer.insert(info.channelMap["G"].toAscii().data(), + frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->g, sizeof(Rgba) * 1, sizeof(Rgba) * width)); - frameBuffer.insert(info.channelMap["B"].toAscii().data(), + frameBuffer.insert(info.channelMap["B"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->b, sizeof(Rgba) * 1, sizeof(Rgba) * width)); if (hasAlpha) { - frameBuffer.insert(info.channelMap["A"].toAscii().data(), + frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->a, sizeof(Rgba) * 1, sizeof(Rgba) * width)); } file.setFrameBuffer(frameBuffer); file.readPixels(ystart + y); Rgba *rgba = pixels.data(); KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); do { // XXX: For now unmultiply the alpha, though compositing will be faster if we // keep it premultiplied. _T_ unmultipliedRed = rgba->r; _T_ unmultipliedGreen = rgba->g; _T_ unmultipliedBlue = rgba->b; if (hasAlpha && rgba -> a >= HALF_EPSILON) { unmultipliedRed /= rgba->a; unmultipliedGreen /= rgba->a; unmultipliedBlue /= rgba->a; } typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast::Pixel*>(it->rawData()); dst->red = unmultipliedRed; dst->green = unmultipliedGreen; dst->blue = unmultipliedBlue; if (hasAlpha) { dst->alpha = rgba->a; } else { dst->alpha = 1.0; } ++rgba; } while (it->nextPixel()); } } bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2) { if (idx1 > idx2) return true; if (group.name == list[idx2]) { return recCheckGroup(*group.parent, list, idx1, idx2 - 1); } return false; } ExrGroupLayerInfo* searchGroup(QList* groups, QStringList list, int idx1, int idx2) { if (idx1 > idx2) { return 0; } // Look for the group for (int i = 0; i < groups->size(); ++i) { if (recCheckGroup(groups->at(i), list, idx1, idx2)) { return &(*groups)[i]; } } // Create the group ExrGroupLayerInfo info; info.name = list.at(idx2); info.parent = searchGroup(groups, list, idx1, idx2 - 1); groups->append(info); return &groups->last(); } KisImageBuilder_Result exrConverter::decode(const KUrl& uri) { dbgFile << "Load exr: " << uri << " " << QFile::encodeName(uri.toLocalFile()); Imf::InputFile file(QFile::encodeName(uri.toLocalFile())); Imath::Box2i dw = file.header().dataWindow(); int width = dw.max.x - dw.min.x + 1; int height = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; // Display the attributes of a file for (Imf::Header::ConstIterator it = file.header().begin(); it != file.header().end(); ++it) { dbgFile << "Attribute: " << it.name() << " type: " << it.attribute().typeName(); } // Constuct the list of LayerInfo QList informationObjects; QList groups; ImageType imageType = IT_UNKNOWN; const Imf::ChannelList &channels = file.header().channels(); std::set layerNames; channels.layers(layerNames); // Test if it is a multilayer EXR or singlelayer if (layerNames.empty()) { dbgFile << "Single layer:"; ExrPaintLayerInfo info; info.name = i18n("HDR Layer"); for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { const Imf::Channel &channel = i.channel(); dbgFile << "Channel name = " << i.name() << " type = " << channel.type; info.updateImageType(imfTypeToKisType(channel.type)); QString qname = i.name(); if (qname != "A" && qname != "R" && qname != "G" && qname != "B") { dbgFile << "Unknow: " << i.name(); info.imageType = IT_UNSUPPORTED; } else { info.channelMap[qname] = qname; } } informationObjects.push_back(info); imageType = info.imageType; } else { dbgFile << "Multi layers:"; for (std::set::const_iterator i = layerNames.begin(); i != layerNames.end(); ++i) { ExrPaintLayerInfo info; dbgFile << "layer name = " << i->c_str(); info.name = i->c_str(); Imf::ChannelList::ConstIterator layerBegin, layerEnd; channels.channelsInLayer(*i, layerBegin, layerEnd); for (Imf::ChannelList::ConstIterator j = layerBegin; j != layerEnd; ++j) { const Imf::Channel &channel = j.channel(); dbgFile << "\tchannel " << j.name() << " type = " << channel.type; info.updateImageType(imfTypeToKisType(channel.type)); QString qname = j.name(); QStringList list = qname.split('.'); QString layersuffix = list.last(); if (list.size() > 1) { info.name = list[list.size()-2]; info.parent = searchGroup(&groups, list, 0, list.size() - 3); } info.channelMap[layersuffix] = qname; } if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) { informationObjects.push_back(info); if (imageType < info.imageType) { imageType = info.imageType; } } } } dbgFile << "File has " << informationObjects.size() << " layers"; // Set the colorspaces for (int i = 0; i < informationObjects.size(); ++i) { ExrPaintLayerInfo& info = informationObjects[i]; QString modelId; if (info.channelMap.size() == 1) { modelId = GrayColorModelID.id(); QString key = info.channelMap.begin().key(); if (key != "G") { info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G")); QString channel = info.channelMap.begin().value(); info.channelMap.clear(); info.channelMap["G"] = channel; } } else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) { if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) { modelId = RGBAColorModelID.id(); } else if (info.channelMap.contains("X") && info.channelMap.contains("Y") && info.channelMap.contains("Z")) { modelId = XYZAColorModelID.id(); QMap newChannelMap; if (info.channelMap.contains("W")) { newChannelMap["A"] = info.channelMap["W"]; info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("W", "A")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("X", "X")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Y", "Y")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Z", "Z")); } else if (info.channelMap.contains("A")) { newChannelMap["A"] = info.channelMap["A"]; } // The decode function expect R, G, B in the channel map newChannelMap["B"] = info.channelMap["X"]; newChannelMap["G"] = info.channelMap["Y"]; newChannelMap["R"] = info.channelMap["Z"]; info.channelMap = newChannelMap; } else { modelId = RGBAColorModelID.id(); QMap newChannelMap; QMap::iterator it = info.channelMap.begin(); newChannelMap["R"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "R")); ++it; newChannelMap["G"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "G")); ++it; newChannelMap["B"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "B")); if (info.channelMap.size() == 4) { ++it; newChannelMap["A"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "A")); } info.channelMap = newChannelMap; } } if (!modelId.isEmpty()) { info.colorSpace = kisTypeToColorSpace(modelId, info.imageType); } } // Get colorspace dbgFile << "Image type = " << imageType; const KoColorSpace* colorSpace = kisTypeToColorSpace(RGBAColorModelID.id(), imageType); if (!colorSpace) return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; dbgFile << "Colorspace: " << colorSpace->name(); // Set the colorspace on all groups for (int i = 0; i < groups.size(); ++i) { ExrGroupLayerInfo& info = groups[i]; info.colorSpace = colorSpace; } // Create the image m_image = new KisImage(m_doc->createUndoStore(), width, height, colorSpace, ""); if (!m_image) { return KisImageBuilder_RESULT_FAILURE; } // Create group layers for (int i = 0; i < groups.size(); ++i) { ExrGroupLayerInfo& info = groups[i]; Q_ASSERT(info.parent == 0 || info.parent->groupLayer); KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : m_image->rootLayer(); info.groupLayer = new KisGroupLayer(m_image, info.name, OPACITY_OPAQUE_U8); m_image->addNode(info.groupLayer, groupLayerParent); } // Load the layers for (int i = 0; i < informationObjects.size(); ++i) { ExrPaintLayerInfo& info = informationObjects[i]; if (info.colorSpace) { dbgFile << "Decoding " << info.name << " with " << info.channelMap.size() << " channels, and color space " << info.colorSpace->id(); KisPaintLayerSP layer = new KisPaintLayer(m_image, info.name, OPACITY_OPAQUE_U8, info.colorSpace); KisTransaction("", layer->paintDevice()); layer->setCompositeOp(COMPOSITE_OVER); if (!layer) { return KisImageBuilder_RESULT_FAILURE; } switch (info.channelMap.size()) { case 1: // Decode the data switch (imageType) { case IT_FLOAT16: decodeData1(file, info, layer, width, dx, dy, height, Imf::HALF); break; case IT_FLOAT32: decodeData1(file, info, layer, width, dx, dy, height, Imf::FLOAT); break; case IT_UNKNOWN: case IT_UNSUPPORTED: qFatal("Impossible error"); } break; case 3: case 4: // Decode the data switch (imageType) { case IT_FLOAT16: decodeData4(file, info, layer, width, dx, dy, height, Imf::HALF); break; case IT_FLOAT32: decodeData4(file, info, layer, width, dx, dy, height, Imf::FLOAT); break; case IT_UNKNOWN: case IT_UNSUPPORTED: qFatal("Impossible error"); } break; default: qFatal("Invalid number of channels: %i", info.channelMap.size()); } // Check if should set the channels if (!info.remappedChannels.isEmpty()) { QList values; foreach(const ExrPaintLayerInfo::Remap& remap, info.remappedChannels) { QMap map; map["original"] = KisMetaData::Value(remap.original); map["current"] = KisMetaData::Value(remap.current); values.append(map); } layer->metaData()->addEntry(KisMetaData::Entry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap", values)); } // Add the layer KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : m_image->rootLayer(); m_image->addNode(layer, groupLayerParent); } else { dbgFile << "No decoding " << info.name << " with " << info.channelMap.size() << " channels, and lack of a color space"; } } return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result exrConverter::buildImage(const KUrl& uri) { if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!KIO::NetAccess::exists(uri, KIO::NetAccess::DestinationSide, QApplication::activeWindow())) { return KisImageBuilder_RESULT_NOT_EXIST; } // We're not set up to handle asynchronous loading at the moment. KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; QString tmpFile; if (KIO::NetAccess::download(uri, tmpFile, qApp->activeWindow())) { KUrl uriTF; uriTF.setPath(tmpFile); result = decode(uriTF); KIO::NetAccess::removeTempFile(tmpFile); } return result; } KisImageWSP exrConverter::image() { return m_image; } struct ExrPaintLayerSaveInfo { QString name; ///< name of the layer with a "." at the end (ie "group1.group2.layer1.") KisPaintLayerSP layer; QList channels; Imf::PixelType pixelType; }; template struct ExrPixel_ { _T_ data[size]; }; class Encoder { public: virtual ~Encoder() {} virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line) = 0; virtual void encodeData(int line) = 0; }; template class EncoderImpl : public Encoder { public: EncoderImpl(Imf::OutputFile* _file, const ExrPaintLayerSaveInfo* _info, int width) : file(_file), info(_info), pixels(width), m_width(width) {} virtual ~EncoderImpl() {} virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line); virtual void encodeData(int line); private: typedef ExrPixel_<_T_, size> ExrPixel; Imf::OutputFile* file; const ExrPaintLayerSaveInfo* info; QVector pixels; int m_width; }; template void EncoderImpl<_T_, size, alphaPos>::prepareFrameBuffer(Imf::FrameBuffer* frameBuffer, int line) { int xstart = 0; int ystart = 0; ExrPixel* frameBufferData = (pixels.data()) - xstart - (ystart + line) * m_width; for (int k = 0; k < size; ++k) { frameBuffer->insert(info->channels[k].toUtf8(), Imf::Slice(info->pixelType, (char *) &frameBufferData->data[k], sizeof(ExrPixel) * 1, sizeof(ExrPixel) * m_width)); } } template void EncoderImpl<_T_, size, alphaPos>::encodeData(int line) { ExrPixel *rgba = pixels.data(); KisHLineIteratorSP it = info->layer->paintDevice()->createHLineIteratorNG(0, line, m_width); do { const _T_* dst = reinterpret_cast < const _T_* >(it->oldRawData()); if (alphaPos == -1) { for (int i = 0; i < size; ++i) { rgba->data[i] = dst[i]; } } else { _T_ alpha = dst[alphaPos]; for (int i = 0; i < size; ++i) { if (i != alphaPos) { rgba->data[i] = dst[i] * alpha; } } rgba->data[alphaPos] = alpha; } ++rgba; } while (it->nextPixel()); } Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width) { // bool hasAlpha = info.layer->colorSpace()->channelCount() != info.layer->colorSpace()->colorChannelCount(); switch (info.layer->colorSpace()->channelCount()) { case 1: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl < half, 1, -1 > (&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl < float, 1, -1 > (&file, &info, width); } break; } case 2: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl(&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl(&file, &info, width); } break; } case 4: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl(&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl(&file, &info, width); } break; } default: qFatal("Impossible error"); } return 0; } void encodeData(Imf::OutputFile& file, const QList& informationObjects, int width, int height) { QList encoders; foreach(const ExrPaintLayerSaveInfo& info, informationObjects) { encoders.push_back(encoder(file, info, width)); } for (int y = 0; y < height; ++y) { Imf::FrameBuffer frameBuffer; foreach(Encoder* encoder, encoders) { encoder->prepareFrameBuffer(&frameBuffer, y); } file.setFrameBuffer(frameBuffer); foreach(Encoder* encoder, encoders) { encoder->encodeData(y); } file.writePixels(1); } qDeleteAll(encoders); } KisImageBuilder_Result exrConverter::buildFile(const KUrl& uri, KisPaintLayerSP layer) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageWSP image = layer->image(); if (!image) return KisImageBuilder_RESULT_EMPTY; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_LOCAL; // Make the header qint32 height = image->height(); qint32 width = image->width(); Imf::Header header(width, height); Imf::PixelType pixelType = Imf::NUM_PIXELTYPES; if(layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { pixelType = Imf::HALF; } else if(layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { pixelType = Imf::FLOAT; } if(pixelType >= Imf::NUM_PIXELTYPES) { return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } header.channels().insert("R", Imf::Channel(pixelType)); header.channels().insert("G", Imf::Channel(pixelType)); header.channels().insert("B", Imf::Channel(pixelType)); header.channels().insert("A", Imf::Channel(pixelType)); ExrPaintLayerSaveInfo info; info.layer = layer; info.channels.push_back("B"); info.channels.push_back("G"); info.channels.push_back("R"); info.channels.push_back("A"); info.pixelType = pixelType; // Open file for writing Imf::OutputFile file(QFile::encodeName(uri.path()), header); QList informationObjects; informationObjects.push_back(info); encodeData(file, informationObjects, width, height); return KisImageBuilder_RESULT_OK; } QString remap(const QMap& current2original, const QString& current) { if (current2original.contains(current)) { return current2original[current]; } return current; } void recBuildPaintLayerSaveInfo(QList& informationObjects, const QString& name, KisGroupLayerSP parent) { for (uint i = 0; i < parent->childCount(); ++i) { KisNodeSP node = parent->at(i); if (KisPaintLayerSP paintLayer = dynamic_cast(node.data())) { QMap current2original; if (paintLayer->metaData()->containsEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap")) { const KisMetaData::Entry& entry = paintLayer->metaData()->getEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap"); QList< KisMetaData::Value> values = entry.value().asArray(); foreach(const KisMetaData::Value& value, values) { QMap map = value.asStructure(); if (map.contains("original") && map.contains("current")) { current2original[map["current"].toString()] = map["original"].toString(); } } } ExrPaintLayerSaveInfo info; info.name = name + paintLayer->name() + '.'; info.layer = paintLayer; if (paintLayer->colorSpace()->colorModelId() == RGBAColorModelID) { info.channels.push_back(info.name + remap(current2original, "B")); info.channels.push_back(info.name + remap(current2original, "G")); info.channels.push_back(info.name + remap(current2original, "R")); info.channels.push_back(info.name + remap(current2original, "A")); } else if (paintLayer->colorSpace()->colorModelId() == GrayAColorModelID) { info.channels.push_back(info.name + remap(current2original, "G")); info.channels.push_back(info.name + remap(current2original, "A")); } else if (paintLayer->colorSpace()->colorModelId() == GrayColorModelID) { info.channels.push_back(info.name + remap(current2original, "G")); } else if (paintLayer->colorSpace()->colorModelId() == XYZAColorModelID) { info.channels.push_back(info.name + remap(current2original, "X")); info.channels.push_back(info.name + remap(current2original, "Y")); info.channels.push_back(info.name + remap(current2original, "Z")); info.channels.push_back(info.name + remap(current2original, "A")); } if (paintLayer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { info.pixelType = Imf::HALF; } else if (paintLayer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { info.pixelType = Imf::FLOAT; } else { info.pixelType = Imf::NUM_PIXELTYPES; } if(info.pixelType < Imf::NUM_PIXELTYPES) { informationObjects.push_back(info); } else { // TODO should probably inform that one of the layer cannot be saved. } } else if (KisGroupLayerSP groupLayer = dynamic_cast(node.data())) { recBuildPaintLayerSaveInfo(informationObjects, name + groupLayer->name() + '.', groupLayer); } } } KisImageBuilder_Result exrConverter::buildFile(const KUrl& uri, KisGroupLayerSP layer) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageWSP image = layer->image(); if (!image) return KisImageBuilder_RESULT_EMPTY; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_LOCAL; qint32 height = image->height(); qint32 width = image->width(); Imf::Header header(width, height); QList informationObjects; recBuildPaintLayerSaveInfo(informationObjects, "", layer); if(informationObjects.isEmpty()) { return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } dbgFile << informationObjects.size() << " layers to save"; foreach(const ExrPaintLayerSaveInfo& info, informationObjects) { if (info.pixelType < Imf::NUM_PIXELTYPES) { foreach(const QString& channel, info.channels) { dbgFile << channel << " " << info.pixelType; header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType)); } } } // Open file for writing Imf::OutputFile file(QFile::encodeName(uri.path()), header); encodeData(file, informationObjects, width, height); return KisImageBuilder_RESULT_OK; } void exrConverter::cancel() { m_stop = true; } #include "exr_converter.moc" diff --git a/krita/plugins/formats/psd/psd_layer_record.cpp b/krita/plugins/formats/psd/psd_layer_record.cpp index cb51543be55..13e40089f05 100644 --- a/krita/plugins/formats/psd/psd_layer_record.cpp +++ b/krita/plugins/formats/psd/psd_layer_record.cpp @@ -1,1144 +1,1144 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "psd_layer_record.h" #include // htonl #include #include #include #include #include #include #include "kis_iterator_ng.h" #include #include "psd_utils.h" #include "psd_header.h" #include "compression.h" #include #include #include #include // htonl // Just for pretty debug messages QString channelIdToChannelType(int channelId, PSDColorMode colormode) { switch(channelId) { case -2: return "User Supplied Layer Mask"; case -1: return "Transparency mask"; case 0: switch(colormode) { case Bitmap: case Indexed: return QString("bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return "gray"; case RGB: case RGB48: return "red"; case Lab: case Lab48: return "L"; case CMYK: case CMYK64: return "cyan"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; case 1: switch(colormode) { case Bitmap: case Indexed: return QString("WARNING bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return QString("WARNING: %1").arg(channelId); case RGB: case RGB48: return "green"; case Lab: case Lab48: return "a"; case CMYK: case CMYK64: return "Magenta"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; case 2: switch(colormode) { case Bitmap: case Indexed: return QString("WARNING bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return QString("WARNING: %1").arg(channelId); case RGB: case RGB48: return "blue"; case Lab: case Lab48: return "b"; case CMYK: case CMYK64: return "yellow"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; case 3: switch(colormode) { case Bitmap: case Indexed: return QString("WARNING bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return QString("WARNING: %1").arg(channelId); case RGB: case RGB48: return QString("alpha: %1").arg(channelId); case Lab: case Lab48: return QString("alpha: %1").arg(channelId); case CMYK: case CMYK64: return "Key"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; default: return QString("unknown: %1").arg(channelId); }; } PSDLayerRecord::PSDLayerRecord(const PSDHeader& header) : top(0) , left(0) , bottom(0) , right(0) , nChannels(0) , opacity(0) , clipping(0) , transparencyProtected(false) , visible(true) , irrelevant(false) , layerName("UNINITIALIZED") , m_header(header) { } bool PSDLayerRecord::read(QIODevice* io) { dbgFile << "Going to read layer record. Pos:" << io->pos(); if (!psdread(io, &top) || !psdread(io, &left) || !psdread(io, &bottom) || !psdread(io, &right) || !psdread(io, &nChannels)) { error = "could not read layer record"; return false; } dbgFile << "\ttop" << top << "left" << left << "bottom" << bottom << "right" << right << "number of channels" << nChannels; Q_ASSERT(top <= bottom); Q_ASSERT(left <= right); Q_ASSERT(nChannels > 0); switch(m_header.colormode) { case(Bitmap): case(Indexed): case(DuoTone): case(Grayscale): case(MultiChannel): if (nChannels < 1) { error = QString("Not enough channels. Got: %1").arg(nChannels); return false; } break; case(RGB): case(CMYK): case(Lab): default: if (nChannels < 3) { error = QString("Not enough channels. Got: %1").arg(nChannels); return false; } break; }; if (nChannels > MAX_CHANNELS) { error = QString("Too many channels. Got: %1").arg(nChannels); return false; } for (int i = 0; i < nChannels; ++i) { if (io->atEnd()) { error = "Could not read enough data for channels"; return false; } ChannelInfo* info = new ChannelInfo; if (!psdread(io, &info->channelId)) { error = "could not read channel id"; delete info; return false; } bool r; if (m_header.version == 1) { quint32 channelDataLength; r = psdread(io, &channelDataLength); info->channelDataLength = (quint64)channelDataLength; } else { r = psdread(io, &info->channelDataLength); } if (!r) { error = "Could not read length for channel data"; delete info; return false; } // dbgFile << "\tchannel" << i << "id" // << channelIdToChannelType(info->channelId, m_header.colormode) // << "length" << info->channelDataLength // << "start" << info->channelDataStart // << "offset" << info->channelOffset // << "channelInfoPosition" << info->channelInfoPosition; channelInfoRecords << info; } QByteArray b; b = io->read(4); if(b.size() != 4 || QString(b) != "8BIM") { error = QString("Could not read blend mode signature for layer. Got %1.") .arg(QString(b)); return false; } dbgFile << "reading blend mode at pos" << io->pos(); blendModeKey = QString(io->read(4)); if (blendModeKey.size() != 4) { error = QString("Could not read blend mode key. Got: %1").arg(blendModeKey); return false; } dbgFile << "\tBlend mode" << blendModeKey << "pos" << io->pos(); if (!psdread(io, &opacity)) { error = "Could not read opacity"; return false; } dbgFile << "\tOpacity" << opacity << io->pos(); if (!psdread(io, &clipping)) { error = "Could not read clipping"; return false; } dbgFile << "\tclipping" << clipping << io->pos(); quint8 flags; if (!psdread(io, &flags)) { error = "Could not read flags"; return false; } dbgFile << "\tflags" << flags << io->pos(); transparencyProtected = flags & 1 ? true : false; dbgFile << "\ttransparency protected" << transparencyProtected; visible = flags & 2 ? false : true; dbgFile << "\tvisible" << visible; if (flags & 8) { irrelevant = flags & 16 ? true : false; } else { irrelevant = false; } dbgFile << "\tirrelevant" << irrelevant; dbgFile << "\tfiller at " << io->pos(); quint8 filler; if (!psdread(io, &filler) || filler != 0) { error = "Could not read padding"; return false; } dbgFile << "\tGoing to read extra data length" << io->pos(); quint32 extraDataLength; if (!psdread(io, &extraDataLength) || io->bytesAvailable() < extraDataLength) { error = QString("Could not read extra layer data: %1 at pos %2").arg(extraDataLength).arg(io->pos()); return false; } dbgFile << "\tExtra data length" << extraDataLength; if (extraDataLength > 0) { dbgFile << "Going to read extra data field. Bytes available: " << io->bytesAvailable() << "pos" << io->pos(); quint32 layerMaskLength = 1; // invalid... if (!psdread(io, &layerMaskLength) || io->bytesAvailable() < layerMaskLength || !(layerMaskLength == 0 || layerMaskLength == 20 || layerMaskLength == 36)) { error = QString("Could not read layer mask length: %1").arg(layerMaskLength); return false; } memset(&layerMask, 0, sizeof(LayerMaskData)); if (layerMaskLength == 20 || layerMaskLength == 36) { if (!psdread(io, &layerMask.top) || !psdread(io, &layerMask.left) || !psdread(io, &layerMask.bottom) || !psdread(io, &layerMask.top) || !psdread(io, &layerMask.defaultColor) || !psdread(io, &flags)) { error = "could not read mask record"; return false; } } if (layerMaskLength == 20) { quint16 padding; if (!psdread(io, &padding)) { error = "Could not read layer mask padding"; return false; } } if (layerMaskLength == 36 ) { if (!psdread(io, &flags) || !psdread(io, &layerMask.defaultColor) || !psdread(io, &layerMask.top) || !psdread(io, &layerMask.left) || !psdread(io, &layerMask.bottom) || !psdread(io, &layerMask.top)) { error = "could not read 'real' mask record"; return false; } } layerMask.positionedRelativeToLayer = flags & 1 ? true : false; layerMask.disabled = flags & 2 ? true : false; layerMask.invertLayerMaskWhenBlending = flags & 4 ? true : false; dbgFile << "\tRead layer mask/adjustment layer data. Length of block:" << layerMaskLength << "pos" << io->pos(); // layer blending thingies quint32 blendingDataLength; if (!psdread(io, &blendingDataLength) || io->bytesAvailable() < blendingDataLength) { error = "Could not read extra blending data."; return false; } //dbgFile << "blending block data length" << blendingDataLength << ", pos" << io->pos(); blendingRanges.data = io->read(blendingDataLength); if ((quint32)blendingRanges.data.size() != blendingDataLength) { error = QString("Got %1 bytes for the blending range block, needed %2").arg(blendingRanges.data.size(), blendingDataLength); } /* // XXX: reading this block correctly failed, I have more channel ranges than I'd expected. if (!psdread(io, &blendingRanges.blackValues[0]) || !psdread(io, &blendingRanges.blackValues[1]) || !psdread(io, &blendingRanges.whiteValues[0]) || !psdread(io, &blendingRanges.whiteValues[1]) || !psdread(io, &blendingRanges.compositeGrayBlendDestinationRange)) { error = "Could not read blending black/white values"; return false; } for (int i = 0; i < nChannels; ++i) { quint32 src; quint32 dst; if (!psdread(io, &src) || !psdread(io, &dst)) { error = QString("could not read src/dst range for channel %1").arg(i); return false; } dbgFile << "\tread range " << src << "to" << dst << "for channel" << i; blendingRanges.sourceDestinationRanges << QPair(src, dst); } */ dbgFile << "\tGoing to read layer name at" << io->pos(); quint8 layerNameLength; if (!psdread(io, &layerNameLength)) { error = "Could not read layer name length"; return false; } dbgFile << "\tlayer name length unpadded" << layerNameLength << "pos" << io->pos(); layerNameLength = ((layerNameLength + 1 + 3) & ~0x03) - 1; dbgFile << "\tlayer name length padded" << layerNameLength << "pos" << io->pos(); layerName = io->read(layerNameLength); dbgFile << "\tlayer name" << layerName << io->pos(); QStringList longBlocks; if (m_header.version > 1) { longBlocks << "LMsk" << "Lr16" << "Layr" << "Mt16" << "Mtrn" << "Alph"; } while(!io->atEnd()) { // read all the additional layer info 8BIM blocks QByteArray b; b = io->peek(4); if(b.size() != 4 || QString(b) != "8BIM") { break; } else { io->seek(io->pos() + 4); // skip the 8BIM header we peeked ahead for } QString key(io->read(4)); if (key.size() != 4) { error = "Could not read key for additional layer info block"; return false; } dbgFile << "found info block with key" << key; if (infoBlocks.contains(key)) { error = QString("Duplicate layer info block with key %1").arg(key); return false; } quint64 size; if (longBlocks.contains(key)) { psdread(io, &size); } else { quint32 _size; psdread(io, &_size); size = _size; } LayerInfoBlock* infoBlock = new LayerInfoBlock(); infoBlock->data = io->read(size); if (infoBlock->data.size() != (qint64)size) { error = QString("Could not read full info block for key %1 for layer %2").arg(key).arg(layerName); return false; } dbgFile << "\tRead layer info block" << key << "for size" << infoBlock->data.size(); // get the unicode layer name if (key == "luni") { QBuffer buf(&infoBlock->data); buf.open(QBuffer::ReadOnly); quint32 stringlen; if (!psdread(&buf, &stringlen)) { error = "Could not read string length for luni block"; return false; } QString unicodeLayerName; for (uint i = 0; i < stringlen; ++i) { quint16 ch; psdread(&buf, &ch); QChar uch(ch); unicodeLayerName.append(uch); } dbgFile << "unicodeLayerName" << unicodeLayerName; if (!unicodeLayerName.isEmpty()) { layerName = unicodeLayerName; } } infoBlocks[key] = infoBlock; } } return valid(); } bool PSDLayerRecord::write(QIODevice* io, KisNodeSP node) { dbgFile << "writing layer info record" << node->name() << "at" << io->pos(); m_node = node; dbgFile << "saving layer record for " << layerName << "at pos" << io->pos(); dbgFile << "\ttop" << top << "left" << left << "bottom" << bottom << "right" << right << "number of channels" << nChannels; Q_ASSERT(left <= right); Q_ASSERT(top <= bottom); Q_ASSERT(nChannels > 0); psdwrite(io, (quint32)top); psdwrite(io, (quint32)left); psdwrite(io, (quint32)bottom); psdwrite(io, (quint32)right); psdwrite(io, (quint16)nChannels); foreach(ChannelInfo *channel, channelInfoRecords) { psdwrite(io, (quint16)channel->channelId); channel->channelInfoPosition = io->pos(); dbgFile << "ChannelInfo record position:" << channel->channelInfoPosition << "channel id" << channel->channelId; psdwrite(io, (quint32)0); // to be filled in when we know how big each channel block is going to be } // blend mode io->write("8BIM", 4); dbgFile << "blendModeKey" << blendModeKey << "pos" << io->pos(); - io->write(blendModeKey.toAscii()); + io->write(blendModeKey.toLatin1()); // opacity psdwrite(io, opacity); // clipping - unused psdwrite(io, clipping); // visibility and protection quint8 flags = 0; if (transparencyProtected) flags |= 1; if (!visible) flags |= 2; psdwrite(io, flags); // padding byte to make the length even psdwrite(io, (quint8)0); // position of the extra data size quint64 extraDataPos = io->pos(); psdwrite(io, (quint32)0); // length of the extra data fields // layer mask data: not implemented for now, so zero psdwrite(io, quint32(0)); // Layer blending ranges: not implemented for now, so zero psdwrite(io, quint32(0)); // layer name: Pascal string, padded to a multiple of 4 bytes. psdwrite_pascalstring(io, layerName, 4); // write luni data block { quint32 len = qMin(layerName.length(), 255); quint32 xdBlockSize = len; if (len % 2) { xdBlockSize = len + 1; } xdBlockSize = (xdBlockSize * 2) + 4; io->write("8BIMluni", 8); psdwrite(io, xdBlockSize); psdwrite(io, len); const ushort *chars = layerName.utf16(); for (uint i = 0; i < len; i++) { psdwrite(io, (quint16)chars[i]); } if (len % 2) { psdwrite(io, (quint16)0); // padding } } // write real length for extra data quint64 eofPos = io->pos(); io->seek(extraDataPos); psdwrite(io, (quint32)(eofPos - extraDataPos - sizeof(quint32))); dbgFile << "ExtraData size" << (eofPos - extraDataPos - sizeof(quint32)) << "extra data pos" << extraDataPos << "eofpos" << eofPos; // retor to eof to continue writing io->seek(eofPos); return true; } bool PSDLayerRecord::writePixelData(QIODevice *io) { dbgFile << "writing pixel data for layer" << layerName << "at" << io->pos(); KisPaintDeviceSP dev = m_node->projection(); // now write all the channels in display order QRect rc = dev->extent(); // yeah... we read the entire layer into a vector of quint8 arrays dbgFile << "layer" << layerName; dbgFile << "\tnode x" << m_node->x() << "paint device x" << dev->x() << "extent x" << rc.x(); dbgFile << "\tnode y" << m_node->y() << "paint device x" << dev->y() << "extent y" << rc.y(); QVector tmp = dev->readPlanarBytes(rc.x() - m_node->x(), rc.y() -m_node->y(), rc.width(), rc.height()); // KisPaintDeviceSP dev2 = new KisPaintDevice(dev->colorSpace()); // dev2->writePlanarBytes(tmp, 0, 0, rc.width(), rc.height()); // dev2->convertToQImage(0).save(layerName + ".png"); // then reorder the planes to fit the psd model -- alpha first, then display order QVector planes; foreach(KoChannelInfo *ch, KoChannelInfo::displayOrderSorted(dev->colorSpace()->channels())) { if (ch->channelType() == KoChannelInfo::ALPHA) { planes.insert(0, tmp[ch->pos()]); } else { planes.append(tmp[ch->pos()]); } } // here's where we save the total size of the channel data for (int channelInfoIndex = 0; channelInfoIndex < nChannels; ++channelInfoIndex) { dbgFile << "\tWriting channel" << channelInfoIndex << "psd channel id" << channelInfoRecords[channelInfoIndex]->channelId; // if the bitdepth > 8, place the bytes in the right order // if cmyk, invert the pixel value if (m_header.channelDepth == 8) { if (channelInfoRecords[channelInfoIndex]->channelId >= 0 && (m_header.colormode == CMYK || m_header.colormode == CMYK64)) { for (int i = 0; i < rc.width() * rc.height(); ++i) { planes[channelInfoIndex][i] = 255 - planes[channelInfoIndex][i]; } } } else if (m_header.channelDepth == 16) { quint16 val; for (int i = 0; i < rc.width() * rc.height(); ++i) { val = reinterpret_cast(planes[channelInfoIndex])[i]; val = ntohs(val); if (channelInfoRecords[channelInfoIndex]->channelId >= 0 && (m_header.colormode == CMYK || m_header.colormode == CMYK64)) { val = quint16_MAX - val; } reinterpret_cast(planes[channelInfoIndex])[i] = val; } } quint32 len = 0; // where this block starts, for the total size calculation quint64 startChannelBlockPos = io->pos(); // XXX: make the compression settting configurable. For now, always use RLE. psdwrite(io, (quint16)Compression::RLE); len += sizeof(quint16); // where this block starts, for the total size calculation quint64 channelRLESizePos = io->pos(); // write zero's for the channel lengths section for(int i = 0; i < rc.height(); ++i) { psdwrite(io, (quint16)0); } len += rc.height() * sizeof(quint16); // here the actual channel data starts; that's where we return after writing // the size of the current row quint64 channelStartPos = io->pos(); quint8 *plane = planes[channelInfoIndex]; quint32 stride = (m_header.channelDepth / 8) * rc.width(); for (qint32 row = 0; row < rc.height(); ++row) { QByteArray uncompressed = QByteArray::fromRawData((const char*)plane + row * stride, stride); QByteArray compressed = Compression::compress(uncompressed, Compression::RLE); quint16 size = compressed.size(); io->seek(channelRLESizePos); psdwrite(io, size); channelRLESizePos +=2; io->seek(channelStartPos); if (!io->write(compressed) == size) { error = "Could not write image data"; return false; } len += size; // dbgFile << "\t\tUncompressed:" << uncompressed.size() << "compressed" << compressed.size(); // QByteArray control = Compression::uncompress(rc.width(), compressed, Compression::RLE); // Q_ASSERT(qstrcmp(control, uncompressed) == 0); // If the layer's size, and therefore the data, is odd, a pad byte will be inserted // at the end of the row. (weirdly enough, that's not true for the image data) // if ((size & 0x01) != 0) { // psdwrite(io, (quint8)0); // size++; // } channelStartPos += size; } // write the size of the channel image data block in the channel info block quint64 currentPos = io->pos(); io->seek(channelInfoRecords[channelInfoIndex]->channelInfoPosition); Q_ASSERT(len == currentPos - startChannelBlockPos); dbgFile << "\t\ttotal length" << len << "calculated length" << currentPos - startChannelBlockPos << "writing at" << channelInfoRecords[channelInfoIndex]->channelInfoPosition; psdwrite(io, (quint32)(currentPos - startChannelBlockPos)); io->seek(currentPos); } return true; } bool PSDLayerRecord::valid() { // XXX: check validity! return true; } bool PSDLayerRecord::readPixelData(QIODevice *io, KisPaintDeviceSP device) { dbgFile << "Reading pixel data for layer" << layerName << "pos" << io->pos(); switch (m_header.colormode) { case Bitmap: error = "Unsupported color mode: bitmap"; return false; // Not supported; case Indexed: error = "Unsupported color mode: indexed"; return false; // Not supported; case MultiChannel: error = "Unsupported color mode: indexed"; return false; // Not supported case DuoTone: error = "Unsupported color mode: Duotone"; return false; // Not supported case Grayscale: return doGrayscale(device, io); case RGB: return doRGB(device, io); case CMYK: return doCMYK(device, io); case Lab: return doLAB(device, io); case UNKNOWN: default: return false; } return false; } bool PSDLayerRecord::doGrayscale(KisPaintDeviceSP /*dev*/, QIODevice */*io*/) { return false; } bool PSDLayerRecord::doRGB(KisPaintDeviceSP dev, QIODevice *io) { quint64 oldPosition = io->pos(); qint64 width = right - left; if (width <= 0) { dbgFile << "Empty layer"; return true; } int channelSize = m_header.channelDepth / 8; int uncompressedLength = width * channelSize; if (channelInfoRecords.first()->compressionType == Compression::ZIP || channelInfoRecords.first()->compressionType == Compression::ZIPWithPrediction) { error = "Unsupported Compression mode: zip"; return false; } KisHLineIteratorSP it = dev->createHLineIteratorNG(left, top, width); for (int row = top ; row < bottom; row++) { QMap channelBytes; foreach(ChannelInfo *channelInfo, channelInfoRecords) { io->seek(channelInfo->channelDataStart + channelInfo->channelOffset); if (channelInfo->compressionType == Compression::Uncompressed) { channelBytes[channelInfo->channelId] = io->read(uncompressedLength); channelInfo->channelOffset += uncompressedLength; } else if (channelInfo->compressionType == Compression::RLE) { int rleLength = channelInfo->rleRowLengths[row - top]; QByteArray compressedBytes = io->read(rleLength); QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, compressedBytes, channelInfo->compressionType); channelBytes.insert(channelInfo->channelId, uncompressedBytes); channelInfo->channelOffset += rleLength; } else { error = "Unsupported Compression mode: " + channelInfo->compressionType; return false; } } for (qint64 col = 0; col < width; col++){ if (channelSize == 1) { quint8 opacity = OPACITY_OPAQUE_U8; if (channelBytes.contains(-1)) { opacity = channelBytes[-1].constData()[col]; } KoBgrU8Traits::setOpacity(it->rawData(), opacity, 1); quint8 red = channelBytes[0].constData()[col]; KoBgrU8Traits::setRed(it->rawData(), red); quint8 green = channelBytes[1].constData()[col]; KoBgrU8Traits::setGreen(it->rawData(), green); quint8 blue = channelBytes[2].constData()[col]; KoBgrU8Traits::setBlue(it->rawData(), blue); } else if (channelSize == 2) { quint16 opacity = quint16_MAX; if (channelBytes.contains(-1)) { opacity = channelBytes[-1].constData()[col]; } // We don't have a convenient setOpacity function :-( memcpy(it->rawData() + KoBgrU16Traits::alpha_pos, &opacity, sizeof(quint16)); quint16 red = ntohs(reinterpret_cast(channelBytes[0].constData())[col]); KoBgrU16Traits::setRed(it->rawData(), red); quint16 green = ntohs(reinterpret_cast(channelBytes[1].constData())[col]); KoBgrU16Traits::setGreen(it->rawData(), green); quint16 blue = ntohs(reinterpret_cast(channelBytes[2].constData())[col]); KoBgrU16Traits::setBlue(it->rawData(), blue); } else { // Unsupported channel sizes for now return false; } /* // XXX see implementation Openexr else if (channelSize == 4) { quint16 red = ntohs(reinterpret_cast(channelBytes.constData())[col]); KoBgrU16Traits::setRed(it->rawData(), red); quint16 green = ntohs(reinterpret_cast(channelBytes.constData())[col]); KoBgrU16Traits::setGreen(it->rawData(), green); quint16 blue = ntohs(reinterpret_cast(channelBytes.constData())[col]); KoBgrU16Traits::setBlue(it->rawData(), blue); } */ it->nextPixel(); } it->nextRow(); } // go back to the old position, because we've been seeking all over the place io->seek(oldPosition); return true; } bool PSDLayerRecord::doCMYK(KisPaintDeviceSP dev, QIODevice *io) { dbgFile << "doCMYK for" << layerName << "channels:" << channelInfoRecords.size() << "compression" << channelInfoRecords.first()->compressionType; dbgFile << "top" << top << "bottom" << bottom << "left" << left << "right" << right; quint64 oldPosition = io->pos(); quint64 width = right - left; int channelSize = m_header.channelDepth / 8; int uncompressedLength = width * channelSize; if (channelInfoRecords.first()->compressionType == Compression::ZIP || channelInfoRecords.first()->compressionType == Compression::ZIPWithPrediction) { dbgFile << "zippedy-do-da!"; // Zip needs to be implemented here. return false; } KisHLineIteratorSP it = dev->createHLineIteratorNG(left, top, width); for (int row = top ; row < bottom; row++) { QMap channelBytes; foreach(ChannelInfo *channelInfo, channelInfoRecords) { io->seek(channelInfo->channelDataStart + channelInfo->channelOffset); if (channelInfo->compressionType == Compression::Uncompressed) { channelBytes[channelInfo->channelId] = io->read(uncompressedLength); channelInfo->channelOffset += uncompressedLength; } else if (channelInfo->compressionType == Compression::RLE) { int rleLength = channelInfo->rleRowLengths[row - top]; QByteArray compressedBytes = io->read(rleLength); QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, compressedBytes, channelInfo->compressionType); channelBytes.insert(channelInfo->channelId, uncompressedBytes); channelInfo->channelOffset += rleLength; } } for (quint64 col = 0; col < width; col++){ if (channelSize == 1) { quint8 opacity = OPACITY_OPAQUE_U8; if (channelBytes.contains(-1)) { opacity = channelBytes[-1].constData()[col]; } quint8 *pixel = new quint8[5]; memset(pixel, 0, 5); dev->colorSpace()->setOpacity(pixel, opacity, 1); memset(pixel, 255 - channelBytes[0].constData()[col], 1); memset(pixel + 1, 255 - channelBytes[1].constData()[col], 1); memset(pixel + 2, 255 - channelBytes[2].constData()[col], 1); memset(pixel + 3, 255 - channelBytes[3].constData()[col], 1); //dbgFile << "C" << pixel[0] << "M" << pixel[1] << "Y" << pixel[2] << "K" << pixel[3] << "A" << pixel[4]; memcpy(it->rawData(), pixel, 5); } else if (channelSize == 2) { quint16 opacity = quint16_MAX; if (channelBytes.contains(-1)) { opacity = channelBytes[-1].constData()[col]; } // We don't have a convenient setOpacity function :-( memcpy(it->rawData() + KoCmykTraits::alpha_pos, &opacity, sizeof(quint16)); quint16 C = ntohs(reinterpret_cast(channelBytes[0].constData())[col]); KoCmykTraits::setC(it->rawData(),C); quint16 M = ntohs(reinterpret_cast(channelBytes[1].constData())[col]); KoCmykTraits::setM(it->rawData(),M); quint16 Y = ntohs(reinterpret_cast(channelBytes[2].constData())[col]); KoCmykTraits::setY(it->rawData(),Y); quint16 K = ntohs(reinterpret_cast(channelBytes[3].constData())[col]); KoCmykTraits::setK(it->rawData(),K); } // XXX see implementation Openexr else if (channelSize == 4) { quint32 C = ntohs(reinterpret_cast(channelBytes[0].constData())[col]); KoCmykTraits::setC(it->rawData(),C); quint32 M = ntohs(reinterpret_cast(channelBytes[1].constData())[col]); KoCmykTraits::setM(it->rawData(),M); quint32 Y = ntohs(reinterpret_cast(channelBytes[2].constData())[col]); KoCmykTraits::setY(it->rawData(),Y); quint32 K = ntohs(reinterpret_cast(channelBytes[3].constData())[col]); KoCmykTraits::setK(it->rawData(),K); } else { // Unsupported channel sizes for now return false; } it->nextPixel(); } it->nextRow(); } // go back to the old position, because we've been seeking all over the place io->seek(oldPosition); return true; } bool PSDLayerRecord::doLAB(KisPaintDeviceSP dev, QIODevice *io) { quint64 oldPosition = io->pos(); quint64 width = right - left; int channelSize = m_header.channelDepth / 8; int uncompressedLength = width * channelSize; if (channelInfoRecords.first()->compressionType == Compression::ZIP || channelInfoRecords.first()->compressionType == Compression::ZIPWithPrediction) { // Zip needs to be implemented here. return false; } KisHLineIteratorSP it = dev->createHLineIteratorNG(left, top, width); for (int row = top ; row < bottom; row++) { QMap channelBytes; foreach(ChannelInfo *channelInfo, channelInfoRecords) { io->seek(channelInfo->channelDataStart + channelInfo->channelOffset); if (channelInfo->compressionType == Compression::Uncompressed) { channelBytes[channelInfo->channelId] = io->read(uncompressedLength); channelInfo->channelOffset += uncompressedLength; } else if (channelInfo->compressionType == Compression::RLE) { int rleLength = channelInfo->rleRowLengths[row - top]; QByteArray compressedBytes = io->read(rleLength); QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, compressedBytes, channelInfo->compressionType); channelBytes.insert(channelInfo->channelId, uncompressedBytes); channelInfo->channelOffset += rleLength; } } for (quint64 col = 0; col < width; col++){ if (channelSize == 1) { quint8 opacity = OPACITY_OPAQUE_U8; if (channelBytes.contains(-1)) { opacity = channelBytes[-1].constData()[col]; } KoLabTraits::setOpacity(it->rawData(), opacity, 1); quint8 L = ntohs(reinterpret_cast(channelBytes[0].constData())[col]); KoLabTraits::setL(it->rawData(),L); quint8 A = ntohs(reinterpret_cast(channelBytes[1].constData())[col]); KoLabTraits::setA(it->rawData(),A); quint8 B = ntohs(reinterpret_cast(channelBytes[2].constData())[col]); KoLabTraits::setB(it->rawData(),B); } else if (channelSize == 2) { quint16 opacity = quint16_MAX; if (channelBytes.contains(-1)) { opacity = channelBytes[-1].constData()[col]; } // We don't have a convenient setOpacity function :-( memcpy(it->rawData() + KoLabU16Traits::alpha_pos, &opacity, sizeof(quint16)); // KoLabTraits::setOpacity(it->rawData(), opacity, 1); quint16 L = ntohs(reinterpret_cast(channelBytes[0].constData())[col]); KoLabTraits::setL(it->rawData(),L); quint16 A = ntohs(reinterpret_cast(channelBytes[1].constData())[col]); KoLabTraits::setA(it->rawData(),A); quint16 B = ntohs(reinterpret_cast(channelBytes[2].constData())[col]); KoLabTraits::setB(it->rawData(),B); } else { // Unsupported channel sizes for now return false; } it->nextPixel(); } it->nextRow(); } // go back to the old position, because we've been seeking all over the place io->seek(oldPosition); return true; } QDebug operator<<(QDebug dbg, const PSDLayerRecord &layer) { #ifndef NODEBUG dbg.nospace() << "valid: " << const_cast(&layer)->valid(); dbg.nospace() << ", name: " << layer.layerName; dbg.nospace() << ", top: " << layer.top; dbg.nospace() << ", left:" << layer.left; dbg.nospace() << ", bottom: " << layer.bottom; dbg.nospace() << ", right: " << layer.right; dbg.nospace() << ", number of channels: " << layer.nChannels; dbg.nospace() << ", blendModeKey: " << layer.blendModeKey; dbg.nospace() << ", opacity: " << layer.opacity; dbg.nospace() << ", clipping: " << layer.clipping; dbg.nospace() << ", transparency protected: " << layer.transparencyProtected; dbg.nospace() << ", visible: " << layer.visible; dbg.nospace() << ", irrelevant: " << layer.irrelevant << "\n"; foreach(const ChannelInfo* channel, layer.channelInfoRecords) { dbg.space() << channel; } #endif return dbg.nospace(); } QDebug operator<<(QDebug dbg, const ChannelInfo &channel) { #ifndef NODEBUG dbg.nospace() << "\tChannel type" << channel.channelId << "size: " << channel.channelDataLength << "compression type" << channel.compressionType << "\n"; #endif return dbg.nospace(); } diff --git a/krita/plugins/formats/psd/psd_utils.cpp b/krita/plugins/formats/psd/psd_utils.cpp index 618abb1e7e7..3527747a440 100644 --- a/krita/plugins/formats/psd/psd_utils.cpp +++ b/krita/plugins/formats/psd/psd_utils.cpp @@ -1,227 +1,227 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "psd_utils.h" #include #include #include // htonl #include bool psdwrite(QIODevice* io, quint8 v) { int written = io->write((char*)&v, 1); return written == 1; } bool psdwrite(QIODevice* io, quint16 v) { quint16 val = ntohs(v); int written = io->write((char*)&val, 2); return written == 2; } bool psdwrite(QIODevice* io, qint16 v) { qint16 val = ntohs(v); int written = io->write((char*)&val, 2); return written == 2; } bool psdwrite(QIODevice* io, quint32 v) { quint32 val = ntohl(v); int written = io->write((char*)&val, 4); return written == 4; } bool psdpad(QIODevice* io, quint32 padding) { char* pad = new char[padding]; memset(pad, 0, padding); quint32 written = io->write(pad, padding); delete [] pad; return written == padding; } bool psdwrite(QIODevice* io, const QString &s) { int l = s.length(); - QByteArray b = s.toAscii(); + QByteArray b = s.toLatin1(); char* str = b.data(); int written = io->write(str, l); return written == l; } bool psdwrite_pascalstring(QIODevice* io, const QString &s) { Q_ASSERT(s.length() < 256); Q_ASSERT(s.length() >= 0); if (s.length() < 0 || s.length() > 255) return false; if (s.isNull()) { psdwrite(io, (quint8)0); psdwrite(io, (quint8)0); return true; } quint8 length = s.length(); psdwrite(io, length); - QByteArray b = s.toAscii(); + QByteArray b = s.toLatin1(); char* str = b.data(); int written = io->write(str, length); if (written != length) return false; if ((length & 0x01) != 0) { return psdwrite(io, (quint8)0); } return true; } bool psdwrite_pascalstring(QIODevice* io, const QString &s, int padding) { Q_ASSERT(s.length() < 256); Q_ASSERT(s.length() >= 0); if (s.length() < 0 || s.length() > 255) return false; if (s.isNull()) { psdwrite(io, (quint8)0); psdwrite(io, (quint8)0); return true; } quint8 length = s.length(); psdwrite(io, length); - QByteArray b = s.toAscii(); + QByteArray b = s.toLatin1(); char* str = b.data(); int written = io->write(str, length); if (written != length) return false; // If the total length (length byte + content) is not a multiple of padding, add zeroes to pad length++; if ((length % padding) != 0) { for (int i = 0; i < (padding - (length %padding)); i++) { psdwrite(io, (quint8)0); } } return true; } bool psdread(QIODevice *io, quint8 *v) { quint64 read = io->read((char*)v, 1); if (read != 1) return false; return true; } bool psdread(QIODevice* io, quint16* v) { quint16 val; quint64 read = io->read((char*)&val, 2); if (read != 2) return false; *v = ntohs(val); return true; } bool psdread(QIODevice* io, qint16* v) { qint16 val; quint64 read = io->read((char*)&val, 2); if (read != 2) return false; *v = ntohs(val); return true; } bool psdread(QIODevice* io, quint32* v) { quint32 val; quint64 read = io->read((char*)&val, 4); if (read != 4) return false; *v = ntohl(val); return true; } bool psdread(QIODevice* io, qint32* v) { qint32 val; quint64 read = io->read((char*)&val, 4); if (read != 4) return false; *v = ntohl(val); return true; } bool psdread(QIODevice* io, quint64* v) { quint64 val; quint64 read = io->read((char*)&val, 8); if (read != 8) return false; *v = ntohl(val); return true; } bool psdread_pascalstring(QIODevice* io, QString& s, int padding) { quint8 length; if (!psdread(io, &length)) { //dbgFile << 1; return false; } //dbgFile << "psdread_pascalstring length:" << length; if (length == 0) { // read the padding for (int i = 0; i < padding -1; ++i) { io->seek(io->pos() + 1); } //dbgFile << 3 << (length == 0); return (length == 0); } QByteArray chars = io->read(length); //dbgFile << "psdread_pascalstring bytes:" << QString::fromLatin1(chars, length) << chars.length(); if (chars.length() != length) { //dbgFile << 4; return false; } // read padding byte quint32 paddedLength = length + 1; if (padding > 0) { while (paddedLength % padding != 0) { if (!io->seek(io->pos() + 1)) { //dbgFile << 5; return false; } paddedLength++; } } s.append(QString::fromLatin1(chars)); ////dbgFile << "psdread_pascalstring bytes:" << s; return true; } diff --git a/krita/plugins/formats/tiff/kis_tiff_converter.cc b/krita/plugins/formats/tiff/kis_tiff_converter.cc index e3bb4c0ef53..695d9e5d324 100644 --- a/krita/plugins/formats/tiff/kis_tiff_converter.cc +++ b/krita/plugins/formats/tiff/kis_tiff_converter.cc @@ -1,632 +1,632 @@ /* * Copyright (c) 2005-2006 Cyrille Berger * * 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 * MERCHANTABILITY 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 "kis_tiff_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_tiff_reader.h" #include "kis_tiff_ycbcr_reader.h" #include "kis_buffer_stream.h" #include "kis_tiff_writer_visitor.h" namespace { QPair getColorSpaceForColorType(uint16 sampletype, uint16 color_type, uint16 color_nb_bits, TIFF *image, uint16 &nbchannels, uint16 &extrasamplescount, uint8 &destDepth) { if (color_type == PHOTOMETRIC_MINISWHITE || color_type == PHOTOMETRIC_MINISBLACK) { if (nbchannels == 0) nbchannels = 1; extrasamplescount = nbchannels - 1; // FIX the extrasamples count in case of if (color_nb_bits <= 8) { destDepth = 8; return QPair(GrayAColorModelID.id(), Integer8BitsColorDepthID.id()); } else { destDepth = 16; return QPair(GrayAColorModelID.id(), Integer16BitsColorDepthID.id()); } } else if (color_type == PHOTOMETRIC_RGB /*|| color_type == */) { if (nbchannels == 0) nbchannels = 3; extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of if (sampletype == SAMPLEFORMAT_IEEEFP) { if (color_nb_bits == 16) { destDepth = 16; return QPair(RGBAColorModelID.id(), Float16BitsColorDepthID.id()); } else if (color_nb_bits == 32) { destDepth = 32; return QPair(RGBAColorModelID.id(), Float32BitsColorDepthID.id()); } return QPair(); } else { if (color_nb_bits <= 8) { destDepth = 8; return QPair(RGBAColorModelID.id(), Integer8BitsColorDepthID.id()); } else { destDepth = 16; return QPair(RGBAColorModelID.id(), Integer16BitsColorDepthID.id()); } } } else if (color_type == PHOTOMETRIC_YCBCR) { if (nbchannels == 0) nbchannels = 3; extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of if (color_nb_bits <= 8) { destDepth = 8; return QPair(YCbCrAColorModelID.id(), Integer8BitsColorDepthID.id()); } else { destDepth = 16; return QPair(YCbCrAColorModelID.id(), Integer16BitsColorDepthID.id()); } } else if (color_type == PHOTOMETRIC_SEPARATED) { if (nbchannels == 0) nbchannels = 4; // SEPARATED is in general CMYK but not always, so we check uint16 inkset; if ((TIFFGetField(image, TIFFTAG_INKSET, &inkset) == 0)) { dbgFile << "Image does not define the inkset."; inkset = 2; } if (inkset != INKSET_CMYK) { dbgFile << "Unsupported inkset (right now, only CMYK is supported)"; char** ink_names; uint16 numberofinks; if (TIFFGetField(image, TIFFTAG_INKNAMES, &ink_names) == 1 && TIFFGetField(image, TIFFTAG_NUMBEROFINKS, &numberofinks) == 1) { dbgFile << "Inks are :"; for (uint i = 0; i < numberofinks; i++) { dbgFile << ink_names[i]; } } else { dbgFile << "inknames are not defined !"; // To be able to read stupid adobe files, if there are no information about inks and four channels, then it's a CMYK file : if (nbchannels - extrasamplescount != 4) { return QPair(); } } } if (color_nb_bits <= 8) { destDepth = 8; return QPair(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id()); } else { destDepth = 16; return QPair(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id()); } } else if (color_type == PHOTOMETRIC_CIELAB || color_type == PHOTOMETRIC_ICCLAB) { destDepth = 16; if (nbchannels == 0) nbchannels = 3; extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of return QPair(LABAColorModelID.id(), Integer16BitsColorDepthID.id()); } else if (color_type == PHOTOMETRIC_PALETTE) { destDepth = 16; if (nbchannels == 0) nbchannels = 2; extrasamplescount = nbchannels - 2; // FIX the extrasamples count in case of // <-- we will convert the index image to RGBA16 as the palette is always on 16bits colors return QPair(RGBAColorModelID.id(), Integer16BitsColorDepthID.id()); } return QPair(); } } KisTIFFConverter::KisTIFFConverter(KisDoc2 *doc) { m_doc = doc; m_job = 0; m_stop = false; } KisTIFFConverter::~KisTIFFConverter() { } KisImageBuilder_Result KisTIFFConverter::decode(const KUrl& uri) { dbgFile << "Start decoding TIFF File"; // Opent the TIFF file TIFF *image = 0; if ((image = TIFFOpen(QFile::encodeName(uri.toLocalFile()), "r")) == NULL) { dbgFile << "Could not open the file, either it does not exist, either it is not a TIFF :" << uri.toLocalFile(); return (KisImageBuilder_RESULT_BAD_FETCH); } do { dbgFile << "Read new sub-image"; KisImageBuilder_Result result = readTIFFDirectory(image); if (result != KisImageBuilder_RESULT_OK) { return result; } } while (TIFFReadDirectory(image)); // Freeing memory TIFFClose(image); return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result KisTIFFConverter::readTIFFDirectory(TIFF* image) { // Read information about the tiff uint32 width, height; if (TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width) == 0) { dbgFile << "Image does not define its width"; TIFFClose(image); return KisImageBuilder_RESULT_INVALID_ARG; } if (TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height) == 0) { dbgFile << "Image does not define its height"; TIFFClose(image); return KisImageBuilder_RESULT_INVALID_ARG; } float xres; if (TIFFGetField(image, TIFFTAG_XRESOLUTION, &xres) == 0) { dbgFile << "Image does not define x resolution"; // but we don't stop xres = 100; } float yres; if (TIFFGetField(image, TIFFTAG_YRESOLUTION, &yres) == 0) { dbgFile << "Image does not define y resolution"; // but we don't stop yres = 100; } uint16 depth; if ((TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &depth) == 0)) { dbgFile << "Image does not define its depth"; depth = 1; } uint16 sampletype; if ((TIFFGetField(image, TIFFTAG_SAMPLEFORMAT, &sampletype) == 0)) { dbgFile << "Image does not define its sample type"; sampletype = SAMPLEFORMAT_UINT; } // Determine the number of channels (useful to know if a file has an alpha or not uint16 nbchannels; if (TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &nbchannels) == 0) { dbgFile << "Image has an undefined number of samples per pixel"; nbchannels = 0; } // Get the number of extrasamples and information about them uint16 *sampleinfo = 0, extrasamplescount; if (TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extrasamplescount, &sampleinfo) == 0) { extrasamplescount = 0; } // Determine the colorspace uint16 color_type; if (TIFFGetField(image, TIFFTAG_PHOTOMETRIC, &color_type) == 0) { dbgFile << "Image has an undefined photometric interpretation"; color_type = PHOTOMETRIC_MINISWHITE; } uint8 dstDepth; QPair colorSpaceId = getColorSpaceForColorType(sampletype, color_type, depth, image, nbchannels, extrasamplescount, dstDepth); if (colorSpaceId.first.isEmpty()) { dbgFile << "Image has an unsupported colorspace :" << color_type << " for this depth :" << depth; TIFFClose(image); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } dbgFile << "Colorspace is :" << colorSpaceId.first << colorSpaceId.second << " with a depth of" << depth << " and with a nb of channels of" << nbchannels; // Read image profile dbgFile << "Reading profile"; const KoColorProfile* profile = 0; quint32 EmbedLen; quint8* EmbedBuffer; if (TIFFGetField(image, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer) == 1) { dbgFile << "Profile found"; QByteArray rawdata; rawdata.resize(EmbedLen); memcpy(rawdata.data(), EmbedBuffer, EmbedLen); profile = KoColorSpaceRegistry::instance()->createColorProfile(colorSpaceId.first, colorSpaceId.second, rawdata); } else { dbgFile << "No Profile found"; } // Check that the profile is used by the color space if (profile && !KoColorSpaceRegistry::instance()->colorSpaceFactory( KoColorSpaceRegistry::instance()->colorSpaceId( colorSpaceId.first, colorSpaceId.second))->profileIsCompatible(profile)) { warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << colorSpaceId.first << " " << colorSpaceId.second; profile = 0; } // Retrieve a pointer to the colorspace const KoColorSpace* cs = 0; if (profile && profile->isSuitableForOutput()) { dbgFile << "image has embedded profile:" << profile -> name() << ""; cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, 0); } if (cs == 0) { dbgFile << "Colorspace" << colorSpaceId.first << colorSpaceId.second << " is not available, please check your installation."; TIFFClose(image); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } // Create the cmsTransform if needed KoColorTransformation* transform = 0; if (profile && !profile->isSuitableForOutput()) { dbgFile << "The profile can't be used in krita, need conversion"; transform = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile)->createColorConverter(cs, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::BlackpointCompensation); } // Check if there is an alpha channel int8 alphapos = -1; // <- no alpha // Check which extra is alpha if any dbgFile << "There are" << nbchannels << " channels and" << extrasamplescount << " extra channels"; if (sampleinfo) { // index images don't have any sampleinfo, and therefor sampleinfo == 0 for (int i = 0; i < extrasamplescount; i ++) { dbgFile << i << "" << extrasamplescount << "" << (cs->colorChannelCount()) << nbchannels << "" << sampleinfo[i]; if (sampleinfo[i] == EXTRASAMPLE_ASSOCALPHA) { // XXX: dangelo: the color values are already multiplied with // the alpha value. This needs to be reversed later (postprocessor?) alphapos = i; } if (sampleinfo[i] == EXTRASAMPLE_UNASSALPHA) { // color values are not premultiplied with alpha, and can be used as they are. alphapos = i; } } } // Read META Information KoDocumentInfo * info = m_doc->documentInfo(); char* text; if (TIFFGetField(image, TIFFTAG_ARTIST, &text) == 1) { info->setAuthorInfo("creator", text); } if (TIFFGetField(image, TIFFTAG_DOCUMENTNAME, &text) == 1) { info->setAboutInfo("title", text); } if (TIFFGetField(image, TIFFTAG_IMAGEDESCRIPTION, &text) == 1) { info->setAboutInfo("description", text); } // Get the planar configuration uint16 planarconfig; if (TIFFGetField(image, TIFFTAG_PLANARCONFIG, &planarconfig) == 0) { dbgFile << "Plannar configuration is not define"; TIFFClose(image); return KisImageBuilder_RESULT_INVALID_ARG; } // Creating the KisImageWSP if (! m_image) { m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, "built image"); m_image->setResolution( POINT_TO_INCH(xres), POINT_TO_INCH(yres )); // It is the "invert" macro because we convert from pointer-per-inchs to points Q_CHECK_PTR(m_image); } else { if (m_image->width() < (qint32)width || m_image->height() < (qint32)height) { quint32 newwidth = (m_image->width() < (qint32)width) ? width : m_image->width(); quint32 newheight = (m_image->height() < (qint32)height) ? height : m_image->height(); m_image->resizeImage(QRect(0,0,newwidth, newheight)); } } KisPaintLayer* layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), quint8_MAX); KisTransaction("", layer -> paintDevice()); tdata_t buf = 0; tdata_t* ps_buf = 0; // used only for planar configuration separated KisBufferStreamBase* tiffstream; KisTIFFReaderBase* tiffReader = 0; quint8 poses[5]; KisTIFFPostProcessor* postprocessor = 0; // Configure poses uint8 nbcolorsamples = nbchannels - extrasamplescount; switch (color_type) { case PHOTOMETRIC_MINISWHITE: { poses[0] = 0; poses[1] = 1; postprocessor = new KisTIFFPostProcessorInvert(nbcolorsamples); } break; case PHOTOMETRIC_MINISBLACK: { poses[0] = 0; poses[1] = 1; postprocessor = new KisTIFFPostProcessor(nbcolorsamples); } break; case PHOTOMETRIC_CIELAB: { poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3; postprocessor = new KisTIFFPostProcessorICCLABtoCIELAB(nbcolorsamples); } break; case PHOTOMETRIC_ICCLAB: { poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3; postprocessor = new KisTIFFPostProcessor(nbcolorsamples); } break; case PHOTOMETRIC_RGB: { poses[0] = 2; poses[1] = 1; poses[2] = 0; poses[3] = 3; postprocessor = new KisTIFFPostProcessor(nbcolorsamples); } break; case PHOTOMETRIC_SEPARATED: { poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3; poses[4] = 4; postprocessor = new KisTIFFPostProcessor(nbcolorsamples); } break; default: break; } // Initisalize tiffReader uint16 * lineSizeCoeffs = new uint16[nbchannels]; uint16 vsubsampling = 1; uint16 hsubsampling = 1; for (uint i = 0; i < nbchannels; i++) { lineSizeCoeffs[i] = 1; } if (color_type == PHOTOMETRIC_PALETTE) { uint16 *red; // No need to free them they are free by libtiff uint16 *green; uint16 *blue; if ((TIFFGetField(image, TIFFTAG_COLORMAP, &red, &green, &blue)) == 0) { dbgFile << "Indexed image does not define a palette"; TIFFClose(image); delete [] lineSizeCoeffs; return KisImageBuilder_RESULT_INVALID_ARG; } tiffReader = new KisTIFFReaderFromPalette(layer->paintDevice(), red, green, blue, poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); } else if (color_type == PHOTOMETRIC_YCBCR) { TIFFGetFieldDefaulted(image, TIFFTAG_YCBCRSUBSAMPLING, &hsubsampling, &vsubsampling); lineSizeCoeffs[1] = hsubsampling; lineSizeCoeffs[2] = hsubsampling; uint16 position; TIFFGetFieldDefaulted(image, TIFFTAG_YCBCRPOSITIONING, &position); if (dstDepth == 8) { tiffReader = new KisTIFFYCbCrReaderTarget8Bit(layer->paintDevice(), layer->image()->width(), layer->image()->height(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor, hsubsampling, vsubsampling, (KisTIFFYCbCr::Position)position); } else if (dstDepth == 16) { tiffReader = new KisTIFFYCbCrReaderTarget16Bit(layer->paintDevice(), layer->image()->width(), layer->image()->height(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor, hsubsampling, vsubsampling, (KisTIFFYCbCr::Position)position); } } else if (dstDepth == 8) { tiffReader = new KisTIFFReaderTarget8bit(layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); } else if (dstDepth == 16) { tiffReader = new KisTIFFReaderTarget16bit(layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); } else if (dstDepth == 32) { tiffReader = new KisTIFFReaderTarget32bit(layer->paintDevice(), poses, alphapos, depth, nbcolorsamples, extrasamplescount, transform, postprocessor); } if (TIFFIsTiled(image)) { dbgFile << "tiled image"; uint32 tileWidth, tileHeight; uint32 x, y; TIFFGetField(image, TIFFTAG_TILEWIDTH, &tileWidth); TIFFGetField(image, TIFFTAG_TILELENGTH, &tileHeight); uint32 linewidth = (tileWidth * depth * nbchannels) / 8; if (planarconfig == PLANARCONFIG_CONTIG) { buf = _TIFFmalloc(TIFFTileSize(image)); if (depth < 16) { tiffstream = new KisBufferStreamContigBelow16((uint8*)buf, depth, linewidth); } else if (depth < 32) { tiffstream = new KisBufferStreamContigBelow32((uint8*)buf, depth, linewidth); } else { tiffstream = new KisBufferStreamContigAbove32((uint8*)buf, depth, linewidth); } } else { ps_buf = new tdata_t[nbchannels]; uint32 * lineSizes = new uint32[nbchannels]; uint16 baseSize = TIFFTileSize(image) / nbchannels; for (uint i = 0; i < nbchannels; i++) { ps_buf[i] = _TIFFmalloc(baseSize); lineSizes[i] = baseSize / lineSizeCoeffs[i]; } tiffstream = new KisBufferStreamSeperate((uint8**) ps_buf, nbchannels, depth, lineSizes); delete [] lineSizes; } dbgFile << linewidth << "" << nbchannels << "" << layer->paintDevice()->colorSpace()->colorChannelCount(); for (y = 0; y < height; y += tileHeight) { for (x = 0; x < width; x += tileWidth) { dbgFile << "Reading tile x =" << x << " y =" << y; if (planarconfig == PLANARCONFIG_CONTIG) { TIFFReadTile(image, buf, x, y, 0, (tsample_t) - 1); } else { for (uint i = 0; i < nbchannels; i++) { TIFFReadTile(image, ps_buf[i], x, y, 0, i); } } uint32 realTileWidth = (x + tileWidth) < width ? tileWidth : width - x; for (uint yintile = 0; y + yintile < height && yintile < tileHeight / vsubsampling;) { tiffReader->copyDataToChannels(x, y + yintile , realTileWidth, tiffstream); yintile += 1; tiffstream->moveToLine(yintile); } tiffstream->restart(); } } } else { dbgFile << "striped image"; tsize_t stripsize = TIFFStripSize(image); uint32 rowsPerStrip; TIFFGetFieldDefaulted(image, TIFFTAG_ROWSPERSTRIP, &rowsPerStrip); dbgFile << rowsPerStrip << "" << height; rowsPerStrip = qMin(rowsPerStrip, height); // when TIFFNumberOfStrips(image) == 1 it might happen that rowsPerStrip is incorrectly set if (planarconfig == PLANARCONFIG_CONTIG) { buf = _TIFFmalloc(stripsize); if (depth < 16) { tiffstream = new KisBufferStreamContigBelow16((uint8*)buf, depth, stripsize / rowsPerStrip); } else if (depth < 32) { tiffstream = new KisBufferStreamContigBelow32((uint8*)buf, depth, stripsize / rowsPerStrip); } else { tiffstream = new KisBufferStreamContigAbove32((uint8*)buf, depth, stripsize / rowsPerStrip); } } else { ps_buf = new tdata_t[nbchannels]; uint32 scanLineSize = stripsize / rowsPerStrip; dbgFile << " scanLineSize for each plan =" << scanLineSize; uint32 * lineSizes = new uint32[nbchannels]; for (uint i = 0; i < nbchannels; i++) { ps_buf[i] = _TIFFmalloc(stripsize); lineSizes[i] = scanLineSize / lineSizeCoeffs[i]; } tiffstream = new KisBufferStreamSeperate((uint8**) ps_buf, nbchannels, depth, lineSizes); delete [] lineSizes; } dbgFile << "Scanline size =" << TIFFRasterScanlineSize(image) << " / strip size =" << TIFFStripSize(image) << " / rowsPerStrip =" << rowsPerStrip << " stripsize/rowsPerStrip =" << stripsize / rowsPerStrip; uint32 y = 0; dbgFile << " NbOfStrips =" << TIFFNumberOfStrips(image) << " rowsPerStrip =" << rowsPerStrip << " stripsize =" << stripsize; for (uint32 strip = 0; y < height; strip++) { if (planarconfig == PLANARCONFIG_CONTIG) { TIFFReadEncodedStrip(image, TIFFComputeStrip(image, y, 0) , buf, (tsize_t) - 1); } else { for (uint i = 0; i < nbchannels; i++) { TIFFReadEncodedStrip(image, TIFFComputeStrip(image, y, i), ps_buf[i], (tsize_t) - 1); } } for (uint32 yinstrip = 0 ; yinstrip < rowsPerStrip && y < height ;) { uint linesreaded = tiffReader->copyDataToChannels(0, y, width, tiffstream); y += linesreaded; yinstrip += linesreaded; tiffstream->moveToLine(yinstrip); } tiffstream->restart(); } } tiffReader->finalize(); delete[] lineSizeCoeffs; delete tiffReader; delete tiffstream; if (planarconfig == PLANARCONFIG_CONTIG) { _TIFFfree(buf); } else { for (uint i = 0; i < nbchannels; i++) { _TIFFfree(ps_buf[i]); } delete[] ps_buf; } m_image->addNode(KisNodeSP(layer), m_image->rootLayer().data()); return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result KisTIFFConverter::buildImage(const KUrl& uri) { if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!KIO::NetAccess::exists(uri, KIO::NetAccess::SourceSide, qApp -> activeWindow())) { return KisImageBuilder_RESULT_NOT_EXIST; } // We're not set up to handle asynchronous loading at the moment. KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; QString tmpFile; if (KIO::NetAccess::download(uri, tmpFile, qApp -> activeWindow())) { KUrl uriTF(tmpFile); result = decode(uriTF); KIO::NetAccess::removeTempFile(tmpFile); } return result; } KisImageWSP KisTIFFConverter::image() { return m_image; } KisImageBuilder_Result KisTIFFConverter::buildFile(const KUrl& uri, KisImageWSP kisimage, KisTIFFOptions options) { dbgFile << "Start writing TIFF File"; if (!kisimage) return KisImageBuilder_RESULT_EMPTY; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_LOCAL; // Open file for writing TIFF *image; if ((image = TIFFOpen(QFile::encodeName(uri.toLocalFile()), "w")) == NULL) { dbgFile << "Could not open the file for writing" << uri.toLocalFile(); TIFFClose(image); return (KisImageBuilder_RESULT_FAILURE); } // Set the document information KoDocumentInfo * info = m_doc->documentInfo(); QString title = info->aboutInfo("title"); if (!title.isEmpty()) { - TIFFSetField(image, TIFFTAG_DOCUMENTNAME, title.toAscii().data()); + TIFFSetField(image, TIFFTAG_DOCUMENTNAME, title.toLatin1().constData()); } QString abstract = info->aboutInfo("description"); if (!abstract.isEmpty()) { - TIFFSetField(image, TIFFTAG_IMAGEDESCRIPTION, abstract.toAscii().data()); + TIFFSetField(image, TIFFTAG_IMAGEDESCRIPTION, abstract.toLatin1().constData()); } QString author = info->authorInfo("creator"); if (!author.isEmpty()) { - TIFFSetField(image, TIFFTAG_ARTIST, author.toAscii().data()); + TIFFSetField(image, TIFFTAG_ARTIST, author.toLatin1().constData()); } dbgFile << "xres: " << INCH_TO_POINT(kisimage->xRes()) << " yres: " << INCH_TO_POINT(kisimage->yRes()); TIFFSetField(image, TIFFTAG_XRESOLUTION, INCH_TO_POINT(kisimage->xRes())); // It is the "invert" macro because we convert from pointer-per-inchs to points TIFFSetField(image, TIFFTAG_YRESOLUTION, INCH_TO_POINT(kisimage->yRes())); KisGroupLayer* root = dynamic_cast(kisimage->rootLayer().data()); if (root == 0) { KIO::del(uri); TIFFClose(image); return KisImageBuilder_RESULT_FAILURE; } KisTIFFWriterVisitor* visitor = new KisTIFFWriterVisitor(image, &options); if (!visitor->visit(root)) { KIO::del(uri); TIFFClose(image); return KisImageBuilder_RESULT_FAILURE; } TIFFClose(image); return KisImageBuilder_RESULT_OK; } void KisTIFFConverter::cancel() { m_stop = true; } #include "kis_tiff_converter.moc" diff --git a/krita/plugins/paintops/libbrush/kis_gbr_brush.cpp b/krita/plugins/paintops/libbrush/kis_gbr_brush.cpp index 97011ff546f..94c754ae01a 100644 --- a/krita/plugins/paintops/libbrush/kis_gbr_brush.cpp +++ b/krita/plugins/paintops/libbrush/kis_gbr_brush.cpp @@ -1,500 +1,500 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Adrian Page * Copyright (c) 2005 Bart Coppens * Copyright (c) 2007 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * 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 * MERCHANTABILITY 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 #include // htonl #include "kis_gbr_brush.h" #include "kis_brush.h" #include "kis_qimage_mask.h" #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_image.h" struct GimpBrushV1Header { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ }; /// All fields are in MSB on disk! struct GimpBrushHeader { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ /* The following are only defined in version 2 */ quint32 magic_number; /* GIMP brush magic number */ quint32 spacing; /* brush spacing as % of width & height, 0 - 1000 */ }; // Needed, or the GIMP won't open it! quint32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0); struct KisGbrBrush::Private { QByteArray data; bool ownData; /* seems to indicate that @ref data is owned by the brush, but in Qt4.x this is already guaranteed... so in reality it seems more to indicate whether the data is loaded from file (ownData = true) or memory (ownData = false) */ bool useColorAsMask; quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 bytes; /* depth of brush in bytes */ quint32 magic_number; /* GIMP brush magic number */ }; #define DEFAULT_SPACING 0.25 KisGbrBrush::KisGbrBrush(const QString& filename) : KisBrush(filename) , d(new Private) { d->ownData = true; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); } KisGbrBrush::KisGbrBrush(const QString& filename, const QByteArray& data, qint32 & dataPos) : KisBrush(filename) , d(new Private) { d->ownData = false; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); d->data = QByteArray::fromRawData(data.data() + dataPos, data.size() - dataPos); init(); d->data.clear(); dataPos += d->header_size + (width() * height() * d->bytes); } KisGbrBrush::KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h) : KisBrush() , d(new Private) { d->ownData = true; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); initFromPaintDev(image, x, y, w, h); } KisGbrBrush::KisGbrBrush(const QImage& image, const QString& name) : KisBrush() , d(new Private) { d->ownData = false; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); setImage(image); setName(name); } KisGbrBrush::KisGbrBrush(const KisGbrBrush& rhs) : KisBrush(rhs) , d(new Private) { setName(rhs.name()); *d = *rhs.d; d->data = QByteArray(); setValid(rhs.valid()); } KisGbrBrush::~KisGbrBrush() { delete d; } bool KisGbrBrush::load() { if (d->ownData) { QFile file(filename()); file.open(QIODevice::ReadOnly); d->data = file.readAll(); file.close(); } return init(); } bool KisGbrBrush::init() { GimpBrushHeader bh; if (sizeof(GimpBrushHeader) > (uint)d->data.size()) { return false; } memcpy(&bh, d->data, sizeof(GimpBrushHeader)); bh.header_size = ntohl(bh.header_size); d->header_size = bh.header_size; bh.version = ntohl(bh.version); d->version = bh.version; bh.width = ntohl(bh.width); bh.height = ntohl(bh.height); bh.bytes = ntohl(bh.bytes); d->bytes = bh.bytes; bh.magic_number = ntohl(bh.magic_number); d->magic_number = bh.magic_number; if (bh.version == 1) { // No spacing in version 1 files so use Gimp default bh.spacing = static_cast(DEFAULT_SPACING * 100); } else { bh.spacing = ntohl(bh.spacing); if (bh.spacing > 1000) { return false; } } setSpacing(bh.spacing / 100.0); if (bh.header_size > (uint)d->data.size() || bh.header_size == 0) { return false; } QString name; if (bh.version == 1) { // Version 1 has no magic number or spacing, so the name // is at a different offset. Character encoding is undefined. const char *text = d->data.constData() + sizeof(GimpBrushV1Header); - name = QString::fromAscii(text, bh.header_size - sizeof(GimpBrushV1Header) - 1); + name = QString::fromLatin1(text, bh.header_size - sizeof(GimpBrushV1Header) - 1); } else { // ### Version = 3->cinepaint; may be float16 data! // Version >=2: UTF-8 encoding is used name = QString::fromUtf8(d->data.constData() + sizeof(GimpBrushHeader), bh.header_size - sizeof(GimpBrushHeader) - 1); } setName(name); if (bh.width == 0 || bh.height == 0) { return false; } QImage::Format imageFormat; if (bh.bytes == 1) { imageFormat = QImage::Format_Indexed8; } else { imageFormat = QImage::Format_ARGB32; } setImage(QImage(bh.width, bh.height, imageFormat)); if (m_image.isNull()) { return false; } qint32 k = bh.header_size; if (bh.bytes == 1) { QVector table; for (int i = 0; i < 256; ++i) table.append(qRgb(i, i, i)); m_image.setColorTable(table); // Grayscale if (static_cast(k + bh.width * bh.height) > d->data.size()) { return false; } setHasColor(false); for (quint32 y = 0; y < bh.height; y++) { uchar *pixel = reinterpret_cast(m_image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k++) { qint32 val = 255 - static_cast(d->data[k]); *pixel = val; ++pixel; } } } else if (bh.bytes == 4) { // RGBA if (static_cast(k + (bh.width * bh.height * 4)) > d->data.size()) { return false; } setHasColor(true); for (quint32 y = 0; y < bh.height; y++) { QRgb *pixel = reinterpret_cast(m_image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k += 4) { *pixel = qRgba(d->data[k], d->data[k+1], d->data[k+2], d->data[k+3]); ++pixel; } } } else { return false; } setWidth(m_image.width()); setHeight(m_image.height()); if (d->ownData) { d->data.resize(0); // Save some memory, we're using enough of it as it is. } if (m_image.width() == 0 || m_image.height() == 0) setValid(false); else setValid(true); return true; } bool KisGbrBrush::initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h) { // Forcefully convert to RGBA8 // XXX profile and exposure? setImage(image->convertToQImage(0, x, y, w, h, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::BlackpointCompensation)); setName(image->objectName()); setHasColor(true); return true; } bool KisGbrBrush::save() { QFile file(filename()); file.open(QIODevice::WriteOnly | QIODevice::Truncate); bool ok = saveToDevice(&file); file.close(); return ok; } bool KisGbrBrush::saveToDevice(QIODevice* dev) const { GimpBrushHeader bh; QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8 char const* name = utf8Name.data(); int nameLength = qstrlen(name); int wrote; bh.header_size = htonl(sizeof(GimpBrushHeader) + nameLength + 1); bh.version = htonl(2); // Only RGBA8 data needed atm, no cinepaint stuff bh.width = htonl(width()); bh.height = htonl(height()); // Hardcoded, 4 bytes RGBA or 1 byte GREY if (!hasColor()) bh.bytes = htonl(1); else bh.bytes = htonl(4); bh.magic_number = htonl(GimpV2BrushMagic); bh.spacing = htonl(static_cast(spacing() * 100.0)); // Write header: first bh, then the name QByteArray bytes = QByteArray::fromRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); wrote = dev->write(bytes); bytes.clear(); if (wrote == -1) return false; wrote = dev->write(name, nameLength + 1); if (wrote == -1) return false; int k = 0; if (!hasColor()) { bytes.resize(width() * height()); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { QRgb c = m_image.pixel(x, y); bytes[k++] = static_cast(255 - qRed(c)); // red == blue == green } } } else { bytes.resize(width() * height() * 4); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { // order for gimp brushes, v2 is: RGBA QRgb pixel = m_image.pixel(x, y); bytes[k++] = static_cast(qRed(pixel)); bytes[k++] = static_cast(qGreen(pixel)); bytes[k++] = static_cast(qBlue(pixel)); bytes[k++] = static_cast(qAlpha(pixel)); } } } wrote = dev->write(bytes); if (wrote == -1) return false; return true; } QImage KisGbrBrush::image() const { if (hasColor() && useColorAsMask()) { QImage image = m_image; for (int y = 0; y < image.height(); y++) { QRgb *pixel = reinterpret_cast(image.scanLine(y)); for (int x = 0; x < image.width(); x++) { QRgb c = pixel[x]; int a = qGray(c); pixel[x] = qRgba(a, a, a, qAlpha(c)); } } return image; } else { return m_image; } } enumBrushType KisGbrBrush::brushType() const { return !hasColor() || useColorAsMask() ? MASK : IMAGE; } void KisGbrBrush::setBrushType(enumBrushType type) { Q_UNUSED(type); qFatal("FATAL: protected member setBrushType has no meaning for KisGbrBrush"); } void KisGbrBrush::setImage(const QImage& image) { KisBrush::setImage(image); setValid(true); } /*QImage KisGbrBrush::outline(double pressure) { KisLayerSP layer = image(KoColorSpaceRegistry::instance()->colorSpace("RGBA",0), KisPaintInformation(pressure)); KisBoundary bounds(layer.data()); int w = maskWidth(pressure); int h = maskHeight(pressure); bounds.generateBoundary(w, h); QPixmap pix(bounds.pixmap(w, h)); QImage result; result = pix; return result; }*/ void KisGbrBrush::makeMaskImage() { if (!hasColor()) { return; } if (m_image.width() == width() && m_image.height() == height()) { int imageWidth = width(); int imageHeight = height(); QImage image(imageWidth, imageHeight, QImage::Format_Indexed8); QVector table; for (int i = 0; i < 256; ++i) { table.append(qRgb(i, i, i)); } image.setColorTable(table); for (int y = 0; y < imageHeight; y++) { QRgb *pixel = reinterpret_cast(m_image.scanLine(y)); uchar * dstPixel = image.scanLine(y); for (int x = 0; x < imageWidth; x++) { QRgb c = pixel[x]; float alpha = qAlpha(c)/255.0f; // linear interpolation with maximum gray value which is transparent in the mask //int a = (qGray(c) * alpha) + ((1.0 - alpha) * 255); // single multiplication version int a = 255 + alpha*(qGray(c) - 255); dstPixel[x] = (uchar)a; } } setImage(image); } setHasColor(false); setUseColorAsMask(false); resetBoundary(); clearScaledBrushes(); } KisGbrBrush* KisGbrBrush::clone() const { return new KisGbrBrush(*this); } void KisGbrBrush::toXML(QDomDocument& d, QDomElement& e) const { Q_UNUSED(d); e.setAttribute("type", "gbr_brush"); e.setAttribute("filename", shortFilename()); e.setAttribute("spacing", QString::number(spacing())); e.setAttribute("angle", QString::number(KisBrush::angle())); e.setAttribute("scale", QString::number(KisBrush::scale())); KisBrush::toXML(d, e); } void KisGbrBrush::setUseColorAsMask(bool useColorAsMask) { d->useColorAsMask = useColorAsMask; resetBoundary(); clearScaledBrushes(); } bool KisGbrBrush::useColorAsMask() const { return d->useColorAsMask; } QString KisGbrBrush::defaultFileExtension() const { return QString(".gbr"); } diff --git a/krita/plugins/paintops/libbrush/tests/kis_auto_brush_test.cpp b/krita/plugins/paintops/libbrush/tests/kis_auto_brush_test.cpp index 49097a9831e..568fbb2f1ad 100644 --- a/krita/plugins/paintops/libbrush/tests/kis_auto_brush_test.cpp +++ b/krita/plugins/paintops/libbrush/tests/kis_auto_brush_test.cpp @@ -1,181 +1,181 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * Copyright (c) 2009 Cyrille Berger * * 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 * MERCHANTABILITY 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 "kis_auto_brush_test.h" #include #include "testutil.h" #include "../kis_auto_brush.h" #include "kis_mask_generator.h" #include "kis_paint_device.h" #include "kis_fill_painter.h" #include #include #include #include #include #include void KisAutoBrushTest::testCreation() { KisCircleMaskGenerator circle(10, 1.0, 1.0, 1.0, 2); KisRectangleMaskGenerator rect(10, 1.0, 1.0, 1.0, 2); } void KisAutoBrushTest::testMaskGeneration() { KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 1.0, 1.0, 1.0, 2); KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisVector2D v2d = KisVector2D::Zero(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, v2d, 0, 0); // check masking an existing paint device KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->setRect(QRect(0, 0, 100, 100)); fdev->initialize(); cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100); QPoint errpoint; QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_1.png"); QImage image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_1.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Check creating a mask dab with a single color fdev = new KisFixedPaintDevice(cs); a->mask(fdev, KoColor(Qt::black, cs), 1.0, 1.0, 0.0, info); result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_3.png"); image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_3.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Check creating a mask dab with a color taken from a paint device KoColor red(Qt::red, cs); cs->setOpacity(red.data(), quint8(128), 1); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 100, 100, red.data()); fdev = new KisFixedPaintDevice(cs); a->mask(fdev, dev, 1.0, 1.0, 0.0, info); result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_4.png"); image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_4.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisAutoBrushTest::testSizeRotation() { { KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 0.5, 1.0, 1.0, 2); KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0); QCOMPARE(a->width(), 10); QCOMPARE(a->height(), 5); QCOMPARE(a->maskWidth(1.0,0.0, KisPaintInformation()), 11); QCOMPARE(a->maskHeight(1.0,0.0, KisPaintInformation()), 6); QCOMPARE(a->maskWidth(2.0,0.0, KisPaintInformation()), 21); QCOMPARE(a->maskHeight(2.0,0.0, KisPaintInformation()), 11); QCOMPARE(a->maskWidth(0.5,0.0, KisPaintInformation()), 6); QCOMPARE(a->maskHeight(0.5,0.0, KisPaintInformation()), 4); QCOMPARE(a->maskWidth(1.0,M_PI, KisPaintInformation()), 11); QCOMPARE(a->maskHeight(1.0,M_PI, KisPaintInformation()), 6); QCOMPARE(a->maskWidth(1.0,M_PI_2, KisPaintInformation()), 6); QCOMPARE(a->maskHeight(1.0,M_PI_2, KisPaintInformation()), 11); QCOMPARE(a->maskWidth(1.0,-M_PI_2, KisPaintInformation()), 6); QCOMPARE(a->maskHeight(1.0,-M_PI_2, KisPaintInformation()), 11); QCOMPARE(a->maskWidth(1.0,0.25*M_PI, KisPaintInformation()), 12); QCOMPARE(a->maskHeight(1.0,0.25*M_PI, KisPaintInformation()), 12); QCOMPARE(a->maskWidth(2.0,0.25*M_PI, KisPaintInformation()), 23); QCOMPARE(a->maskHeight(2.0,0.25*M_PI, KisPaintInformation()), 23); QCOMPARE(a->maskWidth(0.5,0.25*M_PI, KisPaintInformation()), 7); QCOMPARE(a->maskHeight(0.5,0.25*M_PI, KisPaintInformation()), 7); } } //#define SAVE_OUTPUT_IMAGES void KisAutoBrushTest::testCopyMasking() { int w = 64; int h = 64; int x = 0; int y = 0; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor black(Qt::black, cs); KoColor red(Qt::red, cs); KisPaintDeviceSP tempDev = new KisPaintDevice(cs); tempDev->fill(0, 0, w+1, h+1, red.data()); // see the TODO #ifdef SAVE_OUTPUT_IMAGES tempDev->convertToQImage(0).save("tempDev.png"); #endif KisCircleMaskGenerator * mask = new KisCircleMaskGenerator(w,1.0,0.5,0.5,2); KisAutoBrush brush(mask,0,0); KisFixedPaintDeviceSP maskDab = new KisFixedPaintDevice(cs); brush.mask(maskDab,black,1,1,0,KisPaintInformation()); // grows to w+1, h+1 maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8()); #ifdef SAVE_OUTPUT_IMAGES maskDab->convertToQImage(0,0,0,64,64).save("maskDab.png"); #endif QRect rc = tempDev->exactBounds(); //QRect maskRc = maskDab->bounds(); //TODO: if rc != maskRc, bitBltWithFixedSelection works wrong //qDebug() << rc; //qDebug() << maskRc; KisFixedPaintDeviceSP dev2fixed = new KisFixedPaintDevice(cs); dev2fixed->setRect(rc); dev2fixed->initialize(); tempDev->readBytes(dev2fixed->data(),rc); dev2fixed->convertToQImage(0).save("converted-tempDev-to-fixed.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter painter(dev); painter.setCompositeOp(COMPOSITE_COPY); painter.bltFixedWithFixedSelection(x, y, dev2fixed, maskDab, 0,0,0,0,rc.width(), rc.height() ); //painter.bitBltWithFixedSelection(x, y, tempDev, maskDab, 0, 0, 0, 0, rc.width(), rc.height()); #ifdef SAVE_OUTPUT_IMAGES dev->convertToQImage(0).save("final.png"); #endif } QTEST_KDEMAIN(KisAutoBrushTest, GUI) #include "kis_auto_brush_test.moc" diff --git a/krita/plugins/paintops/libbrush/tests/kis_brush_test.cpp b/krita/plugins/paintops/libbrush/tests/kis_brush_test.cpp index dff42e7cac4..29c64c67cd5 100644 --- a/krita/plugins/paintops/libbrush/tests/kis_brush_test.cpp +++ b/krita/plugins/paintops/libbrush/tests/kis_brush_test.cpp @@ -1,194 +1,194 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 * MERCHANTABILITY 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 "kis_brush_test.h" #include #include #include #include #include #include #include "testutil.h" #include "../kis_gbr_brush.h" #include "kis_types.h" #include "kis_paint_device.h" #include "kis_paint_information.h" #include "kis_vec.h" #include void KisBrushTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QImage image(512, 512, QImage::Format_ARGB32); KisGbrBrush a(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"); KisGbrBrush b(dev, 0, 0, 10, 10); KisGbrBrush c(image, "bla"); KisGbrBrush d(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gih"); } void KisBrushTest::testMaskGenerationNoColor() { KisGbrBrush* brush = new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"); brush->load(); Q_ASSERT(brush->valid()); const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); KisVector2D v2d = KisVector2D::Zero(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, v2d, 0, 0); // check masking an existing paint device KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->setRect(QRect(0, 0, 100, 100)); fdev->initialize(); cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100); QPoint errpoint; QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_1.png"); QImage image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_brush_test_1.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } brush->mask(fdev, 1.0, 1.0, 0.0, info); result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_2.png"); image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_brush_test_2.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisBrushTest::testMaskGenerationSingleColor() { KisGbrBrush* brush = new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"); brush->load(); Q_ASSERT(brush->valid()); const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); KisVector2D v2d = KisVector2D::Zero(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, v2d, 0, 0); // check masking an existing paint device KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->setRect(QRect(0, 0, 100, 100)); fdev->initialize(); cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100); // Check creating a mask dab with a single color fdev = new KisFixedPaintDevice(cs); brush->mask(fdev, KoColor(Qt::black, cs), 1.0, 1.0, 0.0, info); QPoint errpoint; QImage result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_3.png"); QImage image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_brush_test_3.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisBrushTest::testMaskGenerationDevColor() { KisGbrBrush* brush = new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"); brush->load(); Q_ASSERT(brush->valid()); const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); KisVector2D v2d = KisVector2D::Zero(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, v2d, 0, 0); // check masking an existing paint device KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->setRect(QRect(0, 0, 100, 100)); fdev->initialize(); cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100); // Check creating a mask dab with a color taken from a paint device KoColor red(Qt::red, cs); cs->setOpacity(red.data(), quint8(128), 1); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 100, 100, red.data()); fdev = new KisFixedPaintDevice(cs); brush->mask(fdev, dev, 1.0, 1.0, 0.0, info); QPoint errpoint; QImage result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_4.png"); QImage image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_brush_test_4.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisBrushTest::testMaskGenerationDefaultColor() { KisGbrBrush* brush = new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"); brush->load(); Q_ASSERT(brush->valid()); const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); KisVector2D v2d = KisVector2D::Zero(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, v2d, 0, 0); // check masking an existing paint device KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->setRect(QRect(0, 0, 100, 100)); fdev->initialize(); cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100); // check creating a mask dab with a default color fdev = new KisFixedPaintDevice(cs); brush->mask(fdev, 1.0, 1.0, 0.0, info); QPoint errpoint; QImage result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_3.png"); QImage image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_brush_test_5.png"); - QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toAscii()); + QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } delete brush; } void KisBrushTest::testImageGeneration() { KisGbrBrush* brush = new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"); brush->load(); Q_ASSERT(brush->valid()); const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); KisVector2D v2d = KisVector2D::Zero(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, v2d, 0, 0); KisFixedPaintDeviceSP fdev = brush->paintDevice(cs, 1.0, 0.0, info); fdev->convertToQImage(0).save("bla.png"); delete brush; } QTEST_KDEMAIN(KisBrushTest, GUI) #include "kis_brush_test.moc" diff --git a/krita/plugins/paintops/libpaintop/kis_texture_option.cpp b/krita/plugins/paintops/libpaintop/kis_texture_option.cpp index a40b0fb61de..debaba9c471 100644 --- a/krita/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/krita/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,360 +1,360 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * * 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 "kis_texture_option.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KisTextureOptionWidget : public QWidget { public: KisTextureOptionWidget(QWidget *parent = 0) : QWidget(parent) { QFormLayout *formLayout = new QFormLayout(this); chooser = new KisPatternChooser(this); chooser->setGrayscalePreview(true); chooser->setMaximumHeight(250); chooser->setCurrentItem(0, 0); formLayout->addRow(chooser); scaleSlider = new KisMultipliersDoubleSliderSpinBox(this); scaleSlider->setRange(0.0, 2.0, 2); scaleSlider->setValue(1.0); scaleSlider->addMultiplier(0.1); scaleSlider->addMultiplier(2); scaleSlider->addMultiplier(10); formLayout->addRow(i18n("Scale:"), scaleSlider); offsetSliderX = new KisSliderSpinBox(this); offsetSliderX->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); formLayout->addRow(i18n("Horizontal Offset:"), offsetSliderX); offsetSliderY = new KisSliderSpinBox(this); offsetSliderY->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); formLayout->addRow(i18n("Vertical Offset:"), offsetSliderY); strengthSlider = new KisDoubleSliderSpinBox(this); strengthSlider->setRange(0.0, 1.0, 2); strengthSlider->setValue(1); strengthSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); formLayout->addRow(i18n("Strength:"), strengthSlider); cmbCutoffPolicy = new QComboBox(this); cmbCutoffPolicy->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); QStringList cutOffPolicies; cutOffPolicies << i18n("Disregard Cutoff") << i18n("Mask Out") << i18n("Disregard Pattern"); cmbCutoffPolicy->addItems(cutOffPolicies); formLayout->addRow(i18n("Cutoff Policy"), cmbCutoffPolicy); cutoffSlider = new KisGradientSlider(this); cutoffSlider->setMinimumSize(256, 30); cutoffSlider->enableGamma(false); cutoffSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); cutoffSlider->setToolTip(i18n("When pattern texture values are outside the range specified" " by the slider, the cut-off policy will be applied.")); formLayout->addRow(i18n("Cutoff"), cutoffSlider); chkInvert = new QCheckBox("", this); chkInvert->setChecked(false); formLayout->addRow(i18n("Invert Texture:"), chkInvert); setLayout(formLayout); } KisPatternChooser *chooser; KisMultipliersDoubleSliderSpinBox *scaleSlider; KisSliderSpinBox *offsetSliderX; KisSliderSpinBox *offsetSliderY; KisDoubleSliderSpinBox *strengthSlider; KisGradientSlider *cutoffSlider; QComboBox *cmbCutoffPolicy; QCheckBox *chkInvert; }; KisTextureOption::KisTextureOption(QObject *) : KisPaintOpOption(i18n("Pattern"), KisPaintOpOption::textureCategory(), true) { setChecked(false); m_optionWidget = new KisTextureOptionWidget; m_optionWidget->hide(); setConfigurationPage(m_optionWidget); connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*))); connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->scaleSlider, SIGNAL(valueChanged(qreal)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->offsetSliderX, SIGNAL(valueChanged(int)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->offsetSliderY, SIGNAL(valueChanged(int)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->strengthSlider, SIGNAL(valueChanged(qreal)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SIGNAL(sigSettingChanged())); connect(m_optionWidget->chkInvert, SIGNAL(toggled(bool)), SIGNAL(sigSettingChanged())); resetGUI(m_optionWidget->chooser->currentResource()); } KisTextureOption::~KisTextureOption() { delete m_optionWidget; } void KisTextureOption::writeOptionSetting(KisPropertiesConfiguration* setting) const { if (!m_optionWidget->chooser->currentResource()) return; KisPattern *pattern = static_cast(m_optionWidget->chooser->currentResource()); if (!pattern) return; qreal scale = m_optionWidget->scaleSlider->value(); int offsetX = m_optionWidget->offsetSliderX->value(); int offsetY = m_optionWidget->offsetSliderY->value(); qreal strength = m_optionWidget->strengthSlider->value(); bool invert = (m_optionWidget->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/Strength", strength); setting->setProperty("Texture/Pattern/CutoffLeft", m_optionWidget->cutoffSlider->black()); setting->setProperty("Texture/Pattern/CutoffRight", m_optionWidget->cutoffSlider->white()); setting->setProperty("Texture/Pattern/CutoffPolicy", m_optionWidget->cmbCutoffPolicy->currentIndex()); setting->setProperty("Texture/Pattern/Invert", invert); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); pattern->image().save(&buffer, "PNG"); setting->setProperty("Texture/Pattern/Pattern", ba.toBase64()); setting->setProperty("Texture/Pattern/PatternFileName", pattern->filename()); setting->setProperty("Texture/Pattern/Name", pattern->name()); setting->setProperty("Texture/Pattern/Enabled", isChecked()); } void KisTextureOption::readOptionSetting(const KisPropertiesConfiguration* setting) { - QByteArray ba = QByteArray::fromBase64(setting->getString("Texture/Pattern/Pattern").toAscii()); + QByteArray ba = QByteArray::fromBase64(setting->getString("Texture/Pattern/Pattern").toLatin1()); QImage img; img.loadFromData(ba, "PNG"); QString name = setting->getString("Texture/Pattern/Name"); if (name.isEmpty()) { name = setting->getString("Texture/Pattern/FileName"); } KisPattern *pattern = 0; if (!img.isNull()) { pattern = new KisPattern(img, name); } // now check whether the pattern already occurs, if not, add it to the // resources. if (pattern) { bool found = false; foreach(KoResource *res, KisResourceServerProvider::instance()->patternServer()->resources()) { KisPattern *pat = static_cast(res); if (pat->md5() == pattern->md5()) { delete pattern; pattern = pat; found = true; break; } } if (!found) { KisResourceServerProvider::instance()->patternServer()->addResource(pattern, false); } m_optionWidget->offsetSliderX->setRange(0, pattern->image().width() / 2); m_optionWidget->offsetSliderY->setRange(0, pattern->image().height() / 2); } else { pattern = static_cast(m_optionWidget->chooser->currentResource()); } m_optionWidget->chooser->setCurrentPattern(pattern); m_optionWidget->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); m_optionWidget->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_optionWidget->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_optionWidget->strengthSlider->setValue(setting->getDouble("Texture/Pattern/Strength", 1.0)); m_optionWidget->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy")); m_optionWidget->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0)); m_optionWidget->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255)); m_optionWidget->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); setChecked(setting->getBool("Texture/Pattern/Enabled")); } void KisTextureOption::resetGUI(KoResource* res) { KisPattern *pattern = static_cast(res); m_optionWidget->offsetSliderX->setRange(0, pattern->image().width() / 2); m_optionWidget->offsetSliderY->setRange(0, pattern->image().height() / 2); } void KisTextureProperties::recalculateMask() { if (!pattern) return; m_mask = 0; QImage mask = pattern->image(); if (!qFuzzyCompare(scale, 0.0)) { QTransform tf; tf.scale(scale, scale); QRect rc = tf.mapRect(mask.rect()); mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } QRgb* pixel = reinterpret_cast( mask.bits() ); int width = mask.width(); int height = mask.height(); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); m_mask = new KisPaintDevice(cs); KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width); for (int row = 0; row < height; ++row ) { for (int col = 0; col < width; ++col ) { const QRgb currentPixel = pixel[row * width + col]; const int red = qRed(currentPixel); const int green = qGreen(currentPixel); const int blue = qBlue(currentPixel); float alpha = qAlpha(currentPixel) / 255.0; const int grayValue = (red * 11 + green * 16 + blue * 5) / 32; float maskValue = (grayValue / 255.0) * strength * alpha + (1 - alpha); if (invert) { maskValue = 1 - maskValue; } if (cutoffPolicy == 1 && (maskValue < (cutoffLeft / 255.0) || maskValue > (cutoffRight / 255.0))) { // mask out the dab if it's outside the pattern's cuttoff points maskValue = OPACITY_TRANSPARENT_U8; } cs->setOpacity(iter->rawData(), maskValue, 1); iter->nextPixel(); } iter->nextRow(); } m_maskBounds = QRect(0, 0, width, height); } void KisTextureProperties::fillProperties(const KisPropertiesConfiguration *setting) { - QByteArray ba = QByteArray::fromBase64(setting->getString("Texture/Pattern/Pattern").toAscii()); + QByteArray ba = QByteArray::fromBase64(setting->getString("Texture/Pattern/Pattern").toLatin1()); QImage img; img.loadFromData(ba, "PNG"); QString name = setting->getString("Texture/Pattern/Name"); if (name.isEmpty()) { name = setting->getString("Texture/Pattern/FileName"); } pattern = 0; if (!img.isNull()) { pattern = new KisPattern(img, name); } if (pattern) { foreach(KoResource *res, KisResourceServerProvider::instance()->patternServer()->resources()) { KisPattern *pat = static_cast(res); if (pat == pattern) { delete pattern; pattern = pat; break; } } } enabled = setting->getBool("Texture/Pattern/Enabled", false); scale = setting->getDouble("Texture/Pattern/Scale", 1.0); offsetX = setting->getInt("Texture/Pattern/OffsetX"); offsetY = setting->getInt("Texture/Pattern/OffsetY"); strength = setting->getDouble("Texture/Pattern/Strength"); invert = setting->getBool("Texture/Pattern/Invert"); cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0); cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255); cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0); recalculateMask(); } void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset) { if (!enabled) return; KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); QRect rect = dab->bounds(); int x = offset.x() % m_maskBounds.width() - offsetX; int y = offset.y() % m_maskBounds.height() - offsetY; fillDevice->setX(x); fillDevice->setY(y); KisFillPainter fillPainter(fillDevice); fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, m_mask, m_maskBounds); fillPainter.end(); quint8 *dabData = dab->data(); KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); for (int row = 0; row < rect.height(); ++row) { for (int col = 0; col < rect.width(); ++col) { if (!(cutoffPolicy == 2 && (*iter->oldRawData() < cutoffLeft || *iter->oldRawData() > cutoffRight))) { dab->colorSpace()->multiplyAlpha(dabData, *iter->oldRawData(), 1); } iter->nextPixel(); dabData += dab->pixelSize(); } iter->nextRow(); } } diff --git a/krita/plugins/paintops/mypaint/mypaint_paintop_factory.cpp b/krita/plugins/paintops/mypaint/mypaint_paintop_factory.cpp index deca4715ef8..bdfdd74c3ca 100644 --- a/krita/plugins/paintops/mypaint/mypaint_paintop_factory.cpp +++ b/krita/plugins/paintops/mypaint/mypaint_paintop_factory.cpp @@ -1,140 +1,140 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "mypaint_paintop_factory.h" #include #include #include #include #include #include "mypaint_paintop_settings_widget.h" #include "mypaint_paintop_settings.h" #include "mypaint_paintop.h" #include "mypaint_brush_resource.h" #include #include class MyPaintFactory::Private { public: KoResourceServer *brushServer; QMap brushes; }; MyPaintFactory::MyPaintFactory() : m_d( new Private ) { KGlobal::mainComponent().dirs()->addResourceType("mypaint_brushes", "data", "krita/mypaintbrushes/"); KGlobal::mainComponent().dirs()->addResourceDir("mypaint_brushes", "/usr/share/mypaint/brushes/"); m_d->brushServer = new KoResourceServer("mypaint_brushes", "*.myb"); QStringList extensionList = m_d->brushServer->extensions().split(':'); QStringList fileNames; foreach (const QString &extension, extensionList) { - fileNames += KGlobal::mainComponent().dirs()->findAllResources(m_d->brushServer->type().toAscii(), extension, + fileNames += KGlobal::mainComponent().dirs()->findAllResources(m_d->brushServer->type().toLatin1(), extension, KStandardDirs::Recursive | KStandardDirs::NoDuplicates);; } m_d->brushServer->loadResources(fileNames); foreach(MyPaintBrushResource* brush, m_d->brushServer->resources()) { QFileInfo info(brush->filename()); m_d->brushes[info.baseName()] = brush; } } MyPaintFactory::~MyPaintFactory() { delete m_d->brushServer; delete m_d; } KisPaintOp * MyPaintFactory::createOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisImageWSP image = 0) { const MyPaintSettings *mypaintSettings = dynamic_cast(settings.data()); Q_ASSERT(settings == 0 || mypaintSettings != 0); if (!settings || !mypaintSettings) { return 0; } KisPaintOp * op = new MyPaint(mypaintSettings, painter, image); Q_CHECK_PTR(op); return op; } KisPaintOpSettingsSP MyPaintFactory::settings(KisImageWSP image) { Q_UNUSED(image); return new MyPaintSettings(); } KisPaintOpSettingsWidget* MyPaintFactory::createSettingsWidget(QWidget * parent) { return new MyPaintSettingsWidget(parent); } QList MyPaintFactory::brushes() const { return m_d->brushes.values(); } MyPaintBrushResource* MyPaintFactory::brush(const QString& fileName) const { if (m_d->brushes.contains(fileName)) { return m_d->brushes[fileName]; } else { return m_d->brushes.values()[0]; } } void MyPaintFactory::processAfterLoading() { KoResourceServer* rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QStringList blackList = rserver->blackListedFiles(); QMapIterator i(m_d->brushes); while (i.hasNext()) { i.next(); if (blackList.contains(i.key())) continue; //Create a preset for every loaded brush KisPaintOpSettingsSP s = settings(0); s->setProperty("paintop", id()); s->setProperty("filename", i.key()); KisPaintOpPreset* preset = new KisPaintOpPreset(); preset->setName(i.key()); preset->setSettings(s); KoID paintOpID(id(), name()); preset->setPaintOp(paintOpID); preset->setValid(true); preset->setImage(i.value()->image()); rserver->addResource(preset, false); } } #include "mypaint_paintop_factory.moc" diff --git a/krita/ui/kis_clipboard.cc b/krita/ui/kis_clipboard.cc index 720bb34adfc..049bac070c0 100644 --- a/krita/ui/kis_clipboard.cc +++ b/krita/ui/kis_clipboard.cc @@ -1,356 +1,356 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * 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 * MERCHANTABILITY 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 "kis_clipboard.h" #include #include #include #include #include #include #include #include #include #include #include "KoColorSpace.h" #include "KoStore.h" #include #include // kritaimage #include #include #include #include // local #include "kis_factory2.h" #include "kis_config.h" KisClipboard::KisClipboard() { m_pushedClipboard = false; m_hasClip = false; // Check that we don't already have a clip ready clipboardDataChanged(); // Make sure we are notified when clipboard changes connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged())); } KisClipboard::~KisClipboard() { dbgRegistry << "deleting KisClipBoard"; } KisClipboard* KisClipboard::instance() { K_GLOBAL_STATIC(KisClipboard, s_instance); qAddPostRoutine(s_instance.destroy); // make sure we get destroyed first. return s_instance; } void KisClipboard::setClip(KisPaintDeviceSP dev, const QPoint& topLeft) { if (!dev) return; m_hasClip = true; // We'll create a store (ZIP format) in memory QBuffer buffer; QByteArray mimeType("application/x-krita-selection"); KoStore* store = KoStore::createStore(&buffer, KoStore::Write, mimeType); Q_ASSERT(store); Q_ASSERT(!store->bad()); store->disallowNameExpansion(); // Layer data if (store->open("layerdata")) { if (!dev->write(store)) { dev->disconnect(); store->close(); delete store; return; } store->close(); } // Coordinates if (store->open("topLeft")) { - store->write(QString("%1 %2").arg(topLeft.x()).arg(topLeft.y()).toAscii()); + store->write(QString("%1 %2").arg(topLeft.x()).arg(topLeft.y()).toLatin1()); store->close(); } // ColorSpace id of layer data if (store->open("colormodel")) { QString csName = dev->colorSpace()->colorModelId().id(); - store->write(csName.toAscii(), strlen(csName.toAscii())); + store->write(csName.toLatin1()); store->close(); } if (store->open("colordepth")) { QString csName = dev->colorSpace()->colorDepthId().id(); - store->write(csName.toAscii(), strlen(csName.toAscii())); + store->write(csName.toLatin1()); store->close(); } if (dev->colorSpace()->profile()) { const KoColorProfile *profile = dev->colorSpace()->profile(); KisAnnotationSP annotation; if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) { annotation = new KisAnnotation("icc", profile->name(), profile->rawData()); if (annotation) { // save layer profile if (store->open("profile.icc")) { store->write(annotation->annotation()); store->close(); } } } } delete store; QMimeData *mimeData = new QMimeData; Q_CHECK_PTR(mimeData); if (mimeData) { mimeData->setData(mimeType, buffer.buffer()); } // We also create a QImage so we can interchange with other applications QImage qimage; KisConfig cfg; const KoColorProfile *monitorProfile = cfg.displayProfile(); qimage = dev->convertToQImage(monitorProfile, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::BlackpointCompensation); if (!qimage.isNull() && mimeData) { mimeData->setImageData(qimage); } if (mimeData) { m_pushedClipboard = true; QClipboard *cb = QApplication::clipboard(); cb->setMimeData(mimeData); } } KisPaintDeviceSP KisClipboard::clip(const QPoint& topLeftHint) { bool customTopLeft = false; // will be true if pasting from a krita clip QPoint topLeft = topLeftHint; QClipboard *cb = QApplication::clipboard(); QByteArray mimeType("application/x-krita-selection"); const QMimeData *cbData = cb->mimeData(); KisPaintDeviceSP clip; bool asKrita = false; if (cbData && cbData->hasFormat(mimeType)) { asKrita = true; dbgUI << "Use clip as x-krita-selection"; QByteArray encodedData = cbData->data(mimeType); QBuffer buffer(&encodedData); KoStore* store = KoStore::createStore(&buffer, KoStore::Read, mimeType); store->disallowNameExpansion(); const KoColorProfile *profile = 0; QString csDepth, csModel; // topLeft if (store->hasFile("topLeft")) { store->open("topLeft"); QString str = store->read(store->size()); store->close(); QStringList list = str.split(' '); if (list.size() == 2) { topLeft.setX(list[0].toInt()); topLeft.setY(list[1].toInt()); customTopLeft = true; } dbgUI << str << topLeft; } // ColorSpace id of layer data if (store->hasFile("colormodel")) { store->open("colormodel"); csModel = QString(store->read(store->size())); store->close(); } if (store->hasFile("colordepth")) { store->open("colordepth"); csDepth = QString(store->read(store->size())); store->close(); } if (store->hasFile("profile.icc")) { QByteArray data; store->open("profile.icc"); data = store->read(store->size()); store->close(); profile = KoColorSpaceRegistry::instance()->createColorProfile(csModel, csDepth, data); } const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(csModel, csDepth, profile); if (!cs) { // we failed to create a colorspace, so let's try later on with the qimage part of the clip asKrita = false; } if (asKrita) { clip = new KisPaintDevice(cs); if (store->hasFile("layerdata")) { store->open("layerdata"); asKrita = clip->read(store); store->close(); } } delete store; } if (!asKrita) { dbgUI << "Use clip as QImage"; QImage qimage = cb->image(); if (qimage.isNull()) return KisPaintDeviceSP(0); KisConfig cfg; quint32 behaviour = cfg.pasteBehaviour(); if (behaviour == PASTE_ASK) { // Ask user each time. behaviour = QMessageBox::question(0, i18n("Pasting data from simple source"), i18n("The image data you are trying to paste has no color profile information.\n\nOn the web and in simple applications the data are supposed to be in sRGB color format.\nImporting as web will show it as it is supposed to look.\nMost monitors are not perfect though so if you made the image yourself\nyou might want to import it as it looked on you monitor.\n\nHow do you want to interpret these data?"), i18n("As &Web"), i18n("As on &Monitor")); } const KoColorSpace * cs; const KoColorProfile *profile = 0; if (behaviour == PASTE_ASSUME_MONITOR) profile = cfg.displayProfile(); cs = KoColorSpaceRegistry::instance()->rgb8(profile); if (!cs) { cs = KoColorSpaceRegistry::instance()->rgb8(); profile = cs->profile(); } clip = new KisPaintDevice(cs); Q_CHECK_PTR(clip); clip->convertFromQImage(qimage, profile); } if (!customTopLeft) { QRect exactBounds = clip->exactBounds(); topLeft -= exactBounds.topLeft() / 2; } clip->setX(topLeft.x()); clip->setY(topLeft.y()); return clip; } void KisClipboard::clipboardDataChanged() { if (!m_pushedClipboard) { m_hasClip = false; QClipboard *cb = QApplication::clipboard(); QImage qimage = cb->image(); const QMimeData *cbData = cb->mimeData(); QByteArray mimeType("application/x-krita-selection"); if (cbData && cbData->hasFormat(mimeType)) m_hasClip = true; if (!qimage.isNull()) m_hasClip = true; } m_pushedClipboard = false; } bool KisClipboard::hasClip() { return m_hasClip; } QSize KisClipboard::clipSize() { QClipboard *cb = QApplication::clipboard(); QByteArray mimeType("application/x-krita-selection"); const QMimeData *cbData = cb->mimeData(); KisPaintDeviceSP clip; if (cbData && cbData->hasFormat(mimeType)) { QByteArray encodedData = cbData->data(mimeType); QBuffer buffer(&encodedData); KoStore* store = KoStore::createStore(&buffer, KoStore::Read, mimeType); const KoColorProfile *profile = 0; QString csDepth, csModel; // ColorSpace id of layer data if (store->hasFile("colormodel")) { store->open("colormodel"); csModel = QString(store->read(store->size())); store->close(); } if (store->hasFile("colordepth")) { store->open("colordepth"); csDepth = QString(store->read(store->size())); store->close(); } if (store->hasFile("profile.icc")) { QByteArray data; store->open("profile.icc"); data = store->read(store->size()); store->close(); profile = KoColorSpaceRegistry::instance()->createColorProfile(csModel, csDepth, data); } const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(csModel, csDepth, profile); clip = new KisPaintDevice(cs); if (store->hasFile("layerdata")) { store->open("layerdata"); clip->read(store); store->close(); } delete store; return clip->exactBounds().size(); } else { QImage qimage = cb->image(); return qimage.size(); } } #include "kis_clipboard.moc" diff --git a/krita/ui/kis_png_converter.cpp b/krita/ui/kis_png_converter.cpp index 7d9ee486255..a88e5e2def8 100644 --- a/krita/ui/kis_png_converter.cpp +++ b/krita/ui/kis_png_converter.cpp @@ -1,1171 +1,1171 @@ /* * Copyright (c) 2005-2007 Cyrille Berger * * 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 * MERCHANTABILITY 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 "kis_png_converter.h" // A big thank to Glenn Randers-Pehrson for his wonderful // documentation of libpng available at // http://www.libpng.org/pub/png/libpng-1.2.5-manual.html #ifndef PNG_MAX_UINT // Removed in libpng 1.4 #define PNG_MAX_UINT PNG_UINT_31_MAX #endif #include // WORDS_BIGENDIAN #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" namespace { const quint8 PIXEL_BLUE = 0; const quint8 PIXEL_GREEN = 1; const quint8 PIXEL_RED = 2; const quint8 PIXEL_ALPHA = 3; int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha) { QString id = cs->id(); if (id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16") { return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if (id == "RGBA" || id == "RGBA16") { return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } // KMessageBox::error(0, i18n("Cannot export images in %1.\n", cs->name())) ; return -1; } QPair getColorSpaceForColorType(int color_type, int color_nb_bits) { QPair r; if (color_type == PNG_COLOR_TYPE_PALETTE) { r.first = RGBAColorModelID.id(); r.second = Integer8BitsColorDepthID.id(); } else { if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { r.first = GrayAColorModelID.id(); } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) { r.first = RGBAColorModelID.id(); } if (color_nb_bits == 16) { r.second = Integer16BitsColorDepthID.id(); } else if (color_nb_bits <= 8) { r.second = Integer8BitsColorDepthID.id(); } } return r; } void fillText(png_text* p_text, const char* key, QString& text) { p_text->compression = PNG_TEXT_COMPRESSION_zTXt; p_text->key = const_cast(key); char* textc = new char[text.length()+1]; - strcpy(textc, text.toAscii()); + strcpy(textc, text.toLatin1()); p_text->text = textc; p_text->text_length = text.length() + 1; } long formatStringList(char *string, const size_t length, const char *format, va_list operands) { int n = vsnprintf(string, length, format, operands); if (n < 0) string[length-1] = '\0'; return((long) n); } long formatString(char *string, const size_t length, const char *format, ...) { long n; va_list operands; va_start(operands, format); n = (long) formatStringList(string, length, format, operands); va_end(operands); return(n); } void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data) { png_textp text; png_uint_32 allocated_length, description_length; const uchar hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; dbgFile << "Writing Raw profile: type=" << profile_type << ", length=" << profile_data.length() << endl; text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text)); description_length = profile_type.length(); allocated_length = (png_uint_32)(profile_data.length() * 2 + (profile_data.length() >> 5) + 20 + description_length); text[0].text = (png_charp) png_malloc(ping, allocated_length); QString key = "Raw profile type " + profile_type.toLatin1(); QByteArray keyData = key.toLatin1(); text[0].key = keyData.data(); uchar* sp = (uchar*)profile_data.data(); png_charp dp = text[0].text; *dp++ = '\n'; - memcpy(dp, (const char *) profile_type.toLatin1().data(), profile_type.length()); + memcpy(dp, profile_type.toLatin1().constData(), profile_type.length()); dp += description_length; *dp++ = '\n'; formatString(dp, allocated_length - strlen(text[0].text), "%8lu ", profile_data.length()); dp += 8; for (long i = 0; i < (long) profile_data.length(); i++) { if (i % 36 == 0) *dp++ = '\n'; *(dp++) = (char) hex[((*sp >> 4) & 0x0f)]; *(dp++) = (char) hex[((*sp++) & 0x0f)]; } *dp++ = '\n'; *dp = '\0'; text[0].text_length = (png_size_t)(dp - text[0].text); text[0].compression = -1; if (text[0].text_length <= allocated_length) png_set_text(ping, ping_info, text, 1); png_free(ping, text[0].text); png_free(ping, text); } QByteArray png_read_raw_profile(png_textp text) { QByteArray profile; static unsigned char unhex[103] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15 }; png_charp sp = text[0].text + 1; /* look for newline */ while (*sp != '\n') sp++; /* look for length */ while (*sp == '\0' || *sp == ' ' || *sp == '\n') sp++; png_uint_32 length = (png_uint_32) atol(sp); while (*sp != ' ' && *sp != '\n') sp++; if (length == 0) { return profile; } profile.resize(length); /* copy profile, skipping white space and column 1 "=" signs */ unsigned char *dp = (unsigned char*)profile.data(); png_uint_32 nibbles = length * 2; for (png_uint_32 i = 0; i < nibbles; i++) { while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f') { if (*sp == '\0') { return QByteArray(); } sp++; } if (i % 2 == 0) *dp = (unsigned char)(16 * unhex[(int) *sp++]); else (*dp++) += unhex[(int) *sp++]; } return profile; } void decode_meta_data(png_textp text, KisMetaData::Store* store, QString type, int headerSize) { dbgFile << "Decoding " << type << " " << text[0].key; KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value(type); Q_ASSERT(exifIO); QByteArray rawProfile = png_read_raw_profile(text); if (headerSize > 0) { rawProfile.remove(0, headerSize); } if (rawProfile.size() > 0) { QBuffer buffer; buffer.setData(rawProfile); exifIO->loadFrom(store, &buffer); } else { dbgFile << "Decoding failed"; } } } KisPNGConverter::KisPNGConverter(KisDoc2 *doc) { // Q_ASSERT(doc); // Q_ASSERT(adapter); m_doc = doc; m_stop = false; m_max_row = 0; m_image = 0; } KisPNGConverter::~KisPNGConverter() { } class KisPNGReadStream { public: KisPNGReadStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) { } int nextValue() { if (m_posinc == 0) { m_posinc = 8; m_buf++; } m_posinc -= m_depth; return (((*m_buf) >> (m_posinc)) & ((1 << m_depth) - 1)); } private: quint32 m_posinc, m_depth; quint8* m_buf; }; class KisPNGWriteStream { public: KisPNGWriteStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) { *m_buf = 0; } void setNextValue(int v) { if (m_posinc == 0) { m_posinc = 8; m_buf++; *m_buf = 0; } m_posinc -= m_depth; *m_buf = (v << m_posinc) | *m_buf; } private: quint32 m_posinc, m_depth; quint8* m_buf; }; class KisPNGReaderAbstract { public: KisPNGReaderAbstract(png_structp _png_ptr, int _width, int _height) : png_ptr(_png_ptr), width(_width), height(_height) {} virtual ~KisPNGReaderAbstract() {} virtual png_bytep readLine() = 0; protected: png_structp png_ptr; int width, height; }; class KisPNGReaderLineByLine : public KisPNGReaderAbstract { public: KisPNGReaderLineByLine(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height) { png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); row_pointer = new png_byte[rowbytes]; } virtual ~KisPNGReaderLineByLine() { delete[] row_pointer; } virtual png_bytep readLine() { png_read_row(png_ptr, row_pointer, NULL); return row_pointer; } private: png_bytep row_pointer; }; class KisPNGReaderFullImage : public KisPNGReaderAbstract { public: KisPNGReaderFullImage(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height), y(0) { row_pointers = new png_bytep[height]; png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); for (int i = 0; i < height; i++) { row_pointers[i] = new png_byte[rowbytes]; } png_read_image(png_ptr, row_pointers); } virtual ~KisPNGReaderFullImage() { for (int i = 0; i < height; i++) { delete[] row_pointers[i]; } delete[] row_pointers; } virtual png_bytep readLine() { return row_pointers[y++]; } private: png_bytepp row_pointers; int y; }; static void _read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr); while (length) { int nr = in->read((char*)data, length); if (nr <= 0) { png_error(png_ptr, "Read Error"); return; } length -= nr; } } static void _write_fn(png_structp png_ptr, png_bytep data, png_size_t length) { QIODevice* out = (QIODevice*)png_get_io_ptr(png_ptr); uint nr = out->write((char*)data, length); if (nr != length) { png_error(png_ptr, "Write Error"); return; } } static void _flush_fn(png_structp png_ptr) { Q_UNUSED(png_ptr); } KisImageBuilder_Result KisPNGConverter::buildImage(QIODevice* iod) { dbgFile << "Start decoding PNG File"; if (!iod->open(QIODevice::ReadOnly)) { dbgFile << "Failed to open PNG File"; return (KisImageBuilder_RESULT_FAILURE); } png_byte signature[8]; iod->peek((char*)signature, 8); #if PNG_LIBPNG_VER < 10400 if (!png_check_sig(signature, 8)) { #else if (png_sig_cmp(signature, 0, 8) != 0) { #endif iod->close(); return (KisImageBuilder_RESULT_BAD_FETCH); } // Initialize the internal structures png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png_ptr) { iod->close(); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); iod->close(); return (KisImageBuilder_RESULT_FAILURE); } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); iod->close(); return (KisImageBuilder_RESULT_FAILURE); } // Catch errors if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); iod->close(); return (KisImageBuilder_RESULT_FAILURE); } // Initialize the special png_set_read_fn(png_ptr, iod, _read_fn); // read all PNG info up to image data png_read_info(png_ptr, info_ptr); if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8) { png_set_expand(png_ptr); } if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE && png_get_bit_depth(png_ptr, info_ptr) < 8) { png_set_packing(png_ptr); } if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE && (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) { png_set_expand(png_ptr); } png_read_update_info(png_ptr, info_ptr); // Read information about the png png_uint_32 width, height; int color_nb_bits, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, NULL, NULL); dbgFile << "width = " << width << " height = " << height << " color_nb_bits = " << color_nb_bits << " color_type = " << color_type << " interlace_type = " << interlace_type << endl; // swap byteorder on little endian machines. #ifndef WORDS_BIGENDIAN if (color_nb_bits > 8) png_set_swap(png_ptr); #endif // Determine the colorspace QPair csName = getColorSpaceForColorType(color_type, color_nb_bits); if (csName.first.isEmpty()) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); iod->close(); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA); // Read image profile png_charp profile_name; #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 png_bytep profile_data; #else png_charp profile_data; #endif int compression_type; png_uint_32 proflen; const KoColorProfile* profile = 0; if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) { QByteArray profile_rawdata; // XXX: Hardcoded for icc type -- is that correct for us? profile_rawdata.resize(proflen); memcpy(profile_rawdata.data(), profile_data, proflen); profile = KoColorSpaceRegistry::instance()->createColorProfile(csName.first, csName.second, profile_rawdata); Q_CHECK_PTR(profile); if (profile) { // dbgFile << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo(); if (!profile->isSuitableForOutput()) { dbgFile << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile"; // TODO: in ko2 popup a selection menu to inform the user } } } else { dbgFile << "no embedded profile, will use the default profile"; } // Check that the profile is used by the color space if (profile && !KoColorSpaceRegistry::instance()->colorSpaceFactory( KoColorSpaceRegistry::instance()->colorSpaceId( csName.first, csName.second))->profileIsCompatible(profile)) { warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << csName.first << " " << csName.second; profile = 0; } // Retrieve a pointer to the colorspace const KoColorSpace* cs; if (profile && profile->isSuitableForOutput()) { dbgFile << "image has embedded profile: " << profile -> name() << "\n"; cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile); } else cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, 0); if (cs == 0) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } //TODO: two fixes : one tell the user about the problem and ask for a solution, and two once the kocolorspace include KoColorTransformation, use that instead of hacking a lcms transformation // Create the cmsTransform if needed KoColorTransformation* transform = 0; if (profile && !profile->isSuitableForOutput()) { transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::BlackpointCompensation); } // Creating the KisImageWSP if (m_image == 0) { m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, "built image"); Q_CHECK_PTR(m_image); } // Read resolution int unit_type; png_uint_32 x_resolution, y_resolution; png_get_pHYs(png_ptr, info_ptr, &x_resolution, &y_resolution, &unit_type); if (unit_type == PNG_RESOLUTION_METER) { m_image->setResolution((double) POINT_TO_CM(x_resolution) / 100.0, (double) POINT_TO_CM(y_resolution) / 100.0); // It is the "invert" macro because we convert from pointer-per-inchs to points } double coeff = quint8_MAX / (double)(pow((double)2, color_nb_bits) - 1); KisPaintLayerSP layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), UCHAR_MAX); KisTransaction("", layer -> paintDevice()); // Read comments/texts... png_text* text_ptr; int num_comments; png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments); if (m_doc) { KoDocumentInfo * info = m_doc->documentInfo(); dbgFile << "There are " << num_comments << " comments in the text"; for (int i = 0; i < num_comments; i++) { QString key = text_ptr[i].key; dbgFile << "key is |" << text_ptr[i].key << "| containing " << text_ptr[i].text << " " << (key == "Raw profile type exif "); if (key == "title") { info->setAboutInfo("title", text_ptr[i].text); } else if (key == "abstract") { info->setAboutInfo("description", text_ptr[i].text); } else if (key == "author") { info->setAuthorInfo("creator", text_ptr[i].text); } else if (key.contains("Raw profile type exif")) { decode_meta_data(text_ptr + i, layer->metaData(), "exif", 6); } else if (key.contains("Raw profile type iptc")) { decode_meta_data(text_ptr + i, layer->metaData(), "iptc", 14); } else if (key.contains("Raw profile type xmp")) { decode_meta_data(text_ptr + i, layer->metaData(), "xmp", 0); } else if (key == "version") { m_image->addAnnotation(new KisAnnotation("kpp_version", "version", QByteArray(text_ptr[i].text))); } else if (key == "preset") { m_image->addAnnotation(new KisAnnotation("kpp_preset", "preset", QByteArray(text_ptr[i].text))); } } } // Read image data KisPNGReaderAbstract* reader = 0; try { if (interlace_type == PNG_INTERLACE_ADAM7) { reader = new KisPNGReaderFullImage(png_ptr, info_ptr, width, height); } else { reader = new KisPNGReaderLineByLine(png_ptr, info_ptr, width, height); } } catch (std::bad_alloc& e) { // new png_byte[] may raise such an exception if the image // is invalid / to large. dbgFile << "bad alloc: " << e.what(); // Free only the already allocated png_byte instances. png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (KisImageBuilder_RESULT_FAILURE); } // Read the palette if the file is indexed png_colorp palette ; int num_palette; if (color_type == PNG_COLOR_TYPE_PALETTE) { png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); } // Read the transparency palette quint8 palette_alpha[256]; memset(palette_alpha, 255, 256); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { if (color_type == PNG_COLOR_TYPE_PALETTE) { png_bytep alpha_ptr; int num_alpha; png_get_tRNS(png_ptr, info_ptr, &alpha_ptr, &num_alpha, NULL); for (int i = 0; i < num_alpha; ++i) { palette_alpha[i] = alpha_ptr[i]; } } } for (png_uint_32 y = 0; y < height; y++) { KisHLineIteratorSP it = layer -> paintDevice() -> createHLineIteratorNG(0, y, width); png_bytep row_pointer = reader->readLine(); switch (color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (color_nb_bits == 16) { quint16 *src = reinterpret_cast(row_pointer); do { quint16 *d = reinterpret_cast(it->rawData()); d[0] = *(src++); if (transform) transform->transform(reinterpret_cast(d), reinterpret_cast(d), 1); if (hasalpha) { d[1] = *(src++); } else { d[1] = quint16_MAX; } } while (it->nextPixel()); } else { KisPNGReadStream stream(row_pointer, color_nb_bits); do { quint8 *d = it->rawData(); d[0] = (quint8)(stream.nextValue() * coeff); if (transform) transform->transform(d, d, 1); if (hasalpha) { d[1] = (quint8)(stream.nextValue() * coeff); } else { d[1] = UCHAR_MAX; } } while (it->nextPixel()); } #ifdef __GNUC__ #warning "KisPngCoverter::buildImage(QIODevice* iod): FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits" #endif break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if (color_nb_bits == 16) { quint16 *src = reinterpret_cast(row_pointer); do { quint16 *d = reinterpret_cast(it->rawData()); d[2] = *(src++); d[1] = *(src++); d[0] = *(src++); if (transform) transform->transform(reinterpret_cast(d), reinterpret_cast(d), 1); if (hasalpha) d[3] = *(src++); else d[3] = quint16_MAX; } while (it->nextPixel()); } else { KisPNGReadStream stream(row_pointer, color_nb_bits); do { quint8 *d = it->rawData(); d[2] = (quint8)(stream.nextValue() * coeff); d[1] = (quint8)(stream.nextValue() * coeff); d[0] = (quint8)(stream.nextValue() * coeff); if (transform) transform->transform(d, d, 1); if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff); else d[3] = UCHAR_MAX; } while (it->nextPixel()); } break; case PNG_COLOR_TYPE_PALETTE: { KisPNGReadStream stream(row_pointer, color_nb_bits); do { quint8 *d = it->rawData(); quint8 index = stream.nextValue(); quint8 alpha = palette_alpha[ index ]; if (alpha == 0) { memset(d, 0, 4); } else { png_color c = palette[ index ]; d[2] = c.red; d[1] = c.green; d[0] = c.blue; d[3] = alpha; } } while (it->nextPixel()); } break; default: return KisImageBuilder_RESULT_UNSUPPORTED; } } m_image->addNode(layer.data(), m_image->rootLayer().data()); png_read_end(png_ptr, end_info); iod->close(); // Freeing memory png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); delete reader; return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result KisPNGConverter::buildImage(const KUrl& uri) { dbgFile << QFile::encodeName(uri.path()) << " " << uri.path() << " " << uri; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!KIO::NetAccess::exists(uri, KIO::NetAccess::SourceSide, qApp -> activeWindow())) { return KisImageBuilder_RESULT_NOT_EXIST; } // We're not set up to handle asynchronous loading at the moment. KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; QString tmpFile; if (KIO::NetAccess::download(uri, tmpFile, qApp -> activeWindow())) { KUrl uriTF; uriTF.setPath(tmpFile); // open the file dbgFile << QFile::encodeName(uriTF.toLocalFile()) << " " << uriTF.toLocalFile() << " " << uriTF; // QFile *fp = new QFile(QFile::encodeName(uriTF.path()) ); QFile *fp = new QFile(uriTF.toLocalFile()); if (fp->exists()) { result = buildImage(fp); } else { result = (KisImageBuilder_RESULT_NOT_EXIST); } KIO::NetAccess::removeTempFile(tmpFile); } return result; } KisImageWSP KisPNGConverter::image() { return m_image; } KisImageBuilder_Result KisPNGConverter::buildFile(const KUrl& uri, KisImageWSP image, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData) { dbgFile << "Start writing PNG File " << uri; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_LOCAL; // Open a QIODevice for writing QFile *fp = new QFile(uri.toLocalFile()); KisImageBuilder_Result result = buildFile(fp, image, device, annotationsStart, annotationsEnd, options, metaData); delete fp; return result; // TODO: if failure do KIO::del(uri); // async } KisImageBuilder_Result KisPNGConverter::buildFile(QIODevice* iodevice, KisImageWSP image, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData) { if (!iodevice->open(QIODevice::WriteOnly)) { dbgFile << "Failed to open PNG File for writing"; return (KisImageBuilder_RESULT_FAILURE); } if (!device) return KisImageBuilder_RESULT_INVALID_ARG; if (!image) return KisImageBuilder_RESULT_EMPTY; // Setup the writing callback of libpng int height = image->height(); int width = image->width(); if (!options.alpha) { KisPaintDeviceSP tmp = new KisPaintDevice(device->colorSpace()); KoColor c(options.transparencyFillColor, device->colorSpace()); tmp->fill(QRect(0, 0, width, height), c); KisPainter gc(tmp); gc.bitBlt(QPoint(0, 0), device, QRect(0, 0, width, height)); gc.end(); device = tmp; } // Initialize structures png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png_ptr) { return (KisImageBuilder_RESULT_FAILURE); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return (KisImageBuilder_RESULT_FAILURE); } // If an error occurs during writing, libpng will jump here if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return (KisImageBuilder_RESULT_FAILURE); } // Initialize the writing // png_init_io(png_ptr, fp); // Setup the progress function #ifdef __GNUC__ #warning "KisPngCoverter::buildFile: Implement progress updating -- png_set_write_status_fn(png_ptr, progress);" #endif // setProgressTotalSteps(100/*height*/); /* set the zlib compression level */ png_set_compression_level(png_ptr, options.compression); png_set_write_fn(png_ptr, (void*)iodevice, _write_fn, _flush_fn); /* set other zlib parameters */ png_set_compression_mem_level(png_ptr, 8); png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); png_set_compression_buffer_size(png_ptr, 8192); int color_nb_bits = 8 * device->pixelSize() / device->channelCount(); int color_type = getColorTypeforColorSpace(device->colorSpace(), options.alpha); if (color_type == -1) { return KisImageBuilder_RESULT_UNSUPPORTED; } // Try to compute a table of color if the colorspace is RGB8f png_colorp palette = 0; int num_palette = 0; if (!options.alpha && options.tryToSaveAsIndexed && KoID(device->colorSpace()->id()) == KoID("RGBA")) { // png doesn't handle indexed images and alpha, and only have indexed for RGB8 palette = new png_color[255]; KisRectConstIteratorSP it = device->createRectConstIteratorNG(0, 0, image->width(), image->height()); bool toomuchcolor = false; do { const quint8* c = it->oldRawData(); bool findit = false; for (int i = 0; i < num_palette; i++) { if (palette[i].red == c[2] && palette[i].green == c[1] && palette[i].blue == c[0]) { findit = true; break; } } if (!findit) { if (num_palette == 255) { toomuchcolor = true; break; } palette[num_palette].red = c[2]; palette[num_palette].green = c[1]; palette[num_palette].blue = c[0]; num_palette++; } } while (it->nextPixel()); if (!toomuchcolor) { dbgFile << "Found a palette of " << num_palette << " colors"; color_type = PNG_COLOR_TYPE_PALETTE; if (num_palette <= 2) { color_nb_bits = 1; } else if (num_palette <= 4) { color_nb_bits = 2; } else if (num_palette <= 16) { color_nb_bits = 4; } else { color_nb_bits = 8; } } else { delete [] palette; } } int interlacetype = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR(png_ptr, info_ptr, width, height, color_nb_bits, color_type, interlacetype, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // set sRGB only if the profile is sRGB -- http://www.w3.org/TR/PNG/#11sRGB says sRGB and iCCP should not both be present bool sRGB = device->colorSpace()->profile()->name().toLower().contains("srgb"); if (!options.saveSRGBProfile && sRGB) { png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); } // set the palette if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_PLTE(png_ptr, info_ptr, palette, num_palette); } // Save annotation vKisAnnotationSP_it it = annotationsStart; while (it != annotationsEnd) { if (!(*it) || (*it)->type().isEmpty()) { dbgFile << "Warning: empty annotation"; it++; continue; } dbgFile << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size(); if ((*it) -> type().startsWith(QString("krita_attribute:"))) { // // Attribute #ifdef __GNUC__ #warning "it should be possible to save krita_attributes in the \"CHUNKs\"" #endif dbgFile << "cannot save this annotation : " << (*it) -> type(); } else if ((*it)->type() == "kpp_version" || (*it)->type() == "kpp_preset" ) { dbgFile << "Saving preset information " << (*it)->description(); png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32) sizeof(png_text)); QByteArray keyData = (*it)->description().toLatin1(); text[0].key = keyData.data(); text[0].text = (*it)->annotation().data(); text[0].text_length = (*it)->annotation().size(); text[0].compression = -1; png_set_text(png_ptr, info_ptr, text, 1); png_free(png_ptr, text); } it++; } // Save the color profile const KoColorProfile* colorProfile = device->colorSpace()->profile(); QByteArray colorProfileData = colorProfile->rawData(); if (!sRGB || options.saveSRGBProfile) { #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 png_set_iCCP(png_ptr, info_ptr, "icc", PNG_COMPRESSION_TYPE_BASE, (const png_bytep)colorProfileData.data(), colorProfileData . size()); #else png_set_iCCP(png_ptr, info_ptr, "icc", PNG_COMPRESSION_TYPE_BASE, (char*)colorProfileData.data(), colorProfileData . size()); #endif } // read comments from the document information if (m_doc) { png_text texts[3]; int nbtexts = 0; KoDocumentInfo * info = m_doc->documentInfo(); QString title = info->aboutInfo("creator"); if (!title.isEmpty()) { fillText(texts + nbtexts, "title", title); nbtexts++; } QString abstract = info->aboutInfo("description"); if (!abstract.isEmpty()) { fillText(texts + nbtexts, "abstract", abstract); nbtexts++; } QString author = info->authorInfo("creator"); if (!author.isEmpty()) { fillText(texts + nbtexts, "author", author); nbtexts++; } png_set_text(png_ptr, info_ptr, texts, nbtexts); } // Save metadata following imagemagick way // Save exif if (metaData && !metaData->empty()) { if (options.exif) { dbgFile << "Trying to save exif information"; KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); Q_ASSERT(exifIO); QBuffer buffer; exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); writeRawProfile(png_ptr, info_ptr, "exif", buffer.data()); } // Save IPTC if (options.iptc) { dbgFile << "Trying to save exif information"; KisMetaData::IOBackend* iptcIO = KisMetaData::IOBackendRegistry::instance()->value("iptc"); Q_ASSERT(iptcIO); QBuffer buffer; iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); dbgFile << "IPTC information size is" << buffer.data().size(); writeRawProfile(png_ptr, info_ptr, "iptc", buffer.data()); } // Save XMP if (options.xmp) #if 1 // TODO enable when XMP support is finiehsed { dbgFile << "Trying to save XMP information"; KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); Q_ASSERT(xmpIO); QBuffer buffer; xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::NoHeader); dbgFile << "XMP information size is" << buffer.data().size(); writeRawProfile(png_ptr, info_ptr, "xmp", buffer.data()); } #endif } #if 0 // Unimplemented? // Save resolution int unit_type; png_uint_32 x_resolution, y_resolution; #endif png_set_pHYs(png_ptr, info_ptr, CM_TO_POINT(image->xRes()) * 100.0, CM_TO_POINT(image->yRes()) * 100.0, PNG_RESOLUTION_METER); // It is the "invert" macro because we convert from pointer-per-inchs to points // Save the information to the file png_write_info(png_ptr, info_ptr); png_write_flush(png_ptr); // swap byteorder on little endian machines. #ifndef WORDS_BIGENDIAN if (color_nb_bits > 8) png_set_swap(png_ptr); #endif // Write the PNG // png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); // Fill the data structure png_byte** row_pointers = new png_byte*[height]; for (int y = 0; y < height; y++) { KisHLineConstIteratorSP it = device->createHLineConstIteratorNG(0, y, width); row_pointers[y] = new png_byte[width*device->pixelSize()]; switch (color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (color_nb_bits == 16) { quint16 *dst = reinterpret_cast(row_pointers[y]); do { const quint16 *d = reinterpret_cast(it->oldRawData()); *(dst++) = d[0]; if (options.alpha) *(dst++) = d[1]; } while (it->nextPixel()); } else { quint8 *dst = row_pointers[y]; do { const quint8 *d = it->oldRawData(); *(dst++) = d[0]; if (options.alpha) *(dst++) = d[1]; } while (it->nextPixel()); } break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if (color_nb_bits == 16) { quint16 *dst = reinterpret_cast(row_pointers[y]); do { const quint16 *d = reinterpret_cast(it->oldRawData()); *(dst++) = d[2]; *(dst++) = d[1]; *(dst++) = d[0]; if (options.alpha) *(dst++) = d[3]; } while (it->nextPixel()); } else { quint8 *dst = row_pointers[y]; do { const quint8 *d = it->oldRawData(); *(dst++) = d[2]; *(dst++) = d[1]; *(dst++) = d[0]; if (options.alpha) *(dst++) = d[3]; } while (it->nextPixel()); } break; case PNG_COLOR_TYPE_PALETTE: { quint8 *dst = row_pointers[y]; KisPNGWriteStream writestream(dst, color_nb_bits); do { const quint8 *d = it->oldRawData(); int i; for (i = 0; i < num_palette; i++) { if (palette[i].red == d[2] && palette[i].green == d[1] && palette[i].blue == d[0]) { break; } while (it->nextPixel()); } writestream.setNextValue(i); } while (it->nextPixel()); } break; default: delete[] row_pointers; return KisImageBuilder_RESULT_UNSUPPORTED; } } png_write_image(png_ptr, row_pointers); // Writing is over png_write_end(png_ptr, info_ptr); // Free memory png_destroy_write_struct(&png_ptr, &info_ptr); for (int y = 0; y < height; y++) { delete[] row_pointers[y]; } delete[] row_pointers; if (color_type == PNG_COLOR_TYPE_PALETTE) { delete [] palette; } iodevice->close(); return KisImageBuilder_RESULT_OK; } void KisPNGConverter::cancel() { m_stop = true; } void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass) { if (png_ptr == NULL || row_number > PNG_MAX_UINT || pass > 7) return; // setProgress(row_number); } #include "kis_png_converter.moc" diff --git a/krita/ui/kis_workspace_resource.cpp b/krita/ui/kis_workspace_resource.cpp index 71e4a6fbf4f..31419caab7e 100644 --- a/krita/ui/kis_workspace_resource.cpp +++ b/krita/ui/kis_workspace_resource.cpp @@ -1,113 +1,113 @@ /* This file is part of the KDE project * Copyright (C) 2011 Sven Langkamp * * 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 "kis_workspace_resource.h" #include #include #include #define WORKSPACE_VERSION 1 KisWorkspaceResource::KisWorkspaceResource(const QString& filename): KoResource(filename) { } KisWorkspaceResource::~KisWorkspaceResource() { } bool KisWorkspaceResource::save() { if (filename().isEmpty()) return false; QFile file(filename()); file.open(QIODevice::WriteOnly); QDomDocument doc; QDomElement root = doc.createElement("Workspace"); root.setAttribute("name", name() ); root.setAttribute("version", WORKSPACE_VERSION); QDomElement state = doc.createElement("state"); state.appendChild(doc.createCDATASection(m_dockerState.toBase64())); root.appendChild(state); // Save KisPropertiesConfiguration settings QDomElement settings = doc.createElement("settings"); KisPropertiesConfiguration::toXML(doc, settings); root.appendChild(settings); doc.appendChild(root); QTextStream textStream(&file); doc.save(textStream, 4); file.close(); return true; } bool KisWorkspaceResource::load() { if (filename().isEmpty()) return false; QFile file(filename()); QDomDocument doc; if (!file.open(QIODevice::ReadOnly)) return false; if (!doc.setContent(&file)) { file.close(); return false; } file.close(); QDomElement element = doc.documentElement(); setName(element.attribute("name")); QDomElement state = element.firstChildElement("state"); if(!state.isNull()) { - m_dockerState = QByteArray::fromBase64(state.text().toAscii()); + m_dockerState = QByteArray::fromBase64(state.text().toLatin1()); } QDomElement settings = element.firstChildElement("settings"); if(!settings.isNull()) { KisPropertiesConfiguration::fromXML(settings); } setValid(true); return true; } QString KisWorkspaceResource::defaultFileExtension() const { return QString(".kws"); } void KisWorkspaceResource::setDockerState(const QByteArray& state) { m_dockerState = state; } QByteArray KisWorkspaceResource::dockerState() { return m_dockerState; } diff --git a/krita/ui/kisexiv2/kis_exif_io.cpp b/krita/ui/kisexiv2/kis_exif_io.cpp index eb4f2320359..2f32a230aac 100644 --- a/krita/ui/kisexiv2/kis_exif_io.cpp +++ b/krita/ui/kisexiv2/kis_exif_io.cpp @@ -1,620 +1,621 @@ /* * Copyright (c) 2007 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "kis_exif_io.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_exiv2.h" #include #include #include #include #include #include struct KisExifIO::Private { }; // ---- Exception convertion functions ---- // // convert ExifVersion and FlashpixVersion to a KisMetaData value KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value) { const Exiv2::DataValue* dvalue = dynamic_cast(&*value); if(dvalue) { Q_ASSERT(dvalue); QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); return KisMetaData::Value(QString(array)); } else { Q_ASSERT(value->typeId() == Exiv2::asciiString); - return KisMetaData::Value(QString::fromAscii(value->toString().c_str())); + return KisMetaData::Value(QString::fromLatin1(value->toString().c_str())); } } // convert from KisMetaData value to ExifVersion and FlashpixVersion Exiv2::Value* kmdValueToExifVersion(const KisMetaData::Value& value) { Exiv2::DataValue* dvalue = new Exiv2::DataValue; QString ver = value.asVariant().toString(); - dvalue->read((const Exiv2::byte*)ver.toAscii().data(), ver.size()); + dvalue->read((const Exiv2::byte*)ver.toLatin1().constData(), ver.size()); return dvalue; } // Convert an exif array of integer string to a KisMetaData array of integer KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value) { QList v; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); if(dvalue) { QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); for (int i = 0; i < array.size(); i++) { QChar c((char)array[i]); v.push_back(KisMetaData::Value(QString(c).toInt(0))); } } else { Q_ASSERT(value->typeId() == Exiv2::asciiString); - QString str = QString::fromAscii(value->toString().c_str()); + QString str = QString::fromLatin1(value->toString().c_str()); v.push_back(KisMetaData::Value(str.toInt())); } return KisMetaData::Value(v, KisMetaData::Value::OrderedArray); } // Convert a KisMetaData array of integer to an exif array of integer string Exiv2::Value* kmdIntOrderedArrayToExifArray(const KisMetaData::Value& value) { QList v = value.asArray(); - QString s; + QByteArray s; for (QList::iterator it = v.begin(); it != v.end(); ++it) { int val = it->asVariant().toInt(0); - s += QString::number(val); + s += QByteArray::number(val); } - return new Exiv2::DataValue((const Exiv2::byte*)s.toAscii().data(), s.toAscii().size()); + return new Exiv2::DataValue((const Exiv2::byte*)s.data(), s.size()); } QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value) { return QDateTime::fromString(value->toString().c_str(), Qt::ISODate); } template inline T fixEndianess(T v, Exiv2::ByteOrder order) { switch (order) { case Exiv2::invalidByteOrder: return v; case Exiv2::littleEndian: return qFromLittleEndian(v); case Exiv2::bigEndian: return qFromBigEndian(v); } warnKrita << "KisExifIO: unknown byte order"; return v; } Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order) { switch (order) { case Exiv2::invalidByteOrder: warnKrita << "KisExifIO: Can't invert Exiv2::invalidByteOrder"; case Exiv2::littleEndian: return Exiv2::bigEndian; case Exiv2::bigEndian: return Exiv2::littleEndian; } return Exiv2::invalidByteOrder; } KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order) { QMap oecfStructure; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); Q_ASSERT(dvalue); QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); int columns = fixEndianess((reinterpret_cast(array.data()))[0], order); int rows = fixEndianess((reinterpret_cast(array.data()))[1], order); if ((columns * rows + 4) > dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera) order = invertByteOrder(order); columns = fixEndianess((reinterpret_cast(array.data()))[0], order); rows = fixEndianess((reinterpret_cast(array.data()))[1], order); Q_ASSERT((columns * rows + 4) > dvalue->count()); } oecfStructure["Columns"] = KisMetaData::Value(columns); oecfStructure["Rows"] = KisMetaData::Value(rows); int index = 4; QList names; for (int i = 0; i < columns; i++) { int lastIndex = array.indexOf((char)0, index); QString name = array.mid(index, lastIndex - index); if (index != lastIndex) { index = lastIndex + 1; dbgFile << "Name [" << i << "] =" << name; names.append(KisMetaData::Value(name)); } else { names.append(KisMetaData::Value("")); } } oecfStructure["Names"] = KisMetaData::Value(names, KisMetaData::Value::OrderedArray); QList values; qint16* dataIt = reinterpret_cast(array.data() + index); for (int i = 0; i < columns; i++) { for (int j = 0; j < rows; j++) { values.append(KisMetaData::Value(KisMetaData::Rational(fixEndianess(dataIt[0], order), fixEndianess(dataIt[1], order)))); dataIt += 8; } } oecfStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray); dbgFile << "OECF: " << ppVar(columns) << ppVar(rows) << ppVar(dvalue->count()); return KisMetaData::Value(oecfStructure); } Exiv2::Value* kmdOECFStructureToExifOECF(const KisMetaData::Value& value) { QMap oecfStructure = value.asStructure(); quint16 columns = oecfStructure["Columns"].asVariant().toInt(0); quint16 rows = oecfStructure["Rows"].asVariant().toInt(0); QList names = oecfStructure["Names"].asArray(); QList values = oecfStructure["Values"].asArray(); Q_ASSERT(columns*rows == values.size()); int length = 4 + rows * columns * 8; // The 4 byte for storing rows/columns and the rows*columns*sizeof(rational) bool saveNames = (!names.empty() && names[0].asVariant().toString().size() > 0); if (saveNames) { for (int i = 0; i < columns; i++) { length += names[i].asVariant().toString().size() + 1; } } QByteArray array(length, 0); (reinterpret_cast(array.data()))[0] = columns; (reinterpret_cast(array.data()))[1] = rows; int index = 4; if (saveNames) { for (int i = 0; i < columns; i++) { - QByteArray name = names[i].asVariant().toString().toAscii(); + QByteArray name = names[i].asVariant().toString().toLatin1(); name.append((char)0); memcpy(array.data() + index, name.data(), name.size()); index += name.size(); } } qint16* dataIt = reinterpret_cast(array.data() + index); for (QList::iterator it = values.begin(); it != values.end(); ++it) { dataIt[0] = it->asRational().numerator; dataIt[1] = it->asRational().denominator; dataIt += 2; } return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size()); } KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value) { QMap deviceSettingStructure; QByteArray array; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); if(dvalue) { array.resize(dvalue->count()); dvalue->copy((Exiv2::byte*)array.data()); } else { Q_ASSERT(value->typeId() == Exiv2::unsignedShort); array.resize(2 * value->count()); value->copy((Exiv2::byte*)array.data(), Exiv2::littleEndian); } int columns = (reinterpret_cast(array.data()))[0]; int rows = (reinterpret_cast(array.data()))[1]; deviceSettingStructure["Columns"] = KisMetaData::Value(columns); deviceSettingStructure["Rows"] = KisMetaData::Value(rows); QList settings; QByteArray null(2, 0); for (int index = 4; index < array.size(); ) { int lastIndex = array.indexOf(null, index); QString setting = QString::fromUtf16((ushort*)( array.data() + index), lastIndex - index + 2); index = lastIndex + 2; dbgFile << "Setting << " << setting; settings.append(KisMetaData::Value(setting)); } deviceSettingStructure["Settings"] = KisMetaData::Value(settings, KisMetaData::Value::OrderedArray); return KisMetaData::Value(deviceSettingStructure); } Exiv2::Value* deviceSettingDescriptionKMDToExif(const KisMetaData::Value& value) { QMap deviceSettingStructure = value.asStructure(); quint16 columns = deviceSettingStructure["Columns"].asVariant().toInt(0); quint16 rows = deviceSettingStructure["Rows"].asVariant().toInt(0); QTextCodec* codec = QTextCodec::codecForName("UTF-16"); QList settings = deviceSettingStructure["Settings"].asArray(); QByteArray array(4, 0); (reinterpret_cast(array.data()))[0] = columns; (reinterpret_cast(array.data()))[1] = rows; for (int i = 0; i < settings.count(); i++) { QString str = settings[i].asVariant().toString(); QByteArray setting = codec->fromUnicode(str); array.append(setting); } return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size()); } KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order) { QMap cfaPatternStructure; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); Q_ASSERT(dvalue); QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); int columns = fixEndianess((reinterpret_cast(array.data()))[0], order); int rows = fixEndianess((reinterpret_cast(array.data()))[1], order); if ((columns * rows + 4) != dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera) order = invertByteOrder(order); columns = fixEndianess((reinterpret_cast(array.data()))[0], order); rows = fixEndianess((reinterpret_cast(array.data()))[1], order); Q_ASSERT((columns * rows + 4) == dvalue->count()); } cfaPatternStructure["Columns"] = KisMetaData::Value(columns); cfaPatternStructure["Rows"] = KisMetaData::Value(rows); QList values; int index = 4; for (int i = 0; i < columns * rows; i++) { values.append(KisMetaData::Value(*(array.data() + index))); index++; } cfaPatternStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray); dbgFile << "CFAPattern " << ppVar(columns) << " " << ppVar(rows) << ppVar(values.size() << ppVar(dvalue->count())); return KisMetaData::Value(cfaPatternStructure); } Exiv2::Value* cfaPatternKMDToExif(const KisMetaData::Value& value) { QMap cfaStructure = value.asStructure(); quint16 columns = cfaStructure["Columns"].asVariant().toInt(0); quint16 rows = cfaStructure["Rows"].asVariant().toInt(0); QList values = cfaStructure["Values"].asArray(); Q_ASSERT(columns*rows == values.size()); QByteArray array(4 + columns*rows, 0); (reinterpret_cast(array.data()))[0] = columns; (reinterpret_cast(array.data()))[1] = rows; for (int i = 0; i < columns * rows; i++) { int val = values[i].asVariant().toInt(); *(array.data() + 4 + i) = val; } dbgFile << "Cfa Array " << ppVar(columns) << ppVar(rows) << ppVar(array.size()); return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size()); } // Read and write Flash // KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value) { uint16_t v = value->toLong(); QMap flashStructure; bool fired = (v & 0x01); // bit 1 is whether flash was fired or not flashStructure["Fired"] = QVariant(fired); int ret = ((v >> 1) & 0x03); // bit 2 and 3 are Return flashStructure["Return"] = QVariant(ret); int mode = ((v >> 3) & 0x03); // bit 4 and 5 are Mode flashStructure["Mode"] = QVariant(mode); bool function = ((v >> 5) & 0x01); // bit 6 if function flashStructure["Function"] = QVariant(function); bool redEye = ((v >> 6) & 0x01); // bit 7 if function flashStructure["RedEyeMode"] = QVariant(redEye); return KisMetaData::Value(flashStructure); } Exiv2::Value* flashKMDToExif(const KisMetaData::Value& value) { uint16_t v = 0; QMap flashStructure = value.asStructure(); v = flashStructure["Fired"].asVariant().toBool(); v |= ((flashStructure["Return"].asVariant().toInt() & 0x03) << 1); v |= ((flashStructure["Mode"].asVariant().toInt() & 0x03) << 3); v |= ((flashStructure["Function"].asVariant().toInt() & 0x03) << 5); v |= ((flashStructure["RedEyeMode"].asVariant().toInt() & 0x03) << 6); return new Exiv2::ValueType(v); } // ---- Implementation of KisExifIO ----// KisExifIO::KisExifIO() : d(new Private) { } KisExifIO::~KisExifIO() { delete d; } bool KisExifIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const { ioDevice->open(QIODevice::WriteOnly); Exiv2::ExifData exifData; if (headerType == KisMetaData::IOBackend::JpegHeader) { QByteArray header(6, 0); header[0] = 0x45; header[1] = 0x78; header[2] = 0x69; header[3] = 0x66; header[4] = 0x00; header[5] = 0x00; ioDevice->write(header); } for (QHash::const_iterator it = store->begin(); it != store->end(); ++it) { try { const KisMetaData::Entry& entry = *it; dbgFile << "Trying to save: " << entry.name() << " of " << entry.schema()->prefix() << ":" << entry.schema()->uri(); QString exivKey = ""; if (entry.schema()->uri() == KisMetaData::Schema::TIFFSchemaUri) { exivKey = "Exif.Image." + entry.name(); } else if (entry.schema()->uri() == KisMetaData::Schema::EXIFSchemaUri) { // Distinguish between exif and gps if (entry.name().left(3) == "GPS") { exivKey = "Exif.GPS." + entry.name(); } else { exivKey = "Exif.Photo." + entry.name(); } } else if (entry.schema()->uri() == KisMetaData::Schema::DublinCoreSchemaUri) { if (entry.name() == "description") { exivKey = "Exif.Image.ImageDescription"; } else if (entry.name() == "creator") { exivKey = "Exif.Image.Artist"; } else if (entry.name() == "rights") { exivKey = "Exif.Image.Copyright"; } } else if (entry.schema()->uri() == KisMetaData::Schema::XMPSchemaUri) { if (entry.name() == "ModifyDate") { exivKey = "Exif.Image.DateTime"; } else if (entry.name() == "CreatorTool") { exivKey = "Exif.Image.Software"; } } else if (entry.schema()->uri() == KisMetaData::Schema::MakerNoteSchemaUri) { if (entry.name() == "RawData") { exivKey = "Exif.Photo.MakerNote"; } } dbgFile << "Saving " << entry.name() << " to " << exivKey; if (exivKey.isEmpty()) { dbgFile << entry.qualifiedName() << " is unsavable to EXIF"; } else { Exiv2::ExifKey exifKey(qPrintable(exivKey)); Exiv2::Value* v = 0; if (exivKey == "Exif.Photo.ExifVersion" || exivKey == "Exif.Photo.FlashpixVersion") { v = kmdValueToExifVersion(entry.value()); } else if (exivKey == "Exif.Photo.FileSource") { char s[] = { 0x03 }; v = new Exiv2::DataValue((const Exiv2::byte*)s, 1); } else if (exivKey == "Exif.Photo.SceneType") { char s[] = { 0x01 }; v = new Exiv2::DataValue((const Exiv2::byte*)s, 1); } else if (exivKey == "Exif.Photo.ComponentsConfiguration") { v = kmdIntOrderedArrayToExifArray(entry.value()); } else if (exivKey == "Exif.Image.Artist") { // load as dc:creator KisMetaData::Value creator = entry.value().asArray()[0]; #if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20 v = kmdValueToExivValue(creator, Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(creator, exifKey.defaultTypeId()); #endif } else if (exivKey == "Exif.Photo.OECF") { v = kmdOECFStructureToExifOECF(entry.value()); } else if (exivKey == "Exif.Photo.DeviceSettingDescription") { v = deviceSettingDescriptionKMDToExif(entry.value()); } else if (exivKey == "Exif.Photo.CFAPattern") { v = cfaPatternKMDToExif(entry.value()); } else if (exivKey == "Exif.Photo.Flash") { v = flashKMDToExif(entry.value()); } else if (exivKey == "Exif.Photo.UserComment") { Q_ASSERT(entry.value().type() == KisMetaData::Value::LangArray); QMap langArr = entry.value().asLangArray(); if (langArr.contains("x-default")) { #if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20 v = kmdValueToExivValue(langArr.value("x-default"), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(langArr.value("x-default"), exifKey.defaultTypeId()); #endif } else if (langArr.size() > 0) { #if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20 v = kmdValueToExivValue(langArr.begin().value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(langArr.begin().value(), exifKey.defaultTypeId()); #endif } } else { dbgFile << exifKey.tag(); #if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20 v = kmdValueToExivValue(entry.value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(entry.value(), exifKey.defaultTypeId()); #endif } if (v && v->typeId() != Exiv2::invalidTypeId) { dbgFile << "Saving key" << exivKey << " of KMD value" << entry.value(); exifData.add(exifKey, v); } else { dbgFile << "No exif value was created for" << entry.qualifiedName() << " as" << exivKey;// << " of KMD value" << entry.value(); } } } catch (Exiv2::AnyError& e) { dbgFile << "exiv error " << e.what(); } } #if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17 Exiv2::DataBuf rawData = exifData.copy(); ioDevice->write((const char*) rawData.pData_, rawData.size_); #else Exiv2::Blob rawData; Exiv2::ExifParser::encode(rawData, Exiv2::littleEndian, exifData); ioDevice->write((const char*) &*rawData.begin(), rawData.size()); #endif ioDevice->close(); return true; } bool KisExifIO::canSaveAllEntries(KisMetaData::Store* /*store*/) const { return false; // It's a known fact that exif can't save all information, but TODO: write the check } bool KisExifIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const { ioDevice->open(QIODevice::ReadOnly); if (!ioDevice->isOpen()) { return false; } QByteArray arr = ioDevice->readAll(); Exiv2::ExifData exifData; Exiv2::ByteOrder byteOrder; #if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17 exifData.load((const Exiv2::byte*)arr.data(), arr.size()); byteOrder = exifData.byteOrder(); #else byteOrder = Exiv2::ExifParser::decode(exifData, (const Exiv2::byte*)arr.data(), arr.size()); #endif dbgFile << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian); dbgFile << "There are" << exifData.count() << " entries in the exif section"; const KisMetaData::Schema* tiffSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri); Q_ASSERT(tiffSchema); const KisMetaData::Schema* exifSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri); Q_ASSERT(exifSchema); const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri); Q_ASSERT(dcSchema); const KisMetaData::Schema* xmpSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri); Q_ASSERT(xmpSchema); for (Exiv2::ExifMetadata::const_iterator it = exifData.begin(); it != exifData.end(); ++it) { if (it->key() == "Exif.Photo.StripOffsets" || it->key() == "RowsPerStrip" || it->key() == "StripByteCounts" || it->key() == "JPEGInterchangeFormat" || it->key() == "JPEGInterchangeFormatLength" || it->tagName() == "0x0000" ) { dbgFile << it->key().c_str() << " is ignored"; } else if (it->key() == "Exif.Photo.MakerNote") { const KisMetaData::Schema* makerNoteSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri); store->addEntry(KisMetaData::Entry(makerNoteSchema, "RawData", exivValueToKMDValue(it->getValue(), false))); } else if (it->key() == "Exif.Image.DateTime") { // load as xmp:ModifyDate store->addEntry(KisMetaData::Entry(xmpSchema, "ModifyDate", KisMetaData::Value(exivValueToDateTime(it->getValue())))); } else if (it->key() == "Exif.Image.ImageDescription") { // load as "dc:description" store->addEntry(KisMetaData::Entry(dcSchema, "description", exivValueToKMDValue(it->getValue(), false))); } else if (it->key() == "Exif.Image.Software") { // load as "xmp:CreatorTool" store->addEntry(KisMetaData::Entry(xmpSchema, "CreatorTool", exivValueToKMDValue(it->getValue(), false))); } else if (it->key() == "Exif.Image.Artist") { // load as dc:creator QList creators; creators.push_back(exivValueToKMDValue(it->getValue(), false)); store->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(creators, KisMetaData::Value::OrderedArray))); } else if (it->key() == "Exif.Image.Copyright") { // load as dc:rights store->addEntry(KisMetaData::Entry(dcSchema, "rights", exivValueToKMDValue(it->getValue(), false))); } else if (it->groupName() == "Image") { // Tiff tags QString fixedTN = it->tagName().c_str(); if (it->key() == "Exif.Image.ExifTag") { dbgFile << "Ignoring " << it->key().c_str(); } else if (KisMetaData::Entry::isValidName(fixedTN)) { store->addEntry(KisMetaData::Entry(tiffSchema, fixedTN, exivValueToKMDValue(it->getValue(), false))) ; } else { dbgFile << "Invalid tag name: " << fixedTN; } } else if (it->groupName() == "Photo" || (it->groupName() == "GPS")) { // Exif tags (and GPS tags) KisMetaData::Value v; if (it->key() == "Exif.Photo.ExifVersion" || it->key() == "Exif.Photo.FlashpixVersion") { v = exifVersionToKMDValue(it->getValue()); } else if (it->key() == "Exif.Photo.FileSource") { v = KisMetaData::Value(3); } else if (it->key() == "Exif.Photo.SceneType") { v = KisMetaData::Value(1); } else if (it->key() == "Exif.Photo.ComponentsConfiguration") { v = exifArrayToKMDIntOrderedArray(it->getValue()); } else if (it->key() == "Exif.Photo.OECF") { v = exifOECFToKMDOECFStructure(it->getValue(), byteOrder); } else if (it->key() == "Exif.Photo.DateTimeDigitized" || it->key() == "Exif.Photo.DateTimeOriginal") { v = KisMetaData::Value(exivValueToDateTime(it->getValue())); } else if (it->key() == "Exif.Photo.DeviceSettingDescription") { v = deviceSettingDescriptionExifToKMD(it->getValue()); } else if (it->key() == "Exif.Photo.CFAPattern") { v = cfaPatternExifToKMD(it->getValue(), byteOrder); } else if (it->key() == "Exif.Photo.Flash") { v = flashExifToKMD(it->getValue()); } else if (it->key() == "Exif.Photo.UserComment") { KisMetaData::Value vUC = exivValueToKMDValue(it->getValue(), false); Q_ASSERT(vUC.type() == KisMetaData::Value::Variant); QVariant commentVar = vUC.asVariant(); QString comment; if (commentVar.type() == QVariant::String) { comment = commentVar.toString(); } else if (commentVar.type() == QVariant::ByteArray) { - comment = QString::fromLatin1(commentVar.toByteArray().data(), commentVar.toByteArray().size()); + const QByteArray commentString = commentVar.toByteArray(); + comment = QString::fromLatin1(commentString.constData(), commentString.size()); } else { warnKrita << "KisExifIO: Unhandled UserComment value type."; } KisMetaData::Value vcomment(comment); vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default")); QList alt; alt.append(vcomment); v = KisMetaData::Value(alt, KisMetaData::Value::LangArray); } else { bool forceSeq = false; KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray; if (it->key() == "Exif.Photo.ISOSpeedRatings") { forceSeq = true; arrayType = KisMetaData::Value::OrderedArray; } v = exivValueToKMDValue(it->getValue(), forceSeq, arrayType); } if (it->key() == "Exif.Photo.InteroperabilityTag" || it->key() == "Exif.Photo.0xea1d") { // InteroperabilityTag isn't useful for XMP, 0xea1d isn't a valid Exif tag dbgFile << "Ignoring " << it->key().c_str(); } else { store->addEntry(KisMetaData::Entry(exifSchema, it->tagName().c_str(), v)); } } else if (it->groupName() == "Thumbnail") { dbgFile << "Ignoring thumbnail tag :" << it->key().c_str(); } else { dbgFile << "Unknown exif tag, cannot load:" << it->key().c_str(); } } ioDevice->close(); return true; } diff --git a/krita/ui/kisexiv2/kis_exiv2.cpp b/krita/ui/kisexiv2/kis_exiv2.cpp index 9f06ff9f249..d993804729d 100644 --- a/krita/ui/kisexiv2/kis_exiv2.cpp +++ b/krita/ui/kisexiv2/kis_exiv2.cpp @@ -1,292 +1,292 @@ /* * Copyright (c) 2007 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "kis_exiv2.h" #include #include #include "kis_iptc_io.h" #include "kis_exif_io.h" #include "kis_xmp_io.h" #include #include // ---- Generic convertion functions ---- // // Convert an exiv value to a KisMetaData value KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr value, bool forceSeq, KisMetaData::Value::ValueType arrayType) { switch (value->typeId()) { case Exiv2::signedByte: case Exiv2::invalidTypeId: case Exiv2::lastTypeId: case Exiv2::directory: dbgFile << "Invalid value :" << value->typeId() << " value =" << value->toString().c_str(); return KisMetaData::Value(); case Exiv2::undefined: { dbgFile << "Undefined value :" << value->typeId() << " value =" << value->toString().c_str(); QByteArray array(value->count() , 0); value->copy((Exiv2::byte*)array.data(), Exiv2::invalidByteOrder); return KisMetaData::Value(QString(array.toBase64())); } case Exiv2::unsignedByte: case Exiv2::unsignedShort: case Exiv2::unsignedLong: case Exiv2::signedShort: case Exiv2::signedLong: { if (value->count() == 1 && !forceSeq) { return KisMetaData::Value((int)value->toLong()); } else { QList array; for (int i = 0; i < value->count(); i++) array.push_back(KisMetaData::Value((int)value->toLong(i))); return KisMetaData::Value(array, arrayType); } } case Exiv2::asciiString: case Exiv2::string: case Exiv2::comment: // look at kexiv2 for the problem about decoding correctly that tag return KisMetaData::Value(value->toString().c_str()); case Exiv2::unsignedRational: if(value->size() < 2) { dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str(); return KisMetaData::Value(); } return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second)); case Exiv2::signedRational: if(value->size() < 2) { dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str(); return KisMetaData::Value(); } return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second)); case Exiv2::date: case Exiv2::time: return KisMetaData::Value(QDateTime::fromString(value->toString().c_str(), Qt::ISODate)); case Exiv2::xmpText: case Exiv2::xmpAlt: case Exiv2::xmpBag: case Exiv2::xmpSeq: case Exiv2::langAlt: default: { dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str(); //Q_ASSERT(false); // This point must never be reached ! return KisMetaData::Value(); } } dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str(); //Q_ASSERT(false); // This point must never be reached ! return KisMetaData::Value(); } // Convert a QtVariant to an Exiv value Exiv2::Value* variantToExivValue(const QVariant& variant, Exiv2::TypeId type) { switch (type) { case Exiv2::undefined: { - QByteArray arr = QByteArray::fromBase64(variant.toString().toAscii()); + QByteArray arr = QByteArray::fromBase64(variant.toString().toLatin1()); return new Exiv2::DataValue((Exiv2::byte*)arr.data(), arr.size()); } case Exiv2::unsignedByte: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::unsignedShort: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::unsignedLong: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::signedShort: return new Exiv2::ValueType(variant.toInt(0)); case Exiv2::signedLong: return new Exiv2::ValueType(variant.toInt(0)); case Exiv2::date: { QDate date = variant.toDate(); return new Exiv2::DateValue(date.year(), date.month(), date.day()); } case Exiv2::asciiString: if (variant.type() == QVariant::DateTime) { return new Exiv2::AsciiValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss"))); } else return new Exiv2::AsciiValue(qPrintable(variant.toString())); case Exiv2::string: { if (variant.type() == QVariant::DateTime) { return new Exiv2::StringValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss"))); } else return new Exiv2::StringValue(qPrintable(variant.toString())); } case Exiv2::comment: return new Exiv2::CommentValue(qPrintable(variant.toString())); default: dbgFile << "Unhandled type:" << type; //Q_ASSERT(false); return 0; } } template Exiv2::Value* arrayToExivValue(const KisMetaData::Value& value) { Exiv2::ValueType<_TYPE_>* ev = new Exiv2::ValueType<_TYPE_>(); for (int i = 0; i < value.asArray().size(); ++i) { ev->value_.push_back(qVariantValue<_TYPE_>(value.asArray()[i].asVariant())); } return ev; } // Convert a KisMetaData to an Exiv value Exiv2::Value* kmdValueToExivValue(const KisMetaData::Value& value, Exiv2::TypeId type) { switch (value.type()) { case KisMetaData::Value::Invalid: return &*Exiv2::Value::create(Exiv2::invalidTypeId); case KisMetaData::Value::Variant: { return variantToExivValue(value.asVariant(), type); } case KisMetaData::Value::Rational: //Q_ASSERT(type == Exiv2::signedRational || type == Exiv2::unsignedRational); if (type == Exiv2::signedRational) { return new Exiv2::ValueType(Exiv2::Rational(value.asRational().numerator, value.asRational().denominator)); } else { return new Exiv2::ValueType(Exiv2::URational(value.asRational().numerator, value.asRational().denominator)); } case KisMetaData::Value::OrderedArray: case KisMetaData::Value::UnorderedArray: case KisMetaData::Value::AlternativeArray: { switch (type) { case Exiv2::unsignedByte: return arrayToExivValue(value); case Exiv2::unsignedShort: return arrayToExivValue(value); case Exiv2::unsignedLong: return arrayToExivValue(value); case Exiv2::signedShort: return arrayToExivValue(value); case Exiv2::signedLong: return arrayToExivValue(value); case Exiv2::string: { Exiv2::StringValue* ev = new Exiv2::StringValue(); for (int i = 0; i < value.asArray().size(); ++i) { - ev->value_ += qVariantValue(value.asArray()[i].asVariant()).toAscii().data(); + ev->value_ += qVariantValue(value.asArray()[i].asVariant()).toLatin1().constData(); if (i != value.asArray().size() - 1) ev->value_ += ','; } return ev; } default: dbgFile << type << " " << value; //Q_ASSERT(false); } } default: dbgFile << type << " " << value; //Q_ASSERT(false); } return 0; } Exiv2::Value* kmdValueToExivXmpValue(const KisMetaData::Value& value) { //Q_ASSERT(value.type() != KisMetaData::Value::Structure); switch (value.type()) { case KisMetaData::Value::Invalid: return new Exiv2::DataValue(Exiv2::invalidTypeId); case KisMetaData::Value::Variant: { QVariant var = value.asVariant(); if (var.type() == QVariant::Bool) { if (var.toBool()) { return new Exiv2::XmpTextValue("True"); } else { return new Exiv2::XmpTextValue("False"); } } else { //Q_ASSERT(var.canConvert(QVariant::String)); - return new Exiv2::XmpTextValue(var.toString().toAscii().data()); + return new Exiv2::XmpTextValue(var.toString().toLatin1().constData()); } } case KisMetaData::Value::Rational: { QString rat = "%1 / %2"; rat = rat.arg(value.asRational().numerator); rat = rat.arg(value.asRational().denominator); - return new Exiv2::XmpTextValue(rat.toAscii().data()); + return new Exiv2::XmpTextValue(rat.toLatin1().constData()); } case KisMetaData::Value::AlternativeArray: case KisMetaData::Value::OrderedArray: case KisMetaData::Value::UnorderedArray: { Exiv2::XmpArrayValue* arrV = new Exiv2::XmpArrayValue; switch (value.type()) { case KisMetaData::Value::OrderedArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaSeq); break; case KisMetaData::Value::UnorderedArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaBag); break; case KisMetaData::Value::AlternativeArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaAlt); break; default: // Cannot happen ; } foreach(const KisMetaData::Value& v, value.asArray()) { Exiv2::Value* ev = kmdValueToExivXmpValue(v); if (ev) { arrV->read(ev->toString()); delete ev; } } return arrV; } case KisMetaData::Value::LangArray: { Exiv2::Value* arrV = new Exiv2::LangAltValue; QMap langArray = value.asLangArray(); for (QMap::iterator it = langArray.begin(); it != langArray.end(); ++it) { QString exivVal; if (it.key() != "x-default") { exivVal = "lang=" + it.key() + ' '; } //Q_ASSERT(it.value().type() == KisMetaData::Value::Variant); QVariant var = it.value().asVariant(); //Q_ASSERT(var.type() == QVariant::String); exivVal += var.toString(); - arrV->read(exivVal.toAscii().data()); + arrV->read(exivVal.toLatin1().constData()); } return arrV; } case KisMetaData::Value::Structure: default: { warnKrita << "KisExiv2: Unhandled value type"; return 0; } } warnKrita << "KisExiv2: Unhandled value type"; return 0; } void KisExiv2::initialize() { // XXX_EXIV: make the exiv io backends real plugins KisMetaData::IOBackendRegistry* ioreg = KisMetaData::IOBackendRegistry::instance(); ioreg->add(new KisIptcIO); ioreg->add(new KisExifIO); ioreg->add(new KisXMPIO); } diff --git a/krita/ui/kisexiv2/kis_xmp_io.cpp b/krita/ui/kisexiv2/kis_xmp_io.cpp index f43c68b23b7..ab350834469 100644 --- a/krita/ui/kisexiv2/kis_xmp_io.cpp +++ b/krita/ui/kisexiv2/kis_xmp_io.cpp @@ -1,365 +1,366 @@ /* * Copyright (c) 2008-2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "kis_xmp_io.h" #include #include #include "kis_exiv2.h" #include #include #include #include #include #include #include #include KisXMPIO::KisXMPIO() { } KisXMPIO::~KisXMPIO() { } inline std::string exiv2Prefix(const KisMetaData::Schema* _schema) { - std::string prefix = Exiv2::XmpProperties::prefix(_schema->uri().toAscii().data()); + const QByteArray latin1SchemaUri = _schema->uri().toLatin1(); + std::string prefix = Exiv2::XmpProperties::prefix(latin1SchemaUri.constData()); if (prefix.empty()) { dbgFile << "Unknown namespace " << ppVar(_schema->uri()) << ppVar(_schema->prefix()); - prefix = _schema->prefix().toAscii().data(); - Exiv2::XmpProperties::registerNs(_schema->uri().toAscii().data(), prefix); + prefix = _schema->prefix().toLatin1().constData(); + Exiv2::XmpProperties::registerNs(latin1SchemaUri.constData(), prefix); } return prefix; } namespace { void saveStructure(Exiv2::XmpData& xmpData_, const QString& name, const std::string& prefix, const QMap& structure, const KisMetaData::Schema* structureSchema) { std::string structPrefix = exiv2Prefix(structureSchema); for (QMap::const_iterator it = structure.begin(); it != structure.end(); ++it) { Q_ASSERT(it.value().type() != KisMetaData::Value::Structure); // Can't nest structure QString key = QString("%1/%2:%3").arg(name).arg(structPrefix.c_str()).arg(it.key()); - Exiv2::XmpKey ekey(prefix, key.toAscii().data()); + Exiv2::XmpKey ekey(prefix, key.toLatin1().constData()); dbgFile << ppVar(key) << ppVar(ekey.key().c_str()); Exiv2::Value *v = kmdValueToExivXmpValue(it.value()); if (v) { xmpData_.add(ekey, v); } } } } bool KisXMPIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const { dbgFile << "Save XMP Data"; Exiv2::XmpData xmpData_; for (QHash::const_iterator it = store->begin(); it != store->end(); ++it) { const KisMetaData::Entry& entry = *it; // Check whether the prefix and namespace are know to exiv2 std::string prefix = exiv2Prefix(entry.schema()); dbgFile << "Saving " << entry.name(); const KisMetaData::Value& value = entry.value(); const KisMetaData::TypeInfo* typeInfo = entry.schema()->propertyType(entry.name()); if (value.type() == KisMetaData::Value::Structure) { QMap structure = value.asStructure(); const KisMetaData::Schema* structureSchema = 0; if (typeInfo) { structureSchema = typeInfo->structureSchema(); } if (!structureSchema) { dbgFile << "Unknown schema for " << entry.name(); structureSchema = entry.schema(); } Q_ASSERT(structureSchema); saveStructure(xmpData_, entry.name(), prefix, structure, structureSchema); } else { - Exiv2::XmpKey key(prefix, entry.name().toAscii().data()); + Exiv2::XmpKey key(prefix, entry.name().toLatin1().constData()); if (typeInfo && (typeInfo->propertyType() == KisMetaData::TypeInfo::OrderedArrayType || typeInfo->propertyType() == KisMetaData::TypeInfo::UnorderedArrayType || typeInfo->propertyType() == KisMetaData::TypeInfo::AlternativeArrayType) && typeInfo->embeddedPropertyType()->propertyType() == KisMetaData::TypeInfo::StructureType) { // Here is the bad part, again we need to do it by hand Exiv2::XmpTextValue tv; switch (typeInfo->propertyType()) { case KisMetaData::TypeInfo::OrderedArrayType: tv.setXmpArrayType(Exiv2::XmpValue::xaSeq); break; case KisMetaData::TypeInfo::UnorderedArrayType: tv.setXmpArrayType(Exiv2::XmpValue::xaBag); break; case KisMetaData::TypeInfo::AlternativeArrayType: tv.setXmpArrayType(Exiv2::XmpValue::xaAlt); break; default: // Cannot happen ; } xmpData_.add(key, &tv); // set the arrya type const KisMetaData::TypeInfo* stuctureTypeInfo = typeInfo->embeddedPropertyType(); const KisMetaData::Schema* structureSchema = 0; if (stuctureTypeInfo) { structureSchema = stuctureTypeInfo->structureSchema(); } if (!structureSchema) { dbgFile << "Unknown schema for " << entry.name(); structureSchema = entry.schema(); } Q_ASSERT(structureSchema); QList array = value.asArray(); for (int idx = 0; idx < array.size(); ++idx) { saveStructure(xmpData_, QString("%1[%2]").arg(entry.name()).arg(idx + 1), prefix, array[idx].asStructure(), structureSchema); } } else { dbgFile << ppVar(key.key().c_str()); Exiv2::Value *v = kmdValueToExivXmpValue(value); if (v) { xmpData_.add(key, v); } } } // TODO property qualifier } // Serialize data std::string xmpPacket_; Exiv2::XmpParser::encode(xmpPacket_, xmpData_); // Save data into the IO device ioDevice->open(QIODevice::WriteOnly); if (headerType == KisMetaData::IOBackend::JpegHeader) { xmpPacket_ = "http://ns.adobe.com/xap/1.0/\0" + xmpPacket_; } ioDevice->write(xmpPacket_.c_str(), xmpPacket_.length()); return true; } bool KisXMPIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const { ioDevice->open(QIODevice::ReadOnly); dbgFile << "Load XMP Data"; std::string xmpPacket_; QByteArray arr = ioDevice->readAll(); xmpPacket_.assign(arr.data(), arr.length()); dbgFile << xmpPacket_.length(); // dbgFile << xmpPacket_.c_str(); Exiv2::XmpData xmpData_; Exiv2::XmpParser::decode(xmpData_, xmpPacket_); QMap< const KisMetaData::Schema*, QMap > > structures; QMap< const KisMetaData::Schema*, QMap > > > arraysOfStructures; for (Exiv2::XmpData::iterator it = xmpData_.begin(); it != xmpData_.end(); ++it) { dbgFile << it->key().c_str(); Exiv2::XmpKey key(it->key()); dbgFile << key.groupName().c_str() << " " << key.tagName().c_str() << " " << key.ns().c_str(); if ((key.groupName() == "exif" || key.groupName() == "tiff") && key.tagName() == "NativeDigest") { // TODO: someone who has time to lose can look in adding support for NativeDigest, it's undocumented use by the XMP SDK to check if exif data has been changed while XMP hasn't been updated dbgFile << "dropped"; } else { const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromPrefix(key.groupName().c_str()); if (!schema) { schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(key.ns().c_str()); if (!schema) { schema = KisMetaData::SchemaRegistry::instance()->create(key.ns().c_str(), key.groupName().c_str()); Q_ASSERT(schema); } } const Exiv2::Value::AutoPtr value = it->getValue(); // Decrypt key QString structName = ""; QString tagName = key.tagName().c_str(); int arrayIndex = -1; const KisMetaData::TypeInfo* typeInfo = 0; bool isStructureEntry = false; bool isStructureInArrayEntry = false; if (tagName.contains("/")) { QRegExp regexp("([A-Za-z]\\w+)/([A-Za-z]\\w+):([A-Za-z]\\w+)"); if (regexp.indexIn(tagName) != -1) { structName = regexp.capturedTexts()[1]; tagName = regexp.capturedTexts()[3]; typeInfo = schema->propertyType(structName); Q_ASSERT(typeInfo == schema->propertyType(structName)); if (typeInfo && typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) { typeInfo = typeInfo->structureSchema()->propertyType(tagName); } isStructureEntry = true; } else { QRegExp regexp2("([A-Za-z]\\w+)\\[(\\d+)\\]/([A-Za-z]\\w+):([A-Za-z]\\w+)"); if (regexp2.indexIn(tagName) != -1) { dbgFile << ppVar(tagName); structName = regexp2.capturedTexts()[1]; arrayIndex = regexp2.capturedTexts()[2].toInt() - 1; tagName = regexp2.capturedTexts()[4]; dbgFile << ppVar(structName) << ppVar(regexp2.capturedTexts()[3]); Q_ASSERT(schema->propertyType(structName)); if (schema->propertyType(structName)) { typeInfo = schema->propertyType(structName)->embeddedPropertyType(); Q_ASSERT(typeInfo); if (typeInfo && typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) { typeInfo = typeInfo->structureSchema()->propertyType(tagName); } } isStructureInArrayEntry = true; } else { dbgFile << "Decoding structure name/entry failed: " << tagName; } } } else { typeInfo = schema->propertyType(tagName); } KisMetaData::Value v; bool ignoreValue = false; // Compute the value if (value->typeId() == Exiv2::xmpBag || value->typeId() == Exiv2::xmpSeq || value->typeId() == Exiv2::xmpAlt) { const KisMetaData::TypeInfo* embeddedTypeInfo = 0; if (typeInfo) { embeddedTypeInfo = typeInfo->embeddedPropertyType(); } const KisMetaData::Parser* parser = 0; if (embeddedTypeInfo) { parser = embeddedTypeInfo->parser(); } const Exiv2::XmpArrayValue* xav = dynamic_cast(value.get()); Q_ASSERT(xav); QList array; for (std::vector< std::string >::const_iterator it = xav->value_.begin(); it != xav->value_.end(); ++it) { QString value = it->c_str(); if (parser) { array.push_back(parser->parse(value)); } else { dbgImage << "No parser " << tagName; array.push_back(KisMetaData::Value(value)); } } KisMetaData::Value::ValueType vt = KisMetaData::Value::Invalid; switch (xav->xmpArrayType()) { case Exiv2::XmpValue::xaNone: warnKrita << "KisXMPIO: Unsupported array"; break; case Exiv2::XmpValue::xaAlt: vt = KisMetaData::Value::AlternativeArray; break; case Exiv2::XmpValue::xaBag: vt = KisMetaData::Value::UnorderedArray; break; case Exiv2::XmpValue::xaSeq: vt = KisMetaData::Value::OrderedArray; break; } v = KisMetaData::Value(array, vt); } else if (value->typeId() == Exiv2::langAlt) { const Exiv2::LangAltValue* xav = dynamic_cast(value.get()); QList alt; for (std::map< std::string, std::string>::const_iterator it = xav->value_.begin(); it != xav->value_.end(); ++it) { KisMetaData::Value valt(it->second.c_str()); valt.addPropertyQualifier("xml:lang", KisMetaData::Value(it->first.c_str())); alt.push_back(valt); } v = KisMetaData::Value(alt, KisMetaData::Value::LangArray); } else { QString valTxt = value->toString().c_str(); if (typeInfo && typeInfo->parser()) { v = typeInfo->parser()->parse(valTxt); } else { dbgFile << "No parser " << tagName; v = KisMetaData::Value(valTxt); } if (valTxt == "type=\"Struct\"") { if (!typeInfo || typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) { ignoreValue = true; } } } // set the value dbgFile << ppVar(tagName); if (isStructureEntry) { structures[schema][structName][tagName] = v; } else if (isStructureInArrayEntry) { if (arraysOfStructures[schema][structName].size() <= arrayIndex) { arraysOfStructures[schema][structName].resize(arrayIndex + 1); } arraysOfStructures[schema][structName][arrayIndex][tagName] = v; } else { if (!ignoreValue) { store->addEntry(KisMetaData::Entry(schema, tagName, v)); } else { dbgFile << "Ignoring value for " << tagName << " " << v; } } } } for (QMap< const KisMetaData::Schema*, QMap > >::iterator it = structures.begin(); it != structures.end(); ++it) { const KisMetaData::Schema* schema = it.key(); for (QMap >::iterator it2 = it.value().begin(); it2 != it.value().end(); ++it2) { store->addEntry(KisMetaData::Entry(schema, it2.key(), KisMetaData::Value(it2.value()))); } } for (QMap< const KisMetaData::Schema*, QMap > > >::iterator it = arraysOfStructures.begin(); it != arraysOfStructures.end(); ++it) { const KisMetaData::Schema* schema = it.key(); for (QMap > >::iterator it2 = it.value().begin(); it2 != it.value().end(); ++it2) { KisMetaData::Value::ValueType type = KisMetaData::Value::OrderedArray; QString entryName = it2.key(); if (schema->propertyType(entryName)) { switch (schema->propertyType(entryName)->propertyType()) { case KisMetaData::TypeInfo::OrderedArrayType: type = KisMetaData::Value::OrderedArray; break; case KisMetaData::TypeInfo::UnorderedArrayType: type = KisMetaData::Value::OrderedArray; break; case KisMetaData::TypeInfo::AlternativeArrayType: type = KisMetaData::Value::AlternativeArray; break; default: type = KisMetaData::Value::Invalid; break; } } else if (store->containsEntry(schema, entryName)) { KisMetaData::Value value = store->getEntry(schema, entryName).value(); if (value.isArray()) { type = value.type(); } } store->removeEntry(schema, entryName); if (type != KisMetaData::Value::Invalid) { QList< KisMetaData::Value > valueList; for (int i = 0; i < it2.value().size(); ++i) { valueList.append(it2.value()[i]); } store->addEntry(KisMetaData::Entry(schema, entryName, KisMetaData::Value(valueList, type))); } } } return true; } diff --git a/krita/ui/opengl/kis_opengl_shader.cpp b/krita/ui/opengl/kis_opengl_shader.cpp index a6febe96153..9087c81693b 100644 --- a/krita/ui/opengl/kis_opengl_shader.cpp +++ b/krita/ui/opengl/kis_opengl_shader.cpp @@ -1,161 +1,161 @@ /* * Copyright (c) 2007 Adrian Page * Copyright (c) 2008 Tom Burdick * * 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 * MERCHANTABILITY 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 "opengl/kis_opengl_shader.h" #include #include #include #include #include "kis_factory2.h" #include "kis_debug.h" KisOpenGLShader::KisOpenGLShader(GLenum shaderType) { m_valid = false; KIS_OPENGL_CLEAR_ERROR(); m_shader = glCreateShader(shaderType); KIS_OPENGL_PRINT_ERROR(); if (m_shader == 0) { dbgUI << "Failed to create shader"; } } KisOpenGLShader::~KisOpenGLShader() { if (m_shader != 0) { KIS_OPENGL_CLEAR_ERROR(); glDeleteShader(m_shader); KIS_OPENGL_PRINT_ERROR(); } } void KisOpenGLShader::loadSourceCodeFromCStrings(GLsizei numSourceCodeStrings, const GLchar **sourceCodeStrings, const GLint *stringLengths) { if (m_shader != 0) { KIS_OPENGL_CLEAR_ERROR(); glShaderSource(m_shader, numSourceCodeStrings, sourceCodeStrings, stringLengths); KIS_OPENGL_PRINT_ERROR(); glCompileShader(m_shader); KIS_OPENGL_PRINT_ERROR(); GLint compiled; glGetShaderiv(m_shader, GL_COMPILE_STATUS, &compiled); KIS_OPENGL_PRINT_ERROR(); if (compiled) { m_valid = true; } else { dbgUI << "Failed to compile shader"; dbgUI << "Info log:" << getInfoLog(); } } } void KisOpenGLShader::loadSourceCodeFromFile(const QString & sourceCodeFilename) { QString fullFilename = KisFactory2::componentData().dirs()->findResource("kis_shaders", sourceCodeFilename); if (fullFilename.isNull()) { dbgUI << "Failed to find shader source code file:" << sourceCodeFilename; return; } QFile sourceCodeFile(fullFilename); if (!sourceCodeFile.open(QIODevice::ReadOnly)) { dbgUI << "Unable to open shader source code file:" << fullFilename; return; } QTextStream sourceCodeStream(&sourceCodeFile); QVector sourceCodeStringList; while (!sourceCodeStream.atEnd()) { sourceCodeStringList.append(sourceCodeStream.readLine().toLatin1()); } QVector sourceCodeStrings; foreach(const QByteArray &sourceString, sourceCodeStringList) { sourceCodeStrings.append(sourceString.constData()); } if (sourceCodeStrings.isEmpty()) { dbgUI << "Shader source code file is empty:" << fullFilename; return; } loadSourceCodeFromCStrings(sourceCodeStrings.count(), &(sourceCodeStrings[0]), NULL); } void KisOpenGLShader::loadSourceCodeFromQString(QString sourceCodeString) { - QByteArray string = sourceCodeString.toAscii(); + QByteArray string = sourceCodeString.toLatin1(); if (string.length() == 0) { dbgUI << "Shader source code vector is empty"; return; } GLint length = string.length(); GLsizei size = 1; const char* cstring = string.constData(); loadSourceCodeFromCStrings(size, &(cstring), &length); } bool KisOpenGLShader::isValid() const { return m_valid; } GLuint KisOpenGLShader::handle() const { return m_shader; } QString KisOpenGLShader::getInfoLog() { GLint infoLogLength; QString infoLog; KIS_OPENGL_CLEAR_ERROR(); glGetShaderiv(m_shader, GL_INFO_LOG_LENGTH, &infoLogLength); KIS_OPENGL_PRINT_ERROR(); if (infoLogLength > 0) { GLchar *infoLogBuffer = new GLchar[infoLogLength]; Q_CHECK_PTR(infoLogBuffer); glGetShaderInfoLog(m_shader, infoLogLength, NULL, infoLogBuffer); KIS_OPENGL_PRINT_ERROR(); infoLog = infoLogBuffer; delete [] infoLogBuffer; } return infoLog; } diff --git a/libs/flake/KoUnavailShape.cpp b/libs/flake/KoUnavailShape.cpp index a15f369a009..ba726879567 100644 --- a/libs/flake/KoUnavailShape.cpp +++ b/libs/flake/KoUnavailShape.cpp @@ -1,658 +1,658 @@ /* This file is part of the KDE project * * Copyright (C) 2010-2011 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 "KoUnavailShape.h" // Qt #include #include #include #include #include #include #include #include // KDE #include #include // Calligra #include "KoUnit.h" #include "KoStore.h" #include "KoXmlNS.h" #include "KoXmlReader.h" #include #include #include #include #include #include #include #include "KoShapeContainerDefaultModel.h" #include "KoShapeRegistry.h" #include // The XML of a frame looks something like this: // // 1. // 2. // 3. // 4. // // or // // 1. // 2. ...inline xml here... // 3. // 4. // // We define each Xml statement on lines 2 and 3 above as an "object". // (Strictly only the first child element is an object in the ODF sense, // but we have to have some terminology here.) // // In an ODF frame, only the first line, i.e. the first object // contains the real contents. All the rest of the objects are used / // shown if we cannot handle the first one. The most common cases are // that there is only one object inside the frame OR that there are 2 // and the 2nd is a picture. // // Sometimes, e.g. in the case of an embedded document, the reference // points not to a file but to a directory structure inside the ODF // store. // // When we load and save in the UnavailShape, we have to be general // enough to cover all possible cases of references and inline XML, // embedded files and embedded directory structures. // // We also have to be careful because we cannot reuse the object names // that are in the original files when saving. Instead we need to // create new object names because the ones that were used in the // original file may already be used by other embedded files/objects // that are saved by other shapes. // // FIXME: There should only be ONE place where new object / file names // are generated, not 2(?) like there are now: // KoEmbeddedDocumentSaver and the KoImageCollection. // // An ObjectEntry is used to store information about objects in the // frame, as defined above. struct ObjectEntry { QByteArray objectXmlContents; // the XML tree in the object QString objectName; // object name in the frame without "./" // This is extracted from objectXmlContents. bool isDir; KoOdfManifestEntry *manifestEntry; // manifest entry for the above. }; // A FileEntry is used to store information about embedded files // inside (i.e. referred to by) an object. struct FileEntry { QString path; // Normalized filename, i.e. without "./". QString mimeType; bool isDir; QByteArray contents; }; class KoUnavailShape::Private { public: Private(KoUnavailShape* qq); ~Private(); void draw(QPainter &painter) const; void drawNull(QPainter &painter) const; void storeObjects(const KoXmlElement &element); void storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer, ObjectEntry *object, QHash &unknownNamespaces); void storeFile(const QString &filename, KoShapeLoadingContext &context); QByteArray loadFile(const QString &filename, KoShapeLoadingContext &context); // Objects inside the frame. For each file, we store: // - The XML code for the object // - Any embedded files (names, contents) that are referenced by xlink:href // - Whether they are directories, i.e. if they contain a file tree and not just one file. // - The manifest entries QList objectEntries; // Embedded files QList embeddedFiles; // List of embedded files. // Some cached values. QPixmap questionMark; QPixmap pixmapPreview; QSvgRenderer *scalablePreview; KoUnavailShape* q; }; KoUnavailShape::Private::Private(KoUnavailShape* qq) : scalablePreview(new QSvgRenderer()) , q(qq) { // Get the question mark "icon". questionMark.load(KStandardDirs::locate("data", "calligra/icons/questionmark.png")); } KoUnavailShape::Private::~Private() { qDeleteAll(objectEntries); qDeleteAll(embeddedFiles); // It's a QObject, but we haven't parented it. delete(scalablePreview); } // ---------------------------------------------------------------- // The main class KoUnavailShape::KoUnavailShape() : KoFrameShape( "", "" ) , KoShapeContainer(new KoShapeContainerDefaultModel()) , d(new Private(this)) { setShapeId(KoUnavailShape_SHAPEID); // Default size of the shape. KoShape::setSize( QSizeF( CM_TO_POINT( 5 ), CM_TO_POINT( 3 ) ) ); } KoUnavailShape::~KoUnavailShape() { delete d; } void KoUnavailShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { applyConversion(painter, converter); // If the frame is empty, just draw a background. kDebug(30006) << "Number of objects:" << d->objectEntries.size(); if (d->objectEntries.isEmpty()) { // But... only try to draw the background if there's one such if (background()) { QPainterPath p; p.addRect(QRectF(QPointF(), size())); background()->paint(painter, converter, paintContext, p); } } else { if(shapes().isEmpty()) { d->draw(painter); } } } void KoUnavailShape::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &) { Q_UNUSED(painter); Q_UNUSED(converter); } void KoUnavailShape::Private::draw(QPainter &painter) const { painter.save(); painter.setRenderHint(QPainter::Antialiasing); // Run through the previews in order of preference. Draw a placeholder // questionmark if there is no preview available for rendering. if (scalablePreview->isValid()) { QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height()); scalablePreview->render(&painter, bounds); } else if (!pixmapPreview.isNull()) { QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height()); painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.drawPixmap(bounds, pixmapPreview); } else if (q->shapes().isEmpty()) { // Draw a nice question mark with a frame around it if there // is no other preview image. If there is a contained image // shape, we don't need to draw anything. // Get the question mark "icon". // FIXME: We should be able to use d->questionMark here. QPixmap questionMark; questionMark.load(KStandardDirs::locate("data", "calligra/icons/questionmark.png")); // The size of the image is: // - the size of the shape if shapesize < 2cm // - 2 cm if 2cm <= shapesize <= 8cm // - shapesize / 4 if shapesize > 8cm qreal width = q->size().width(); qreal height = q->size().height(); qreal picSize = CM_TO_POINT(2); // Default size is 2 cm. if (width < CM_TO_POINT(2) || height < CM_TO_POINT(2)) picSize = qMin(width, height); else if (width > CM_TO_POINT(8) && height > CM_TO_POINT(8)) picSize = qMin(width, height) / qreal(4.0); painter.drawPixmap((width - picSize) / qreal(2.0), (height - picSize) / qreal(2.0), picSize, picSize, questionMark); // Draw a gray rectangle around the shape. painter.setPen(QPen(QColor(172, 196, 206))); painter.drawRect(QRectF(QPointF(0,0), q->size())); } painter.restore(); } void KoUnavailShape::Private::drawNull(QPainter &painter) const { QRectF rect(QPointF(0,0), q->size()); painter.save(); // Draw a simple cross in a rectangle just to indicate that there is something here. painter.drawLine(rect.topLeft(), rect.bottomRight()); painter.drawLine(rect.bottomLeft(), rect.topRight()); painter.restore(); } // ---------------------------------------------------------------- // Loading and Saving void KoUnavailShape::saveOdf(KoShapeSavingContext & context) const { kDebug(30006) << "START SAVING ##################################################"; KoEmbeddedDocumentSaver &fileSaver = context.embeddedSaver(); KoXmlWriter &writer = context.xmlWriter(); writer.startElement("draw:frame"); // See also loadOdf() in loadOdfAttributes. saveOdfAttributes( context, OdfAllAttributes ); // Write the stored XML to the file, but don't reuse object names. int lap = 0; QString newName; foreach (const ObjectEntry *object, d->objectEntries) { QByteArray xmlArray(object->objectXmlContents); QString objectName(object->objectName); // Possibly empty. KoOdfManifestEntry *manifestEntry(object->manifestEntry); // Create a name for this object. If this is not the first // object, i.e. a replacement object (most likely a picture), // then reuse the name but put it in ReplacementObjects. if (++lap == 1) { // The first lap in the loop is the actual object. All // other laps are replacement objects. newName = fileSaver.getFilename("Object "); } else if (lap == 2) { newName = "ObjectReplacements/" + newName; } else // FIXME: what should replacement 2 and onwards be called? newName = newName + "_"; // If there was a previous object name, replace it with the new one. if (!objectName.isEmpty() && manifestEntry) { // FIXME: We must make a copy of the byte array here because // otherwise we won't be able to save > 1 time. xmlArray.replace(objectName.toLatin1(), newName.toLatin1()); } writer.addCompleteElement(xmlArray.data()); // If the objectName is empty, this may be inline XML. // If so, we are done now. if (objectName.isEmpty() || !manifestEntry) { continue; } // Save embedded files for this object. foreach (FileEntry *entry, d->embeddedFiles) { QString fileName(entry->path); // If we found a file for this object, we need to write it // but with the new object name instead of the old one. if (!fileName.startsWith(objectName)) continue; kDebug(30006) << "Object name: " << objectName << "newName: " << newName << "filename: " << fileName << "isDir: " << entry->isDir; fileName.replace(objectName, newName); fileName.prepend("./"); kDebug(30006) << "New filename: " << fileName; // FIXME: Check if we need special treatment of directories. fileSaver.saveFile(fileName, entry->mimeType.toLatin1(), entry->contents); } // Write the manifest entry for the object itself. If it's a // file, the manifest is already written by saveFile, so skip // it here. if (object->isDir) { fileSaver.saveManifestEntry(newName + '/', manifestEntry->mediaType(), manifestEntry->version()); } } writer.endElement(); // draw:frame } bool KoUnavailShape::loadOdf(const KoXmlElement &frameElement, KoShapeLoadingContext &context) { kDebug(30006) << "START LOADING ##################################################"; //kDebug(30006) << "Loading ODF frame in the KoUnavailShape. Element = " // << frameElement.tagName(); loadOdfAttributes(frameElement, context, OdfAllAttributes); // NOTE: We cannot use loadOdfFrame() because we want to save all // the things inside the frame, not just one of them, like // loadOdfFrame() provides. // Get the manifest. QList manifest = context.odfLoadingContext().manifestEntries(); #if 0 // Enable to show all manifest entries. kDebug(30006) << "MANIFEST: "; foreach (KoOdfManifestEntry *entry, manifest) { kDebug(30006) << entry->mediaType() << entry->fullPath() << entry->version(); } #endif // 1. Get the XML contents of the objects from the draw:frame. As // a side effect, this extracts the object names from all // xlink:href and stores them into d->objectNames. The saved // xml contents itself is saved into d->objectXmlContents // (QByteArray) so we can save it back from saveOdf(). d->storeObjects(frameElement); #if 1 // Debug only kDebug(30006) << "----------------------------------------------------------------"; kDebug(30006) << "After storeObjects():"; foreach (ObjectEntry *object, d->objectEntries) { kDebug(30006) << "objectXmlContents: " << object->objectXmlContents << "objectName: " << object->objectName; // Note: at this point, isDir and manifestEntry are not set. #endif } // 2. Loop through the objects that were found in the frame and // save all the files associated with them. Some of the // objects are files, and some are directories. The // directories are searched and the files within are saved as // well. // // In this loop, isDir and manifestEntry of each ObjectEntry are set. bool foundPreview = false; foreach (ObjectEntry *object, d->objectEntries) { QString objectName = object->objectName; if (objectName.isEmpty()) continue; kDebug(30006) << "Storing files for object named:" << objectName; // Try to find out if the entry is a directory. // If the object is a directory, then save all the files // inside it, otherwise save the file as it is. QString dirName = objectName + '/'; bool isDir = !context.odfLoadingContext().mimeTypeForPath(dirName).isEmpty(); if (isDir) { // A directory: the files can be found in the manifest. foreach (KoOdfManifestEntry *entry, manifest) { if (entry->fullPath() == dirName) continue; if (entry->fullPath().startsWith(dirName)) { d->storeFile(entry->fullPath(), context); } } } else { // A file: save it. d->storeFile(objectName, context); } // Get the manifest entry for this object. KoOdfManifestEntry *entry = 0; QString entryName = isDir ? dirName : objectName; for (int j = 0; j < manifest.size(); ++j) { KoOdfManifestEntry *temp = manifest.value(j); if (temp->fullPath() == entryName) { entry = new KoOdfManifestEntry(*temp); break; } } object->isDir = isDir; object->manifestEntry = entry; // If we have not already found a preview in previous times // through the loop, then see if this one may be a preview. if (!foundPreview) { kDebug(30006) << "Attempting to load preview from " << objectName; QByteArray previewData = d->loadFile(objectName, context); // Check to see if we know the mimetype for this entry. Specifically: // 1. Check to see if the item is a loadable SVG file // FIXME: Perhaps check in the manifest first? But this // seems to work well. d->scalablePreview->load(previewData); if (d->scalablePreview->isValid()) { kDebug(30006) << "Found scalable preview image!"; d->scalablePreview->setViewBox(d->scalablePreview->boundsOnElement("svg")); foundPreview = true; continue; } // 2. Otherwise check to see if it's a loadable pixmap file d->pixmapPreview.loadFromData(previewData); if (!d->pixmapPreview.isNull()) { kDebug(30006) << "Found pixel based preview image!"; foundPreview = true; } } } #if 0 // Enable to get more detailed debug messages kDebug(30006) << "Object manifest entries:"; for (int i = 0; i < d->manifestEntries.size(); ++i) { KoOdfManifestEntry *entry = d->manifestEntries.value(i); kDebug(30006) << i << ":" << entry; if (entry) kDebug(30006) << entry->fullPath() << entry->mediaType() << entry->version(); else kDebug(30006) << "--"; } kDebug(30006) << "END LOADING ####################################################"; #endif return true; } // Load the actual contents inside the frame. bool KoUnavailShape::loadOdfFrameElement(const KoXmlElement & /*element*/, KoShapeLoadingContext &/*context*/) { return true; } // ---------------------------------------------------------------- // Private functions void KoUnavailShape::Private::storeObjects(const KoXmlElement &element) { // Loop through all the child elements of the draw:frame and save them. KoXmlNode n = element.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { kDebug(30006) << "In draw:frame, node =" << n.nodeName(); // This disregards #text, but that's not in the spec anyway so // it doesn't need to be saved. if (!n.isElement()) continue; KoXmlElement el = n.toElement(); ObjectEntry *object = new ObjectEntry; QByteArray contentsTmp; QBuffer buffer(&contentsTmp); // the member KoXmlWriter writer(&buffer); // 1. Find out the objectName // Save the normalized filename, i.e. without a starting "./". // An empty string is saved if no name is found. QString name = el.attributeNS(KoXmlNS::xlink, "href", QString()); if (name.startsWith("./")) name = name.mid(2); object->objectName = name; // 2. Copy the XML code. QHash unknownNamespaces; storeXmlRecursive(el, writer, object, unknownNamespaces); object->objectXmlContents = contentsTmp; // 3, 4: the isDir and manifestEntry members are not set here, // but initialize them anyway. . object->isDir = false; // Has to be initialized to something. object->manifestEntry = 0; objectEntries.append(object); } } void KoUnavailShape::Private::storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer, ObjectEntry *object, QHash &unknownNamespaces) { // Start the element; // keep the name in a QByteArray so that it stays valid until end element is called. - const QByteArray name(el.nodeName().toAscii()); + const QByteArray name(el.nodeName().toLatin1()); writer.startElement(name.constData()); // Copy all the attributes, including namespaces. QList< QPair > attributeNames = el.attributeFullNames(); for (int i = 0; i < attributeNames.size(); ++i) { QPair attrPair(attributeNames.value(i)); if (attrPair.first.isEmpty()) { - writer.addAttribute(attrPair.second.toAscii(), el.attribute(attrPair.second)); + writer.addAttribute(attrPair.second.toLatin1(), el.attribute(attrPair.second)); } else { // This somewhat convoluted code is because we need the // namespace, not the namespace URI. - QString nsShort = KoXmlNS::nsURI2NS(attrPair.first.toAscii()); + QString nsShort = KoXmlNS::nsURI2NS(attrPair.first.toLatin1()); // in case we don't find the namespace in our list create a own one and use that // so the document created on saving is valid. if (nsShort.isEmpty()) { nsShort = unknownNamespaces.value(attrPair.first); if (nsShort.isEmpty()) { nsShort = QString("ns%1").arg(unknownNamespaces.size() + 1); unknownNamespaces.insert(attrPair.first, nsShort); } - writer.addAttribute("xmlns:" + nsShort.toAscii(), attrPair.first); + writer.addAttribute("xmlns:" + nsShort.toLatin1(), attrPair.first); } QString attr(nsShort + ':' + attrPair.second); - writer.addAttribute(attr.toAscii(), el.attributeNS(attrPair.first, + writer.addAttribute(attr.toLatin1(), el.attributeNS(attrPair.first, attrPair.second)); } } // Child elements // Loop through all the child elements of the draw:frame. KoXmlNode n = el.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (n.isElement()) { storeXmlRecursive(n.toElement(), writer, object, unknownNamespaces); } else if (n.isText()) { writer.addTextNode(n.toText().data()/*.toUtf8()*/); } } // End the element writer.endElement(); } /** * This function stores the embedded file in an internal store - it does not save files to disk, * and thus it is named in this manner, to avoid the function being confused with functions which * save files to disk. */ void KoUnavailShape::Private::storeFile(const QString &fileName, KoShapeLoadingContext &context) { kDebug(30006) << "Saving file: " << fileName; // Directories need to be saved too, but they don't have any file contents. if (fileName.endsWith('/')) { FileEntry *entry = new FileEntry; entry->path = fileName; entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path); entry->isDir = true; embeddedFiles.append(entry); } QByteArray fileContent = loadFile(fileName, context); if (fileContent.isNull()) return; // Actually store the file in the list. FileEntry *entry = new FileEntry; entry->path = fileName; if (entry->path.startsWith("./")) entry->path = entry->path.mid(2); entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path); entry->isDir = false; entry->contents = fileContent; embeddedFiles.append(entry); kDebug(30006) << "File length: " << fileContent.size(); } QByteArray KoUnavailShape::Private::loadFile(const QString &fileName, KoShapeLoadingContext &context) { // Can't load a file which is a directory, return an invalid QByteArray if (fileName.endsWith('/')) return QByteArray(); KoStore *store = context.odfLoadingContext().store(); QByteArray fileContent; if (!store->open(fileName)) { store->close(); return QByteArray(); } int fileSize = store->size(); fileContent = store->read(fileSize); store->close(); //kDebug(30006) << "File content: " << fileContent; return fileContent; } diff --git a/libs/kotext/opendocument/KoTextWriter_p.cpp b/libs/kotext/opendocument/KoTextWriter_p.cpp index c3025229472..dd80362c606 100644 --- a/libs/kotext/opendocument/KoTextWriter_p.cpp +++ b/libs/kotext/opendocument/KoTextWriter_p.cpp @@ -1,2501 +1,2501 @@ /* * Copyright (c) 2010 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "KoTextWriter_p.h" #include #include // A convenience function to get a listId from a list-format static KoListStyle::ListIdType ListId(const QTextListFormat &format) { KoListStyle::ListIdType listId; if (sizeof(KoListStyle::ListIdType) == sizeof(uint)) listId = format.property(KoListStyle::ListId).toUInt(); else listId = format.property(KoListStyle::ListId).toULongLong(); return listId; } KoTextWriter::Private::Private(KoShapeSavingContext &context) : rdfData(0) , sharedData(0) , styleManager(0) , changeTracker(0) , document(0) , writer(0) , context(context) , splitEndBlockNumber(-1) , splitRegionOpened(false) , splitIdCounter(1) , deleteMergeRegionOpened(false) , deleteMergeEndBlockNumber(-1) { currentPairedInlineObjectsStack = new QStack(); writer = &context.xmlWriter(); changeStack.push(0); } void KoTextWriter::Private::writeBlocks(QTextDocument *document, int from, int to, QHash &listStyles, QTextTable *currentTable, QTextList *currentList) { pairedInlineObjectsStackStack.push(currentPairedInlineObjectsStack); currentPairedInlineObjectsStack = new QStack(); QTextBlock block = document->findBlock(from); int sectionLevel = 0; while (block.isValid() && ((to == -1) || (block.position() <= to))) { QTextCursor cursor(block); int frameType = cursor.currentFrame()->format().intProperty(KoText::SubFrameType); if (frameType == KoText::AuxillaryFrameType) { break; // we've reached the "end" (end/footnotes saved by themselves) // note how NoteFrameType passes through here so the notes can // call writeBlocks to save their contents. } QTextBlockFormat format = block.blockFormat(); if (format.hasProperty(KoParagraphStyle::SectionStartings)) { QVariant v = format.property(KoParagraphStyle::SectionStartings); QList sectionStarts = v.value >(); foreach (QVariant sv, sectionStarts) { KoSection* section = (KoSection*)(sv.value()); if (section) { ++sectionLevel; section->saveOdf(context); } } } if (format.hasProperty(KoParagraphStyle::HiddenByTable)) { block = block.next(); continue; } if (format.hasProperty(KoParagraphStyle::TableOfContentsData)) { saveTableOfContents(document, listStyles, block); block = block.next(); continue; } if (format.hasProperty(KoParagraphStyle::BibliographyData)) { saveBibliography(document, listStyles, block); block = block.next(); continue; } int blockOutlineLevel = format.property(KoParagraphStyle::OutlineLevel).toInt(); if (cursor.currentTable() && cursor.currentTable() != currentTable) { // Call the code to save the table.... saveTable(cursor.currentTable(), listStyles); // We skip to the end of the table. block = cursor.currentTable()->lastCursorPosition().block(); block = block.next(); continue; } if (cursor.currentList() && cursor.currentList() != currentList) { int previousBlockNumber = block.blockNumber(); block = saveList(block, listStyles, 1, currentTable); int blockNumberToProcess = block.blockNumber(); if (blockNumberToProcess != previousBlockNumber) continue; } if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if (!deleteMergeRegionOpened && !cursor.currentTable() && (!cursor.currentList() || blockOutlineLevel)) { deleteMergeEndBlockNumber = checkForDeleteMerge(block); if (deleteMergeEndBlockNumber != -1) { deleteMergeRegionOpened = true; openSplitMergeRegion(); } } } saveParagraph(block, from, to); if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if (deleteMergeRegionOpened && (block.blockNumber() == deleteMergeEndBlockNumber) && (!cursor.currentList() || blockOutlineLevel)) { closeSplitMergeRegion(); deleteMergeRegionOpened = false; deleteMergeEndBlockNumber = -1; postProcessDeleteMergeXml(); } } if (format.hasProperty(KoParagraphStyle::SectionEndings)) { QVariant v = format.property(KoParagraphStyle::SectionEndings); QList sectionEndings = v.value >(); KoSectionEnd sectionEnd; foreach (QVariant sv, sectionEndings) { if (sectionLevel >= 1) { --sectionLevel; sectionEnd.saveOdf(context); } } } block = block.next(); } // while while (sectionLevel >= 1) { --sectionLevel; KoSectionEnd sectionEnd; sectionEnd.saveOdf(context); } Q_ASSERT(!pairedInlineObjectsStackStack.isEmpty()); delete currentPairedInlineObjectsStack; currentPairedInlineObjectsStack = pairedInlineObjectsStackStack.pop(); } QHash KoTextWriter::Private::saveListStyles(QTextBlock block, int to) { QHash generatedLists; QHash listStyles; for (;block.isValid() && ((to == -1) || (block.position() < to)); block = block.next()) { QTextList *textList = block.textList(); if (!textList) continue; KoListStyle::ListIdType listId = ListId(textList->format()); if (KoList *list = KoTextDocument(document).list(listId)) { if (generatedLists.contains(list)) { if (!listStyles.contains(textList)) listStyles.insert(textList, generatedLists.value(list)); continue; } KoListStyle *listStyle = list->style(); if (listStyle && listStyle->isOulineStyle()) { continue; } bool automatic = listStyle->styleId() == 0; KoGenStyle style(automatic ? KoGenStyle::ListAutoStyle : KoGenStyle::ListStyle); if (automatic && context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) style.setAutoStyleInStylesDotXml(true); listStyle->saveOdf(style, context); QString generatedName = context.mainStyles().insert(style, listStyle->name(), listStyle->isNumberingStyle() ? KoGenStyles::AllowDuplicates : KoGenStyles::DontAddNumberToName); listStyles[textList] = generatedName; generatedLists.insert(list, generatedName); } else { if (listStyles.contains(textList)) continue; KoListLevelProperties llp = KoListLevelProperties::fromTextList(textList); KoGenStyle style(KoGenStyle::ListAutoStyle); if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) style.setAutoStyleInStylesDotXml(true); KoListStyle listStyle; listStyle.setLevelProperties(llp); if (listStyle.isOulineStyle()) { continue; } listStyle.saveOdf(style, context); QString generatedName = context.mainStyles().insert(style, listStyle.name()); listStyles[textList] = generatedName; } } return listStyles; } void KoTextWriter::Private::saveAllChanges() { if (!changeTracker) return; changeTransTable = changeTracker->saveInlineChanges(changeTransTable, sharedData->genChanges()); } //---------------------------- PRIVATE ----------------------------------------------------------- void KoTextWriter::Private::saveODF12Change(QTextCharFormat format) { if (!changeTracker /*&& changeTracker->isEnabled()*/) return;//The change tracker exist and we are allowed to save tracked changes int changeId = format.property(KoCharacterStyle::ChangeTrackerId).toInt(); //First we need to check if the eventual already opened change regions are still valid while (int change = changeStack.top()) { if (!changeId || !changeTracker->isParent(change, changeId)) { writer->startElement("text:change-end", false); writer->addAttribute("text:change-id", changeTransTable.value(change)); writer->endElement(); changeStack.pop(); } } if (changeId) { //There is a tracked change if (changeTracker->elementById(changeId)->getChangeType() != KoGenChange::DeleteChange) { //Now start a new change region if not already done if (!changeStack.contains(changeId)) { QString changeName = changeTransTable.value(changeId); writer->startElement("text:change-start", false); writer->addAttribute("text:change-id",changeName); writer->endElement(); changeStack.push(changeId); } } } KoInlineTextObjectManager *textObjectManager = KoTextDocument(document).inlineTextObjectManager(); KoDeleteChangeMarker *changeMarker; if (textObjectManager && (changeMarker = dynamic_cast(textObjectManager->inlineTextObject(format)))) { if (!savedDeleteChanges.contains(changeMarker->changeId())) { QString deleteChangeXml = generateDeleteChangeXml(changeMarker); changeMarker->setDeleteChangeXml(deleteChangeXml); changeMarker->saveOdf(context); savedDeleteChanges.append(changeMarker->changeId()); } } } QString KoTextWriter::Private::generateDeleteChangeXml(KoDeleteChangeMarker *marker) { if (!changeTracker) return QString(); //Create a QTextDocument from the Delete Fragment QTextDocument doc; QTextCursor cursor(&doc); cursor.insertFragment(changeTracker->elementById(marker->changeId())->getDeleteData()); //Save the current writer KoXmlWriter &oldWriter = context.xmlWriter(); //Create a new KoXmlWriter pointing to a QBuffer QByteArray xmlArray; QBuffer xmlBuffer(&xmlArray); KoXmlWriter newXmlWriter(&xmlBuffer); //Set our xmlWriter as the writer to be used writer = &newXmlWriter; context.setXmlWriter(newXmlWriter); //Call writeBlocks to generate the xml QHash listStyles = saveListStyles(doc.firstBlock(), doc.characterCount()); writeBlocks(&doc, 0, doc.characterCount(),listStyles); //Restore the actual xml writer writer = &oldWriter; context.setXmlWriter(oldWriter); QString generatedXmlString(xmlArray); return generatedXmlString; } int KoTextWriter::Private::openTagRegion(int position, ElementType elementType, TagInformation& tagInformation) { int changeId = 0, returnChangeId = 0; if (!changeTracker) { //kDebug(30015) << "tag:" << tagInformation.name() << openedTagStack.size(); if (tagInformation.name()) { writer->startElement(tagInformation.name(), elementType != ParagraphOrHeader); QPair attribute; foreach (attribute, tagInformation.attributes()) { writer->addAttribute(attribute.first.toLocal8Bit(), attribute.second); } } openedTagStack.push(tagInformation.name()); //kDebug(30015) << "stack" << openedTagStack.size(); return changeId; } QTextCursor cursor(document); QTextBlock block = document->findBlock(position); openedTagStack.push(tagInformation.name()); KoChangeTracker::ChangeSaveFormat changeSaveFormat = changeTracker->saveFormat(); if (changeSaveFormat == KoChangeTracker::DELTAXML) { switch (elementType) { case KoTextWriter::Private::Span: cursor.setPosition(position + 1); changeId = cursor.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt(); break; case KoTextWriter::Private::ParagraphOrHeader: changeId = checkForBlockChange(block); break; case KoTextWriter::Private::NumberedParagraph: changeId = checkForBlockChange(block); break; case KoTextWriter::Private::ListItem: changeId = checkForListItemChange(block); break; case KoTextWriter::Private::List: changeId = checkForListChange(block); break; case KoTextWriter::Private::TableRow: changeId = checkForTableRowChange(position); break; case KoTextWriter::Private::TableColumn: changeId = checkForTableColumnChange(position); break; case KoTextWriter::Private::TableCell: cursor.setPosition(position); changeId = cursor.currentTable()->cellAt(position).format().property(KoCharacterStyle::ChangeTrackerId).toInt(); break; case KoTextWriter::Private::Table: cursor.setPosition(position); QTextTableFormat tableFormat = cursor.currentTable()->format(); changeId = tableFormat.property(KoCharacterStyle::ChangeTrackerId).toInt(); break; } } if (!changeId || (changeStack.top() == changeId)) { changeId = 0; } else if ((changeTracker->isDuplicateChangeId(changeId)) && (changeTracker->originalChangeId(changeId) == changeStack.top())) { QVectorIterator changeStackIterator(changeStack); changeStackIterator.toBack(); while ((changeStackIterator.peekPrevious()) && (changeStackIterator.peekPrevious() == changeTracker->originalChangeId(changeId))) { changeStackIterator.previous(); changeId = changeTracker->parent(changeId); } } else if ((changeTracker->isDuplicateChangeId(changeId)) && (changeTracker->isParent(changeStack.top(), changeId))) { changeId = 0; } returnChangeId = changeId; //Navigate through the change history and push into a stack so that they can be processed in the reverse order (i.e starting from earliest) QStack changeHistory; while (changeId && (changeId != changeStack.top())) { changeHistory.push(changeId); changeId = changeTracker->parent(changeId); } if (returnChangeId) { changeStack.push(returnChangeId); } while (changeHistory.size()) { int changeId = changeHistory.pop(); if (changeTracker->isDuplicateChangeId(changeId)) { changeId = changeTracker->originalChangeId(changeId); } if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::DeleteChange) { writer->startElement("delta:removed-content", false); writer->addAttribute("delta:removal-change-idref", changeTransTable.value(changeId)); } else if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::InsertChange) { tagInformation.addAttribute("delta:insertion-change-idref", changeTransTable.value(changeId)); tagInformation.addAttribute("delta:insertion-type", "insert-with-content"); } else if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::FormatChange && elementType == KoTextWriter::Private::Span) { KoFormatChangeInformation *formatChangeInformation = changeTracker->formatChangeInformation(changeId); if (formatChangeInformation && formatChangeInformation->formatType() == KoFormatChangeInformation::eTextStyleChange) { writer->startElement("delta:remove-leaving-content-start", false); writer->addAttribute("delta:removal-change-idref", changeTransTable.value(changeId)); writer->addAttribute("delta:end-element-idref", QString("end%1").arg(changeId)); cursor.setPosition(position); KoTextStyleChangeInformation *textStyleChangeInformation = static_cast(formatChangeInformation); QString styleName = saveCharacterStyle(textStyleChangeInformation->previousCharFormat(), cursor.blockCharFormat()); if (!styleName.isEmpty()) { writer->startElement("text:span", false); writer->addAttribute("text:style-name", styleName); writer->endElement(); } writer->endElement(); } tagInformation.addAttribute("delta:insertion-change-idref", changeTransTable.value(changeId)); tagInformation.addAttribute("delta:insertion-type", "insert-around-content"); } else if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::FormatChange && elementType == KoTextWriter::Private::ParagraphOrHeader) { KoFormatChangeInformation *formatChangeInformation = changeTracker->formatChangeInformation(changeId); if (formatChangeInformation && formatChangeInformation->formatType() == KoFormatChangeInformation::eParagraphStyleChange) { KoParagraphStyleChangeInformation *paraStyleChangeInformation = static_cast(formatChangeInformation); QString styleName = saveParagraphStyle(paraStyleChangeInformation->previousBlockFormat(), QTextCharFormat()); QString attributeChangeRecord = changeTransTable.value(changeId) + QString(",") + QString("modify") + QString(",") + QString("text:style-name") + QString(",") + styleName; tagInformation.addAttribute("ac:change001", attributeChangeRecord); } } else if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::FormatChange && elementType == KoTextWriter::Private::ListItem) { KoFormatChangeInformation *formatChangeInformation = changeTracker->formatChangeInformation(changeId); if (formatChangeInformation && formatChangeInformation->formatType() == KoFormatChangeInformation::eListItemNumberingChange) { KoListItemNumChangeInformation *listItemChangeInfo = static_cast(formatChangeInformation); if (listItemChangeInfo->listItemNumChangeType() == KoListItemNumChangeInformation::eNumberingRestarted) { QString attributeChangeRecord = changeTransTable.value(changeId) + QString(",") + QString("insert") + QString(",") + QString("text:start-value"); tagInformation.addAttribute("ac:change001", attributeChangeRecord); } else if (listItemChangeInfo->listItemNumChangeType() == KoListItemNumChangeInformation::eRestartRemoved) { QString attributeChangeRecord = changeTransTable.value(changeId) + QString(",") + QString("remove") + QString(",") + QString("text:start-value") + QString(",") + QString::number(listItemChangeInfo->previousStartNumber()); tagInformation.addAttribute("ac:change001", attributeChangeRecord); } } } } if (tagInformation.name()) { writer->startElement(tagInformation.name(), false); const QVector > &attributeList = tagInformation.attributes(); QPair attribute; foreach(attribute, attributeList) { - writer->addAttribute(attribute.first.toAscii(), attribute.second.toAscii()); + writer->addAttribute(attribute.first.toLatin1(), attribute.second.toLatin1()); } } return returnChangeId; } void KoTextWriter::Private::closeTagRegion(int changeId) { // the tag needs to be closed even if there is no change tracking //kDebug(30015) << "stack" << openedTagStack.size(); const char *tagName = openedTagStack.pop(); //kDebug(30015) << "tag:" << tagName << openedTagStack.size(); if (tagName) { writer->endElement(); // close the tag } if (!changeTracker) return; if (changeId) changeStack.pop(); if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::DeleteChange) { writer->endElement(); //delta:removed-content } else if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::FormatChange) { KoFormatChangeInformation *formatChangeInformation = changeTracker->formatChangeInformation(changeId); if (formatChangeInformation && formatChangeInformation->formatType() == KoFormatChangeInformation::eTextStyleChange) { writer->startElement("delta:remove-leaving-content-end", false); writer->addAttribute("delta:end-element-id", QString("end%1").arg(changeId)); writer->endElement(); } } return; } QString KoTextWriter::Private::saveParagraphStyle(const QTextBlock &block) { return KoTextWriter::saveParagraphStyle(block, styleManager, context); } QString KoTextWriter::Private::saveParagraphStyle(const QTextBlockFormat &blockFormat, const QTextCharFormat &charFormat) { return KoTextWriter::saveParagraphStyle(blockFormat, charFormat, styleManager, context); } QString KoTextWriter::Private::saveCharacterStyle(const QTextCharFormat &charFormat, const QTextCharFormat &blockCharFormat) { KoCharacterStyle *defaultCharStyle = styleManager->defaultCharacterStyle(); KoCharacterStyle *originalCharStyle = styleManager->characterStyle(charFormat.intProperty(KoCharacterStyle::StyleId)); if (!originalCharStyle) originalCharStyle = defaultCharStyle; QString generatedName; QString displayName = originalCharStyle->name(); QString internalName = QString(QUrl::toPercentEncoding(displayName, "", " ")).replace('%', '_'); KoCharacterStyle *autoStyle = originalCharStyle->autoStyle(charFormat, blockCharFormat); if (autoStyle->isEmpty()) { // This is the real, unmodified character style. if (originalCharStyle != defaultCharStyle) { KoGenStyle style(KoGenStyle::TextStyle, "text"); originalCharStyle->saveOdf(style); generatedName = context.mainStyles().insert(style, internalName, KoGenStyles::DontAddNumberToName); } } else { // There are manual changes... We'll have to store them then KoGenStyle style(KoGenStyle::TextAutoStyle, "text", originalCharStyle != defaultCharStyle ? internalName : "" /*parent*/); if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) style.setAutoStyleInStylesDotXml(true); autoStyle->saveOdf(style); generatedName = context.mainStyles().insert(style, "T"); } delete autoStyle; return generatedName; } QString KoTextWriter::Private::saveTableStyle(const QTextTable& table) { KoTableStyle *originalTableStyle = styleManager->tableStyle(table.format().intProperty(KoTableStyle::StyleId)); QString generatedName; QString internalName = ""; if (originalTableStyle) { internalName = QString(QUrl::toPercentEncoding(originalTableStyle->name(), "", " ")).replace('%', '_'); } KoTableStyle tableStyle(table.format()); if ((originalTableStyle) && (*originalTableStyle == tableStyle)) { // This is the real unmodified table style KoGenStyle style(KoGenStyle::TableStyle, "table"); originalTableStyle->saveOdf(style); generatedName = context.mainStyles().insert(style, internalName, KoGenStyles::DontAddNumberToName); } else { // There are manual changes... We'll have to store them then KoGenStyle style(KoGenStyle::TableAutoStyle, "table", internalName); if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) style.setAutoStyleInStylesDotXml(true); if (originalTableStyle) tableStyle.removeDuplicates(*originalTableStyle); if (!tableStyle.isEmpty()) { tableStyle.saveOdf(style); generatedName = context.mainStyles().insert(style, "Table"); } } return generatedName; } QString KoTextWriter::Private::saveTableColumnStyle(const KoTableColumnStyle& tableColumnStyle, int columnNumber, const QString& tableStyleName) { // 26*26 columns should be enough for everyone QString columnName = QChar('A' + int(columnNumber % 26)); if (columnNumber > 25) columnName.prepend(QChar('A' + int(columnNumber/26))); QString generatedName = tableStyleName + '.' + columnName; KoGenStyle style(KoGenStyle::TableColumnAutoStyle, "table-column"); if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) style.setAutoStyleInStylesDotXml(true); tableColumnStyle.saveOdf(style); generatedName = context.mainStyles().insert(style, generatedName, KoGenStyles::DontAddNumberToName); return generatedName; } QString KoTextWriter::Private::saveTableRowStyle(const KoTableRowStyle& tableRowStyle, int rowNumber, const QString& tableStyleName) { QString generatedName = tableStyleName + '.' + QString::number(rowNumber + 1); KoGenStyle style(KoGenStyle::TableRowAutoStyle, "table-row"); if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) style.setAutoStyleInStylesDotXml(true); tableRowStyle.saveOdf(style); generatedName = context.mainStyles().insert(style, generatedName, KoGenStyles::DontAddNumberToName); return generatedName; } QString KoTextWriter::Private::saveTableCellStyle(const QTextTableCellFormat& cellFormat, int columnNumber, const QString& tableStyleName) { // 26*26 columns should be enough for everyone QString columnName = QChar('A' + int(columnNumber % 26)); if (columnNumber > 25) columnName.prepend(QChar('A' + int(columnNumber/26))); QString generatedName = tableStyleName + '.' + columnName; KoGenStyle style(KoGenStyle::TableCellAutoStyle, "table-cell"); if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) style.setAutoStyleInStylesDotXml(true); KoTableCellStyle cellStyle(cellFormat); cellStyle.saveOdf(style, context); generatedName = context.mainStyles().insert(style, generatedName); return generatedName; } void KoTextWriter::Private::saveInlineRdf(KoTextInlineRdf* rdf, TagInformation* tagInfos) { QBuffer rdfXmlData; KoXmlWriter *rdfXmlWriter = new KoXmlWriter(&rdfXmlData); rdfXmlWriter->startDocument("rdf"); rdfXmlWriter->startElement("rdf"); rdf->saveOdf(context, rdfXmlWriter); rdfXmlWriter->endElement(); rdfXmlWriter->endDocument(); KoXmlDocument *xmlReader = new KoXmlDocument; xmlReader->setContent(rdfXmlData.data(), true); KoXmlElement mainElement = xmlReader->documentElement(); QPair attributeNameNS; foreach (attributeNameNS, mainElement.attributeFullNames()) { QString attributeName = QString("%1:%2").arg(KoXmlNS::nsURI2NS(attributeNameNS.first)) .arg(attributeNameNS.second); if (attributeName.startsWith(':')) attributeName.prepend("xml"); tagInfos->addAttribute(attributeName, mainElement.attribute(attributeNameNS.second)); } delete(rdfXmlWriter); } /* Note on saving textranges: Start and end tags of textranges can appear on cursor positions in a text block. in front of the first text element, between the elements, or behind the last. A textblock is composed of no, one or many text fragments. If there is no fragment at all, the only possible cursor position is 0 (relative to the begin of the block). Example: ([] marks a block, {} a fragment) Three blocks, first with text fragments {AB} {C}, second empty, last with {DEF}. Possible positions are: [|{A|B}|{C}|] [|] [|{D|E|F}|] Start tags are ideally written in front of the content they are tagging, not behind the previous content. That way tags which are at the very begin of the complete document do not need special handling. End tags are ideally written directly behind the content, and not in front of the next content. That way end tags which are at the end of the complete document do not need special handling. Next there is the case of start tags which are at the final position of a text block: the content they belong to includes the block end/border, so they need to be written at the place of the last position. Then there is the case of end tags at the first position of a text block: the content they belong to includes the block start/border, so they need to be written at the place of the first position. Example: (< marks a start tag, > marks an end tag) [|>{}|{}|<] [|><] [|>{||}|<] */ void KoTextWriter::Private::saveParagraph(const QTextBlock &block, int from, int to) { QTextCursor cursor(block); QTextBlockFormat blockFormat = block.blockFormat(); const int outlineLevel = blockFormat.intProperty(KoParagraphStyle::OutlineLevel); TagInformation blockTagInformation; if (outlineLevel > 0) { blockTagInformation.setTagName("text:h"); blockTagInformation.addAttribute("text:outline-level", outlineLevel); if (blockFormat.boolProperty(KoParagraphStyle::IsListHeader) || blockFormat.boolProperty(KoParagraphStyle::UnnumberedListItem)) { blockTagInformation.addAttribute("text:is-list-header", "true"); } } else { blockTagInformation.setTagName("text:p"); } int changeId = openTagRegion(block.position(), KoTextWriter::Private::ParagraphOrHeader, blockTagInformation); if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if (!deleteMergeRegionOpened && !splitRegionOpened && !cursor.currentTable() && (!cursor.currentList() || outlineLevel)) { splitEndBlockNumber = checkForSplit(block); if (splitEndBlockNumber != -1) { splitRegionOpened = true; QString splitId = QString("split") + QString::number(splitIdCounter); writer->addAttribute("split:split001-idref", splitId); } } if (splitRegionOpened && (block.blockNumber() == splitEndBlockNumber)) { splitRegionOpened = false; splitEndBlockNumber = -1; QString splitId = QString("split") + QString::number(splitIdCounter); writer->addAttribute("delta:split-id", splitId); int changeId = block.blockFormat().intProperty(KoCharacterStyle::ChangeTrackerId); writer->addAttribute("delta:insertion-change-idref", changeTransTable.value(changeId)); writer->addAttribute("delta:insertion-type", "split"); splitIdCounter++; } } QString styleName = saveParagraphStyle(block); if (!styleName.isEmpty()) writer->addAttribute("text:style-name", styleName); KoElementReference xmlid; xmlid.invalidate(); if (const KoTextBlockData *blockData = dynamic_cast(block.userData())) { if (blockData->saveXmlID()) { xmlid = context.xmlid(blockData); xmlid.saveOdf(writer, KoElementReference::TextId); } } if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { QTextBlock previousBlock = block.previous(); if (previousBlock.isValid()) { QTextBlockFormat blockFormat = block.blockFormat(); int changeId = blockFormat.intProperty(KoCharacterStyle::ChangeTrackerId); if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::DeleteChange) { QTextFragment firstFragment = (block.begin()).fragment(); QTextCharFormat firstFragmentFormat = firstFragment.charFormat(); int firstFragmentChangeId = firstFragmentFormat.intProperty(KoCharacterStyle::ChangeTrackerId); if (changeTracker->isDuplicateChangeId(firstFragmentChangeId)) { firstFragmentChangeId = changeTracker->originalChangeId(firstFragmentChangeId); } if (firstFragmentChangeId != changeId) { QString outputXml(""); writer->addCompleteElement(outputXml.toUtf8()); } } } } // Write the fragments and their formats QTextCharFormat blockCharFormat = cursor.blockCharFormat(); QTextCharFormat previousCharFormat; QTextBlock::iterator it; if (KoTextInlineRdf* inlineRdf = KoTextInlineRdf::tryToGetInlineRdf(blockCharFormat)) { // Write xml:id here for Rdf kDebug(30015) << "have inline rdf xmlid:" << inlineRdf->xmlId() << "active xml id" << xmlid.toString(); inlineRdf->saveOdf(context, writer, xmlid); } const KoTextRangeManager *mgr = KoTextDocument(block.document()).textRangeManager(); // write tags for ranges which end at the first position of the block const QHash endingTextRangesAtStart = mgr->textRangesChangingWithin(block.position(), block.position(), globalFrom, globalTo); foreach (const KoTextRange *range, endingTextRangesAtStart) { range->saveOdf(context, block.position(), KoTextRange::EndTag); } QString previousFragmentLink; int linkTagChangeId = -1; // stores the end position of the last fragment, is position of the block without any fragment at all int lastEndPosition = block.position(); for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment currentFragment = it.fragment(); const int fragmentStart = currentFragment.position(); const int fragmentEnd = fragmentStart + currentFragment.length(); if (to != -1 && fragmentStart >= to) break; if (currentFragment.isValid()) { QTextCharFormat charFormat = currentFragment.charFormat(); QTextCharFormat compFormat = charFormat; previousCharFormat.clearProperty(KoCharacterStyle::ChangeTrackerId); compFormat.clearProperty(KoCharacterStyle::ChangeTrackerId); if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::ODF_1_2) { saveODF12Change(charFormat); } if ((!previousFragmentLink.isEmpty()) && (charFormat.anchorHref() != previousFragmentLink || !charFormat.isAnchor())) { // Close the current text:a closeTagRegion(linkTagChangeId); previousFragmentLink = ""; } if (charFormat.isAnchor() && charFormat.anchorHref() != previousFragmentLink) { // Open a text:a previousFragmentLink = charFormat.anchorHref(); TagInformation linkTagInformation; if (charFormat.intProperty(KoCharacterStyle::AnchorType) == KoCharacterStyle::Bookmark) { linkTagInformation.setTagName("text:bookmark-ref"); QString href = previousFragmentLink.right(previousFragmentLink.size()-1); linkTagInformation.addAttribute("text:ref-name", href); //linkTagInformation.addAttribute("text:ref-format", add the style of the ref here); } else { linkTagInformation.setTagName("text:a"); linkTagInformation.addAttribute("xlink:type", "simple"); linkTagInformation.addAttribute("xlink:href", charFormat.anchorHref()); } if (KoTextInlineRdf* inlineRdf = KoTextInlineRdf::tryToGetInlineRdf(charFormat)) { // Write xml:id here for Rdf kDebug(30015) << "have inline rdf xmlid:" << inlineRdf->xmlId(); saveInlineRdf(inlineRdf, &linkTagInformation); } linkTagChangeId = openTagRegion(currentFragment.position(), KoTextWriter::Private::Span, linkTagInformation); } KoInlineTextObjectManager *textObjectManager = KoTextDocument(document).inlineTextObjectManager(); KoInlineObject *inlineObject = textObjectManager ? textObjectManager->inlineTextObject(charFormat) : 0; // If we are in an inline object if (currentFragment.length() == 1 && inlineObject && currentFragment.text()[0].unicode() == QChar::ObjectReplacementCharacter) { if (!dynamic_cast(inlineObject)) { bool saveInlineObject = true; if (KoTextMeta* z = dynamic_cast(inlineObject)) { if (z->position() < from) { // // This starts before the selection, default // to not saving it with special cases to allow saving // saveInlineObject = false; if (z->type() == KoTextMeta::StartBookmark) { if (z->endBookmark()->position() > from) { // // They have selected something starting after the // opening but before the // saveInlineObject = true; } } } } // get all text ranges which start before this inline object // or end directly after it (+1 to last position for that) const QHash textRanges = mgr->textRangesChangingWithin(currentFragment.position(), currentFragment.position()+1, globalFrom, (globalTo==-1)?-1:globalTo+1); // get all text ranges which start before this const QList textRangesBefore = textRanges.values(currentFragment.position()); // write tags for ranges which start before this content or at positioned at it foreach (const KoTextRange *range, textRangesBefore) { range->saveOdf(context, currentFragment.position(), KoTextRange::StartTag); } bool saveSpan = dynamic_cast(inlineObject) != 0; if (saveSpan) { QString styleName = saveCharacterStyle(charFormat, blockCharFormat); if (!styleName.isEmpty()) { writer->startElement("text:span", false); writer->addAttribute("text:style-name", styleName); } else { saveSpan = false; } } if (saveInlineObject) { int changeId = charFormat.intProperty(KoCharacterStyle::ChangeTrackerId); KoTextAnchor *textAnchor = dynamic_cast(inlineObject); if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if (textAnchor && changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::InsertChange) { textAnchor->shape()->setAdditionalAttribute("delta:insertion-change-idref", changeTransTable.value(changeId)); textAnchor->shape()->setAdditionalAttribute("delta:insertion-type", "insert-with-content"); } else if (textAnchor && changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::DeleteChange) { writer->startElement("delta:removed-content", false); writer->addAttribute("delta:removal-change-idref", changeTransTable.value(changeId)); } } inlineObject->saveOdf(context); if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if (textAnchor && changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::InsertChange) { textAnchor->shape()->removeAdditionalAttribute("delta:insertion-change-idref"); textAnchor->shape()->removeAdditionalAttribute("delta:insertion-type"); } else if (textAnchor && changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::DeleteChange) { writer->endElement(); } } } if (saveSpan) { writer->endElement(); } // write tags for ranges which end after this inline object const QList textRangesAfter = textRanges.values(currentFragment.position()+1); foreach (const KoTextRange *range, textRangesAfter) { range->saveOdf(context, currentFragment.position()+1, KoTextRange::EndTag); } // // Track the end marker for matched pairs so we produce valid // ODF // if (KoTextMeta* z = dynamic_cast(inlineObject)) { kDebug(30015) << "found kometa, type:" << z->type(); if (z->type() == KoTextMeta::StartBookmark) currentPairedInlineObjectsStack->push(z->endBookmark()); if (z->type() == KoTextMeta::EndBookmark && !currentPairedInlineObjectsStack->isEmpty()) currentPairedInlineObjectsStack->pop(); }/* else if (KoBookmark* z = dynamic_cast(inlineObject)) { if (z->type() == KoBookmark::StartBookmark) currentPairedInlineObjectsStack->push(z->endBookmark()); if (z->type() == KoBookmark::EndBookmark && !currentPairedInlineObjectsStack->isEmpty()) currentPairedInlineObjectsStack->pop(); }*/ } } else { // Normal block, easier to handle QString styleName = saveCharacterStyle(charFormat, blockCharFormat); TagInformation fragmentTagInformation; if (!styleName.isEmpty() /*&& !identical*/) { fragmentTagInformation.setTagName("text:span"); fragmentTagInformation.addAttribute("text:style-name", styleName); } int changeId = openTagRegion(currentFragment.position(), KoTextWriter::Private::Span, fragmentTagInformation); QString text = currentFragment.text(); int spanFrom = fragmentStart >= from ? fragmentStart : from; int spanTo = to == -1 ? fragmentEnd : (fragmentEnd > to ? to : fragmentEnd); // get all text ranges which change within this span // or end directly after it (+1 to last position to include those) const QHash textRanges = mgr->textRangesChangingWithin(spanFrom, spanTo, globalFrom, (globalTo==-1)?-1:globalTo+1); // avoid mid, if possible if (spanFrom != fragmentStart || spanTo != fragmentEnd || !textRanges.isEmpty()) { if (textRanges.isEmpty()) { writer->addTextSpan(text.mid(spanFrom - fragmentStart, spanTo - spanFrom)); } else { // split the fragment into subspans at the points of range starts/ends QList subSpanTos = textRanges.uniqueKeys(); qSort(subSpanTos); // ensure last subSpanTo to be at the end if (subSpanTos.last() != spanTo) { subSpanTos.append(spanTo); } // spanFrom should not need to be included if (subSpanTos.first() == spanFrom) { subSpanTos.removeOne(spanFrom); } int subSpanFrom = spanFrom; // for all subspans foreach (int subSpanTo, subSpanTos) { // write tags for text ranges which start before this subspan or are positioned at it const QList textRangesStartingBefore = textRanges.values(subSpanFrom); foreach (const KoTextRange *range, textRangesStartingBefore) { range->saveOdf(context, subSpanFrom, KoTextRange::StartTag); } // write subspan content writer->addTextSpan(text.mid(subSpanFrom - fragmentStart, subSpanTo - subSpanFrom)); // write tags for text ranges which end behind this subspan const QList textRangesEndingBehind = textRanges.values(subSpanTo); foreach (const KoTextRange *range, textRangesEndingBehind) { range->saveOdf(context, subSpanTo, KoTextRange::EndTag); } subSpanFrom = subSpanTo; } } } else { writer->addTextSpan(text); } closeTagRegion(changeId); } // if (inlineObject) previousCharFormat = charFormat; lastEndPosition = fragmentEnd; } } if (!previousFragmentLink.isEmpty()) { writer->endElement(); } if (it.atEnd() && ((to == -1) || (lastEndPosition <= to))) { // write tags for ranges which start at the last position of the block, // i.e. at the position after the last (text) fragment const QHash startingTextRangesAtEnd = mgr->textRangesChangingWithin(lastEndPosition, lastEndPosition, globalFrom, globalTo); foreach (const KoTextRange *range, startingTextRangesAtEnd) { range->saveOdf(context, lastEndPosition, KoTextRange::StartTag); } } QString text = block.text(); if (text.length() == 0 || text.at(text.length()-1) == QChar(0x2028)) { if (block.blockFormat().hasProperty(KoParagraphStyle::EndCharStyle)) { QVariant v = block.blockFormat().property(KoParagraphStyle::EndCharStyle); QSharedPointer endCharStyle = v.value< QSharedPointer >(); if (!endCharStyle.isNull()) { QTextCharFormat charFormat; endCharStyle->applyStyle(charFormat); QString styleName = saveCharacterStyle(charFormat, blockCharFormat); if (!styleName.isEmpty()) { writer->startElement("text:span", false); writer->addAttribute("text:style-name", styleName); writer->endElement(); } } } } if (to !=-1 && to < block.position() + block.length()) { foreach (KoInlineObject* inlineObject, *currentPairedInlineObjectsStack) { inlineObject->saveOdf(context); } } if (changeTracker) { if (changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { QTextBlock nextBlock = block.next(); if (nextBlock.isValid() && deleteMergeRegionOpened) { QTextBlockFormat nextBlockFormat = nextBlock.blockFormat(); int changeId = nextBlockFormat.intProperty(KoCharacterStyle::ChangeTrackerId); if (changeId && changeTracker->elementById(changeId)->getChangeType() == KoGenChange::DeleteChange) { QTextFragment lastFragment = (--block.end()).fragment(); QTextCharFormat lastFragmentFormat = lastFragment.charFormat(); int lastFragmentChangeId = lastFragmentFormat.intProperty(KoCharacterStyle::ChangeTrackerId); if (changeTracker->isDuplicateChangeId(lastFragmentChangeId)) { lastFragmentChangeId = changeTracker->originalChangeId(lastFragmentChangeId); } if (lastFragmentChangeId != changeId) { QString outputXml(""); writer->addCompleteElement(outputXml.toUtf8()); } } } } if (changeTracker->saveFormat() == KoChangeTracker::ODF_1_2) { while (int change = changeStack.top()) { writer->startElement("text:change-end", false); writer->addAttribute("text:change-id", changeTransTable.value(change)); writer->endElement(); changeStack.pop(); } } } closeTagRegion(changeId); } //Check if the whole Block is a part of a single change //If so return the changeId else return 0 int KoTextWriter::Private::checkForBlockChange(const QTextBlock &block) { int changeId = 0; if (!changeTracker) { return changeId; } QTextBlock::iterator it = block.begin(); if (it.atEnd()) { //This is a empty block. So just return the change-id of the block changeId = block.blockFormat().property(KoCharacterStyle::ChangeTrackerId).toInt(); } for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) { QTextCharFormat charFormat = currentFragment.charFormat(); int currentChangeId = charFormat.property(KoCharacterStyle::ChangeTrackerId).toInt(); KoInlineTextObjectManager *textObjectManager = KoTextDocument(block.document()).inlineTextObjectManager(); if (textObjectManager) { KoInlineObject *inlineObject = textObjectManager->inlineTextObject(charFormat); if (currentFragment.length() == 1 && inlineObject && currentFragment.text()[0].unicode() == QChar::ObjectReplacementCharacter) { continue; } } if (!currentChangeId) { // Encountered a fragment that is not a change // So break out of loop and return 0 changeId = 0; break; } else { // This Fragment is a change fragment. Continue further. if (changeId == 0) { //First fragment and it is a change-fragment //Store it and continue changeId = currentChangeId; continue; } else { if (currentChangeId == changeId) { //Change Fragment and it is the same as the first change. //continue looking continue; } else if (changeTracker->isParent(currentChangeId, changeId)) { //The currentChangeId is a parent of changeId changeId = currentChangeId; continue; } else if (changeTracker->isParent(changeId, currentChangeId)) { //The current change id is a child of change-id continue; } else { //A Change Fragment but not same as the first change fragment //Break-out of loop and return 0 changeId = 0; break; } } } } } return changeId; } //Check if the whole list-item is a part of a single change //If so return the changeId else return 0 int KoTextWriter::Private::checkForListItemChange(const QTextBlock &block) { QTextBlock listItemBlock = block; int listItemChangeId = checkForBlockChange(listItemBlock); while (listItemChangeId) { QTextBlock nextBlock = listItemBlock.next(); if (!nextBlock.textList() || !nextBlock.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) break; listItemBlock = nextBlock; listItemChangeId = checkForBlockChange(listItemBlock); } return listItemChangeId; } //Check if the whole list is a part of a single change //If so return the changeId else return 0 int KoTextWriter::Private::checkForListChange(const QTextBlock &listBlock) { QTextBlock block(listBlock); KoTextDocument textDocument(block.document()); KoList *list = textDocument.list(block); int topListLevel = KoList::level(block); int changeId = 0; do { int currentChangeId = checkForBlockChange(block); if (changeTracker->isDuplicateChangeId(currentChangeId)) { currentChangeId = changeTracker->originalChangeId(currentChangeId); } if (!currentChangeId) { // Encountered a list-item that is not a change // So break out of loop and return 0 changeId = 0; break; } else { // This list-item is a changed cell. Continue further. if (changeId == 0) { //First list-item and it is a changed list-item //Store it and continue changeId = currentChangeId; block = block.next(); continue; } else { if (currentChangeId == changeId) { //Change found and it is the same as the first change. //continue looking block = block.next(); continue; } else if (changeTracker->isParent(currentChangeId, changeId)) { //The currentChangeId is a parent of changeId changeId = currentChangeId; block = block.next(); continue; } else if (changeTracker->isParent(changeId, currentChangeId)) { //The current change id is a child of change-id block = block.next(); continue; } else { //A Change found but not same as the first change //Break-out of loop and return 0 changeId = 0; break; } } } } while ((textDocument.list(block) == list) && (KoList::level(block) >= topListLevel)); return changeId; } //Check if the whole of table row is a part of a single change //If so return the changeId else return 0 int KoTextWriter::Private::checkForTableRowChange(int position) { int changeId = 0; QTextCursor cursor(document); cursor.setPosition(position); QTextTable *table = cursor.currentTable(); if (table) { int row = table->cellAt(position).row(); for (int i=0; icolumns(); i++) { int currentChangeId = table->cellAt(row,i).format().property(KoCharacterStyle::ChangeTrackerId).toInt(); if (!currentChangeId) { // Encountered a cell that is not a change // So break out of loop and return 0 changeId = 0; break; } else { // This cell is a changed cell. Continue further. if (changeId == 0) { //First cell and it is a changed-cell //Store it and continue changeId = currentChangeId; continue; } else { if (currentChangeId == changeId) { //Change found and it is the same as the first change. //continue looking continue; } else { //A Change found but not same as the first change //Break-out of loop and return 0 changeId = 0; break; } } } } } return changeId; } //Check if the whole of table column is a part of a single change //If so return the changeId else return 0 int KoTextWriter::Private::checkForTableColumnChange(int position) { int changeId = 0; QTextCursor cursor(document); cursor.setPosition(position); QTextTable *table = cursor.currentTable(); if (table) { int column = table->cellAt(position).column(); for (int i=0; irows(); i++) { int currentChangeId = table->cellAt(i,column).format().property(KoCharacterStyle::ChangeTrackerId).toInt(); if (!currentChangeId) { // Encountered a cell that is not a change // So break out of loop and return 0 changeId = 0; break; } else { // This cell is a changed cell. Continue further. if (changeId == 0) { //First cell and it is a changed-cell //Store it and continue changeId = currentChangeId; continue; } else { if (currentChangeId == changeId) { //Change found and it is the same as the first change. //continue looking continue; } else { //A Change found but not same as the first change //Break-out of loop and return 0 changeId = 0; break; } } } } } return changeId; } void KoTextWriter::Private::saveTable(QTextTable *table, QHash &listStyles) { KoTableColumnAndRowStyleManager tcarManager = KoTableColumnAndRowStyleManager::getManager(table); int numberHeadingRows = table->format().property(KoTableStyle::NumberHeadingRows).toInt(); TagInformation tableTagInformation; QString tableStyleName = saveTableStyle(*table); tableTagInformation.setTagName("table:table"); tableTagInformation.addAttribute("table:style-name", tableStyleName); if (table->format().boolProperty(KoTableStyle::TableIsProtected)) { tableTagInformation.addAttribute("table:protected", "true"); } if (table->format().hasProperty(KoTableStyle::TableTemplate)) { tableTagInformation.addAttribute("table:template-name", sharedData->styleName(table->format().intProperty(KoTableStyle::TableTemplate))); } if (table->format().boolProperty(KoTableStyle::UseBandingColumnStyles)) { tableTagInformation.addAttribute("table:use-banding-columns-styles", "true"); } if (table->format().boolProperty(KoTableStyle::UseBandingRowStyles)) { tableTagInformation.addAttribute("table:use-banding-rows-styles", "true"); } if (table->format().boolProperty(KoTableStyle::UseFirstColumnStyles)) { tableTagInformation.addAttribute("table:use-first-column-styles", "true"); } if (table->format().boolProperty(KoTableStyle::UseFirstRowStyles)) { tableTagInformation.addAttribute("table:use-first-row-styles", "true"); } if (table->format().boolProperty(KoTableStyle::UseLastColumnStyles)) { tableTagInformation.addAttribute("table:use-last-column-styles", "true"); } if (table->format().boolProperty(KoTableStyle::UseLastRowStyles)) { tableTagInformation.addAttribute("table:use-last-row-styles", "true"); } int changeId = openTagRegion(table->firstCursorPosition().position(), KoTextWriter::Private::Table, tableTagInformation); for (int c = 0 ; c < table->columns() ; c++) { KoTableColumnStyle columnStyle = tcarManager.columnStyle(c); int repetition = 0; for (; repetition < (table->columns() - c) ; repetition++) { if (columnStyle != tcarManager.columnStyle(c + repetition + 1)) break; } TagInformation tableColumnInformation; tableColumnInformation.setTagName("table:table-column"); QString columnStyleName = saveTableColumnStyle(columnStyle, c, tableStyleName); tableColumnInformation.addAttribute("table:style-name", columnStyleName); if (repetition > 0) tableColumnInformation.addAttribute("table:number-columns-repeated", repetition + 1); int changeId = openTagRegion(table->cellAt(0,c).firstCursorPosition().position(), KoTextWriter::Private::TableColumn, tableColumnInformation); closeTagRegion(changeId); c += repetition; } if (numberHeadingRows) writer->startElement("table:table-header-rows"); for (int r = 0 ; r < table->rows() ; r++) { TagInformation tableRowInformation; tableRowInformation.setTagName("table:table-row"); KoTableRowStyle rowStyle = tcarManager.rowStyle(r); if (!rowStyle.isEmpty()) { QString rowStyleName = saveTableRowStyle(rowStyle, r, tableStyleName); tableRowInformation.addAttribute("table:style-name", rowStyleName); } int changeId = openTagRegion(table->cellAt(r,0).firstCursorPosition().position(), KoTextWriter::Private::TableRow, tableRowInformation); for (int c = 0 ; c < table->columns() ; c++) { QTextTableCell cell = table->cellAt(r, c); int changeId = 0; TagInformation tableCellInformation; if ((cell.row() == r) && (cell.column() == c)) { tableCellInformation.setTagName("table:table-cell"); if (cell.rowSpan() > 1) tableCellInformation.addAttribute("table:number-rows-spanned", cell.rowSpan()); if (cell.columnSpan() > 1) tableCellInformation.addAttribute("table:number-columns-spanned", cell.columnSpan()); if (cell.format().boolProperty(KoTableCellStyle::CellIsProtected)) { tableCellInformation.addAttribute("table:protected", "true"); } // Save the Rdf for the table cell QTextTableCellFormat cellFormat = cell.format().toTableCellFormat(); QVariant v = cellFormat.property(KoTableCellStyle::InlineRdf); if (KoTextInlineRdf* inlineRdf = v.value()) { inlineRdf->saveOdf(context, writer); } QString cellStyleName = saveTableCellStyle(cellFormat, c, tableStyleName); tableCellInformation.addAttribute("table:style-name", cellStyleName); changeId = openTagRegion(table->cellAt(r,c).firstCursorPosition().position(), KoTextWriter::Private::TableCell, tableCellInformation); writeBlocks(table->document(), cell.firstPosition(), cell.lastPosition(), listStyles, table); } else { tableCellInformation.setTagName("table:covered-table-cell"); if (cell.format().boolProperty(KoTableCellStyle::CellIsProtected)) { tableCellInformation.addAttribute("table:protected", "true"); } changeId = openTagRegion(table->cellAt(r,c).firstCursorPosition().position(), KoTextWriter::Private::TableCell, tableCellInformation); } closeTagRegion(changeId); } closeTagRegion(changeId); if (r + 1 == numberHeadingRows) { writer->endElement(); // table:table-header-rows writer->startElement("table:table-rows"); } } if (numberHeadingRows) writer->endElement(); // table:table-rows closeTagRegion(changeId); } void KoTextWriter::Private::saveTableOfContents(QTextDocument *document, QHash &listStyles, QTextBlock toc) { Q_UNUSED(document); writer->startElement("text:table-of-content"); KoTableOfContentsGeneratorInfo *info = toc.blockFormat().property(KoParagraphStyle::TableOfContentsData).value(); QTextDocument *tocDocument = toc.blockFormat().property(KoParagraphStyle::GeneratedDocument).value(); if (!info->m_styleName.isNull()) { writer->addAttribute("text:style-name",info->m_styleName); } writer->addAttribute("text:name",info->m_name); info->saveOdf(writer); writer->startElement("text:index-body"); // write the title (one p block) QTextCursor localBlock = tocDocument->rootFrame()->firstCursorPosition(); localBlock.movePosition(QTextCursor::NextBlock); int endTitle = localBlock.position(); writer->startElement("text:index-title"); writer->addAttribute("text:name", QString("%1_Head").arg(info->m_name)); writeBlocks(tocDocument, 0, endTitle, listStyles); writer->endElement(); // text:index-title writeBlocks(tocDocument, endTitle, -1, listStyles); writer->endElement(); // table:index-body writer->endElement(); // table:table-of-content } void KoTextWriter::Private::saveBibliography(QTextDocument *document, QHash &listStyles, QTextBlock bib) { Q_UNUSED(document); writer->startElement("text:bibliography"); KoBibliographyInfo *info = bib.blockFormat().property(KoParagraphStyle::BibliographyData).value(); QTextDocument *bibDocument = bib.blockFormat().property(KoParagraphStyle::GeneratedDocument).value(); if (!info->m_styleName.isNull()) { writer->addAttribute("text:style-name",info->m_styleName); } writer->addAttribute("text:name",info->m_name); info->saveOdf(writer); writer->startElement("text:index-body"); // write the title (one p block) QTextCursor localBlock = bibDocument->rootFrame()->firstCursorPosition(); localBlock.movePosition(QTextCursor::NextBlock); int endTitle = localBlock.position(); writer->startElement("text:index-title"); writeBlocks(bibDocument, 0, endTitle, listStyles); writer->endElement(); // text:index-title writeBlocks(bibDocument, endTitle, -1, listStyles); writer->endElement(); // table:index-body writer->endElement(); // table:bibliography } QTextBlock& KoTextWriter::Private::saveList(QTextBlock &block, QHash &listStyles, int level, QTextTable *currentTable) { QTextList *textList, *topLevelTextList; topLevelTextList = textList = block.textList(); int headingLevel = 0, numberedParagraphLevel = 0; QTextBlockFormat blockFormat = block.blockFormat(); headingLevel = blockFormat.intProperty(KoParagraphStyle::OutlineLevel); numberedParagraphLevel = blockFormat.intProperty(KoParagraphStyle::ListLevel); KoTextDocument textDocument(block.document()); KoList *list = textDocument.list(block); int topListLevel = KoList::level(block); if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if ((level == 1) && (!deleteMergeRegionOpened) && !headingLevel) { QTextBlock listBlock = block; do { int endBlockNumber = checkForDeleteMerge(listBlock); if (endBlockNumber != -1) { deleteMergeEndBlockNumber = endBlockNumber; deleteMergeRegionOpened = true; openSplitMergeRegion(); break; } listBlock = listBlock.next(); } while(textDocument.list(listBlock) == list); } } bool closeDelMergeRegion = false; if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if ((level == 1) && (deleteMergeRegionOpened) && !headingLevel) { QTextBlock listBlock = block; do { if (listBlock.blockNumber() == deleteMergeEndBlockNumber) { closeDelMergeRegion = true; } listBlock = listBlock.next(); } while(textDocument.list(listBlock) == list); } } bool splitRegionOpened = false; bool listStarted = false; int listChangeId = 0; if (!headingLevel && !numberedParagraphLevel) { listStarted = true; TagInformation listTagInformation; listTagInformation.setTagName("text:list"); listTagInformation.addAttribute("text:style-name", listStyles[textList]); if (list && listXmlIds.contains(list->listContinuedFrom())) { listTagInformation.addAttribute("text:continue-list", listXmlIds.value(list->listContinuedFrom())); } QString listXmlId = QString("list-%1").arg(createXmlId()); listTagInformation.addAttribute("xml:id", listXmlId); if (! listXmlIds.contains(list)) { listXmlIds.insert(list, listXmlId); } listChangeId = openTagRegion(block.position(), KoTextWriter::Private::List, listTagInformation); } if (!headingLevel) { int splitEndBlockNumber = -1; do { if (numberedParagraphLevel) { TagInformation paraTagInformation; paraTagInformation.setTagName("text:numbered-paragraph"); paraTagInformation.addAttribute("text:level", numberedParagraphLevel); paraTagInformation.addAttribute("text:style-name", listStyles.value(textList)); QString listId = numberedParagraphListIds.value(list, QString("list-%1").arg(createXmlId())); numberedParagraphListIds.insert(list, listId); paraTagInformation.addAttribute("text:list-id", listId); int changeId = openTagRegion(block.position(), KoTextWriter::Private::NumberedParagraph, paraTagInformation); writeBlocks(textDocument.document(), block.position(), block.position() + block.length() - 1, listStyles, currentTable, textList); closeTagRegion(changeId); } else { if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { int endBlockNumber = checkForSplit(block); if (!deleteMergeRegionOpened && !splitRegionOpened && (endBlockNumber != -1)) { openSplitMergeRegion(); splitRegionOpened = true; splitEndBlockNumber = endBlockNumber; } } const bool listHeader = blockFormat.boolProperty(KoParagraphStyle::IsListHeader)|| blockFormat.boolProperty(KoParagraphStyle::UnnumberedListItem); int listItemChangeId; TagInformation listItemTagInformation; listItemTagInformation.setTagName(listHeader ? "text:list-header" : "text:list-item"); if (block.blockFormat().hasProperty(KoParagraphStyle::ListStartValue)) { int startValue = block.blockFormat().intProperty(KoParagraphStyle::ListStartValue); listItemTagInformation.addAttribute("text:start-value", startValue); } if (textList == topLevelTextList) { listItemChangeId = openTagRegion(block.position(), KoTextWriter::Private::ListItem, listItemTagInformation); } else { // This is a sub-list. So check for a list-change listItemChangeId = openTagRegion(block.position(), KoTextWriter::Private::List, listItemTagInformation); } if (KoListStyle::isNumberingStyle(textList->format().style())) { if (KoTextBlockData *blockData = dynamic_cast(block.userData())) { writer->startElement("text:number", false); writer->addTextSpan(blockData->counterText()); writer->endElement(); } } if (topListLevel == level && textList == topLevelTextList) { writeBlocks(textDocument.document(), block.position(), block.position() + block.length() - 1, listStyles, currentTable, textList); // we are generating a text:list-item. Look forward and generate unnumbered list items. while (true) { QTextBlock nextBlock = block.next(); if (!nextBlock.textList() || !nextBlock.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) break; block = nextBlock; saveParagraph(block, block.position(), block.position() + block.length() - 1); } } else { //This is a sub-list while (KoList::level(block) >= (level + 1) && !(headingLevel || numberedParagraphLevel)) { block = saveList(block, listStyles, level + 1, currentTable); blockFormat = block.blockFormat(); headingLevel = blockFormat.intProperty(KoParagraphStyle::OutlineLevel); numberedParagraphLevel = blockFormat.intProperty(KoParagraphStyle::ListLevel); } //saveList will return a block one-past the last block of the list. //Since we are doing a block.next() below, we need to go one back. block = block.previous(); } closeTagRegion(listItemChangeId); if (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML) { if (splitRegionOpened && (block.blockNumber() == splitEndBlockNumber)) { splitRegionOpened = false; splitEndBlockNumber = -1; closeSplitMergeRegion(); postProcessListItemSplit(block.blockFormat().intProperty(KoCharacterStyle::ChangeTrackerId)); } } } block = block.next(); blockFormat = block.blockFormat(); headingLevel = blockFormat.intProperty(KoParagraphStyle::OutlineLevel); numberedParagraphLevel = blockFormat.intProperty(KoParagraphStyle::ListLevel); textList = block.textList(); } while ((textDocument.list(block) == list) && (KoList::level(block) >= topListLevel)); } if (listStarted) { closeTagRegion(listChangeId); } if (closeDelMergeRegion && (changeTracker && changeTracker->saveFormat() == KoChangeTracker::DELTAXML)) { closeSplitMergeRegion(); deleteMergeRegionOpened = false; deleteMergeEndBlockNumber = -1; postProcessDeleteMergeXml(); } return block; } void KoTextWriter::Private::postProcessListItemSplit(int changeId) { QString change = changeTransTable.value(changeId); QString generatedXmlString(generatedXmlArray); //Add the name-space definitions so that this can be parsed addNameSpaceDefinitions(generatedXmlString); //Now Parse the generatedXML and if successful generate the final output QString errorMsg; int errorLine, errorColumn; KoXmlDocument doc; QXmlStreamReader reader(generatedXmlString); reader.setNamespaceProcessing(true); bool ok = doc.setContent(&reader, &errorMsg, &errorLine, &errorColumn); if (!ok) return; QString outputXml; QTextStream outputXmlStream(&outputXml); KoXmlElement rootElement = doc.documentElement(); KoXmlElement listItemElement = rootElement.firstChild().toElement(); removeLeavingContentStart(outputXmlStream, listItemElement, change, 1); KoXmlElement pElement = rootElement.firstChild().firstChild().toElement(); removeLeavingContentStart(outputXmlStream, pElement, change, 2); KoXmlElement childElement; forEachElement(childElement, rootElement) { if (childElement.localName() == "list-item") { insertAroundContent(outputXmlStream, childElement, change); KoXmlElement pElement = childElement.firstChild().toElement(); insertAroundContent(outputXmlStream, pElement, change); writeNode(outputXmlStream, pElement, true); outputXmlStream << ""; outputXmlStream << ""; } else { writeNode(outputXmlStream, pElement); } } removeLeavingContentEnd(outputXmlStream, 2); removeLeavingContentEnd(outputXmlStream, 1); writer->addCompleteElement(outputXml.toUtf8()); } int KoTextWriter::Private::checkForSplit(const QTextBlock &block) { return checkForMergeOrSplit(block, KoGenChange::InsertChange); } int KoTextWriter::Private::checkForDeleteMerge(const QTextBlock &block) { return checkForMergeOrSplit(block, KoGenChange::DeleteChange); } int KoTextWriter::Private::checkForMergeOrSplit(const QTextBlock &block, KoGenChange::Type changeType) { QTextBlock endBlock = block; int endBlockNumber = -1; int splitMergeChangeId = 0, changeId = 0; do { if (!endBlock.next().isValid()) break; int nextBlockChangeId; QTextTable *currentTable; if ((currentTable = QTextCursor(endBlock.next()).currentTable())) { nextBlockChangeId = currentTable->format().intProperty(KoCharacterStyle::ChangeTrackerId); } else { nextBlockChangeId = endBlock.next().blockFormat().property(KoCharacterStyle::ChangeTrackerId).toInt(); } if (changeTracker && changeTracker->isDuplicateChangeId(nextBlockChangeId)) { nextBlockChangeId = changeTracker->originalChangeId(nextBlockChangeId); } if (!changeId) { splitMergeChangeId = changeId = nextBlockChangeId; if ((changeId) && (changeTracker && changeTracker->elementById(nextBlockChangeId)->getChangeType() == changeType)) { endBlock = endBlock.next(); } else { changeId = 0; } } else { if (nextBlockChangeId == changeId) { endBlock = endBlock.next(); } else { changeId = 0; } } } while(changeId); if ((endBlock.blockNumber() != block.blockNumber()) && (endBlock.text().length()) && !(QTextCursor(endBlock).currentTable())) { //Check that the last fragment of this block is not a part of this change. If so, it is not a merge or a split QTextFragment lastFragment = (--(endBlock.end())).fragment(); QTextCharFormat lastFragmentFormat = lastFragment.charFormat(); int lastFragmentChangeId = lastFragmentFormat.intProperty(KoCharacterStyle::ChangeTrackerId); if (changeTracker && changeTracker->isDuplicateChangeId(lastFragmentChangeId)) { lastFragmentChangeId = changeTracker->originalChangeId(lastFragmentChangeId); } if (lastFragmentChangeId != splitMergeChangeId) { endBlockNumber = endBlock.blockNumber(); } } return endBlockNumber; } void KoTextWriter::Private::openSplitMergeRegion() { //Save the current writer oldXmlWriter = writer; //Create a new KoXmlWriter pointing to a QBuffer generatedXmlArray.clear(); generatedXmlBuffer.setBuffer(&generatedXmlArray); newXmlWriter = new KoXmlWriter(&generatedXmlBuffer); //Set our xmlWriter as the writer to be used writer = newXmlWriter; context.setXmlWriter(*newXmlWriter); } void KoTextWriter::Private::closeSplitMergeRegion() { //delete the new writer delete newXmlWriter; //Restore the actual xml writer writer = oldXmlWriter; context.setXmlWriter(*oldXmlWriter); } void KoTextWriter::Private::postProcessDeleteMergeXml() { QString generatedXmlString(generatedXmlArray); //Add the name-space definitions so that this can be parsed addNameSpaceDefinitions(generatedXmlString); //Now Parse the generatedXML and if successful generate the final output QString errorMsg; int errorLine, errorColumn; KoXmlDocument doc; QXmlStreamReader reader(generatedXmlString); reader.setNamespaceProcessing(true); bool ok = doc.setContent(&reader, &errorMsg, &errorLine, &errorColumn); if (ok) { //Generate the final XML output and save it QString outputXml; QTextStream outputXmlStream(&outputXml); generateFinalXml(outputXmlStream, doc.documentElement()); writer->addCompleteElement(outputXml.toUtf8()); } } void KoTextWriter::Private::addNameSpaceDefinitions(QString &generatedXmlString) { //Generate the name-space definitions so that it can be parsed. Like what is office:text, office:delta etc QString nameSpaceDefinitions; QTextStream nameSpacesStream(&nameSpaceDefinitions); nameSpacesStream << ""; generatedXmlString.prepend(nameSpaceDefinitions); generatedXmlString.append(""); } void KoTextWriter::Private::generateFinalXml(QTextStream &outputXmlStream, const KoXmlElement &element) { QString firstChild = element.firstChild().toElement().localName(); KoXmlElement secondChildElement = element.firstChild().nextSibling().toElement(); QString secondChild; do { secondChild = secondChildElement.localName(); secondChildElement = secondChildElement.nextSibling().toElement(); } while (secondChild == "removed-content"); if ((firstChild == "p") && (secondChild == "h")) { handleParagraphOrHeaderMerge(outputXmlStream, element); } else if ((firstChild == "h") && (secondChild == "p")) { handleParagraphOrHeaderMerge(outputXmlStream, element); } else if ((firstChild == "p") && (secondChild == "p")) { handleParagraphOrHeaderMerge(outputXmlStream, element); } else if ((firstChild == "h") && (secondChild == "h")) { handleParagraphOrHeaderMerge(outputXmlStream, element); } else if ((firstChild == "p") && (secondChild == "list")) { handleParagraphWithListItemMerge(outputXmlStream, element); } else if ((firstChild == "h") && (secondChild == "list")) { handleParagraphWithListItemMerge(outputXmlStream, element); } else if ((firstChild == "list") && (secondChild == "p")) { handleListItemWithParagraphMerge(outputXmlStream, element); } else if ((firstChild == "list") && (secondChild == "h")) { handleListItemWithParagraphMerge(outputXmlStream, element); } else if ((firstChild == "list") && (secondChild == "list")) { handleListWithListMerge(outputXmlStream, element); } else if ((firstChild == "list") && (secondChild.isEmpty())) { handleListItemWithListItemMerge(outputXmlStream, element); } else { //Not Possible } } void KoTextWriter::Private::removeLeavingContentStart(QTextStream &outputXmlStream, KoXmlElement &element, QString &changeId, int endIdCounter) { outputXmlStream << ""; outputXmlStream << ""; outputXmlStream << ""; } void KoTextWriter::Private::removeLeavingContentEnd(QTextStream &outputXmlStream, int endIdCounter) { outputXmlStream << ""; } void KoTextWriter::Private::insertAroundContent(QTextStream &outputXmlStream, KoXmlElement &element, QString &changeId) { outputXmlStream << ""; } void KoTextWriter::Private::handleParagraphOrHeaderMerge(QTextStream &outputXmlStream, const KoXmlElement &element) { // Find the first child of the element // This is needed because we need to determine whether the final element is going to a

or a KoXmlElement firstChildElement = element.firstChild().toElement(); QString firstChild = firstChildElement.localName(); // Find the Change-id KoXmlElement removedContentElement = firstChildElement.lastChild().toElement(); QString changeId = removedContentElement.attributeNS(KoXmlNS::delta, "removal-change-idref"); outputXmlStream << ""; for (KoXmlNode node = firstChildElement.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement() && node.toElement().localName() == "removed-content" && node.nextSibling().isNull()) { outputXmlStream << ""; outputXmlStream << ""; writeNode(outputXmlStream, node, true); outputXmlStream << ""; } else { writeNode(outputXmlStream, node); } } outputXmlStream << ""; KoXmlElement mergeEndElement = firstChildElement.nextSibling().toElement(); while (mergeEndElement.localName() == "removed-content") { writeNode(outputXmlStream, mergeEndElement, true); mergeEndElement = mergeEndElement.nextSibling().toElement(); } outputXmlStream << ""; for (KoXmlNode node = mergeEndElement.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement() && node.toElement().localName() == "removed-content" && node.previousSibling().isNull()) { outputXmlStream << ""; outputXmlStream << ""; writeNode(outputXmlStream, node, true); outputXmlStream << ""; outputXmlStream << ""; outputXmlStream << ""; } else { writeNode(outputXmlStream, node); } } outputXmlStream << ""; } void KoTextWriter::Private::handleParagraphWithHeaderMerge(QTextStream &outputXmlStream, const KoXmlElement &element) { // Find the first child of the element // This is needed because we need to determine whether the final element is going to a

or a KoXmlElement firstChildElement = element.firstChild().toElement(); QString firstChild = firstChildElement.localName(); // Find the Change-id KoXmlElement removedContentElement = firstChildElement.lastChild().toElement(); QString changeId = removedContentElement.attributeNS(KoXmlNS::delta, "removal-change-idref"); //Start generating the XML insertAroundContent(outputXmlStream, firstChildElement, changeId); //Start a counter for end-element-idref int endIdCounter = 1; KoXmlElement childElement; forEachElement (childElement, element) { if (childElement.localName() == "removed-content") { writeNode(outputXmlStream, childElement, false); } else { removeLeavingContentStart(outputXmlStream, childElement, changeId, endIdCounter); writeNode(outputXmlStream, childElement, true); removeLeavingContentEnd(outputXmlStream, endIdCounter); endIdCounter++; } } outputXmlStream << ""; } void KoTextWriter::Private::handleParagraphWithListItemMerge(QTextStream &outputXmlStream, const KoXmlElement &element) { // Find the first child of the element // This is needed because we need to determine whether the final element is going to a

or a KoXmlElement firstChildElement = element.firstChild().toElement(); QString firstChild = firstChildElement.localName(); // Find the Change-id KoXmlElement removedContentElement = firstChildElement.lastChild().toElement(); QString changeId = removedContentElement.attributeNS(KoXmlNS::delta, "removal-change-idref"); //Start generating the XML insertAroundContent(outputXmlStream, firstChildElement, changeId); //Start a counter for end-element-idref int endIdCounter = 1; KoXmlElement childElement; forEachElement (childElement, element) { if (childElement.localName() == "removed-content") { writeNode(outputXmlStream, childElement, false); } else if (childElement.localName() == firstChild) { removeLeavingContentStart(outputXmlStream, childElement, changeId, endIdCounter); writeNode(outputXmlStream, childElement, true); removeLeavingContentEnd(outputXmlStream, endIdCounter); endIdCounter++; } else if (childElement.localName() == "list"){ generateListForPWithListMerge(outputXmlStream, childElement, firstChild, changeId, endIdCounter, true); } } } void KoTextWriter::Private::generateListForPWithListMerge(QTextStream &outputXmlStream, KoXmlElement &element, QString &mergeResultElement, QString &changeId, int &endIdCounter, bool removeLeavingContent) { int listEndIdCounter = endIdCounter; if (removeLeavingContent) { endIdCounter++; removeLeavingContentStart(outputXmlStream, element, changeId, listEndIdCounter); } else { outputXmlStream << ""; } bool tagTypeChangeEnded = false; bool listStarted = false; KoXmlElement childElement; forEachElement (childElement, element) { if (childElement.localName() == "removed-content") { writeNode(outputXmlStream, childElement, false); } else if ((childElement.localName() == "list-item") || (childElement.localName() == "list-header")) { generateListItemForPWithListMerge(outputXmlStream, childElement, mergeResultElement, changeId, endIdCounter, !tagTypeChangeEnded); if (!tagTypeChangeEnded) { tagTypeChangeEnded = true; if (childElement != element.lastChild().toElement()) { listStarted = true; insertAroundContent(outputXmlStream, element, changeId); } } } else { //Not Possible } } if (listStarted) outputXmlStream << ""; if (removeLeavingContent) { removeLeavingContentEnd(outputXmlStream, listEndIdCounter); } else { outputXmlStream << ""; } } void KoTextWriter::Private::generateListItemForPWithListMerge(QTextStream &outputXmlStream, KoXmlElement &element, QString &mergeResultElement, QString &changeId, int &endIdCounter, bool removeLeavingContent) { int listItemEndIdCounter = endIdCounter; if (removeLeavingContent) { endIdCounter++; removeLeavingContentStart(outputXmlStream, element, changeId, listItemEndIdCounter); } else { outputXmlStream << ""; } KoXmlElement childElement; forEachElement (childElement, element) { if (childElement.localName() == "removed-content") { writeNode(outputXmlStream, childElement, false); } else if (childElement.localName() == "p") { if (removeLeavingContent) { int paragraphEndIdCounter = endIdCounter; endIdCounter++; removeLeavingContentStart(outputXmlStream, childElement, changeId, paragraphEndIdCounter); writeNode(outputXmlStream, childElement, true); removeLeavingContentEnd(outputXmlStream, paragraphEndIdCounter); outputXmlStream << ""; } else { writeNode(outputXmlStream, childElement, false); } } else if (childElement.localName() == "list") { generateListForPWithListMerge(outputXmlStream, childElement, mergeResultElement, changeId, endIdCounter, removeLeavingContent); } else { //Not Possible } } if (removeLeavingContent) { removeLeavingContentEnd(outputXmlStream, listItemEndIdCounter); } else { outputXmlStream << ""; } } void KoTextWriter::Private::handleListItemWithParagraphMerge(QTextStream &outputXmlStream, const KoXmlElement &element) { deleteStartDepth = 0; // Find the first

so that we can get the change-id KoXmlElement paragraphElement; forEachElement(paragraphElement, element) { if (paragraphElement.localName() == "p") { break; } } // Find the Change-id KoXmlElement removedContentElement = paragraphElement.firstChild().toElement(); QString changeId = removedContentElement.attributeNS(KoXmlNS::delta, "removal-change-idref"); int endIdCounter = 1; KoXmlElement childElement; forEachElement(childElement, element) { if (childElement.localName() == "list") { generateListForListWithPMerge(outputXmlStream, childElement, changeId, endIdCounter, true); } else if (childElement.localName() == "removed-content") { writeNode(outputXmlStream, childElement, false); } else if (childElement.localName() == "p") { int paragraphEndIdCounter = endIdCounter; endIdCounter++; removeLeavingContentStart(outputXmlStream, childElement, changeId, paragraphEndIdCounter); writeNode(outputXmlStream, childElement, true); removeLeavingContentEnd(outputXmlStream, paragraphEndIdCounter); outputXmlStream << ""; for (int i=0; i < deleteStartDepth; i++) { outputXmlStream << ""; outputXmlStream << ""; } break; } else { } } } void KoTextWriter::Private::generateListForListWithPMerge(QTextStream &outputXmlStream, KoXmlElement &element, QString &changeId, int &endIdCounter, bool removeLeavingContent) { static int listDepth = 0; listDepth++; if (!deleteStartDepth) { KoXmlElement childElement; forEachElement(childElement, element) { if ((childElement.localName() == "list-item") || (childElement.localName() == "list-header")) { bool startOfDeleteMerge = checkForDeleteStartInListItem(childElement, false); if (startOfDeleteMerge) { deleteStartDepth = listDepth; } } } } int listEndIdCounter = endIdCounter; if (removeLeavingContent) { endIdCounter++; removeLeavingContentStart(outputXmlStream, element, changeId, listEndIdCounter); insertAroundContent(outputXmlStream, element, changeId); } else { outputXmlStream << ""; } KoXmlElement childElement; forEachElement(childElement, element) { if ((childElement.localName() == "list-item") || (childElement.localName() == "list-header")) { bool startOfDeleteMerge = checkForDeleteStartInListItem(childElement); generateListItemForListWithPMerge(outputXmlStream, childElement, changeId, endIdCounter, startOfDeleteMerge); } else if (childElement.localName() == "removed-content") { writeNode(outputXmlStream, childElement, false); } } if (removeLeavingContent) { removeLeavingContentEnd(outputXmlStream, listEndIdCounter); } else { outputXmlStream << ""; } } void KoTextWriter::Private::generateListItemForListWithPMerge(QTextStream &outputXmlStream, KoXmlElement &element, QString &changeId, int &endIdCounter, bool removeLeavingContent) { if (!removeLeavingContent) { writeNode(outputXmlStream, element, false); } else { int listItemEndIdCounter = endIdCounter; endIdCounter++; insertAroundContent(outputXmlStream, element, changeId); removeLeavingContentStart(outputXmlStream, element, changeId, listItemEndIdCounter); KoXmlElement childElement; forEachElement (childElement, element) { if (childElement.localName() == "p") { int paragraphEndIdCounter = endIdCounter; endIdCounter++; insertAroundContent(outputXmlStream, childElement, changeId); removeLeavingContentStart(outputXmlStream, childElement, changeId, paragraphEndIdCounter); writeNode(outputXmlStream, childElement, true); removeLeavingContentEnd(outputXmlStream, paragraphEndIdCounter); } else if(childElement.localName() == "list") { generateListForListWithPMerge(outputXmlStream, childElement, changeId, endIdCounter, removeLeavingContent); } } removeLeavingContentEnd(outputXmlStream, listItemEndIdCounter); } } bool KoTextWriter::Private::checkForDeleteStartInListItem(KoXmlElement &element, bool checkRecursively) { bool returnValue = false; KoXmlElement childElement; forEachElement(childElement, element) { if (childElement.localName() == "p") { if (childElement.lastChild().toElement().localName() == "removed-content") { returnValue = true; break; } } else if ((childElement.localName() == "list") && (checkRecursively)) { KoXmlElement listItem; forEachElement(listItem, childElement) { returnValue = checkForDeleteStartInListItem(listItem); if (returnValue) break; } } else { } if (returnValue) break; } return returnValue; } void KoTextWriter::Private::handleListWithListMerge(QTextStream &outputXmlStream, const KoXmlElement &element) { int endIdCounter = 1; KoXmlElement listElement = element.firstChild().toElement(); QString changeId = findChangeIdForListItemMerge(listElement); KoXmlElement firstChildElement = element.firstChild().toElement(); generateListForListItemMerge(outputXmlStream, firstChildElement, changeId, endIdCounter, true, false); KoXmlElement secondChildElement = element.firstChild().nextSibling().toElement(); QString secondChild = secondChildElement.localName(); while (secondChild == "removed-content") { writeNode(outputXmlStream, secondChildElement, false); secondChildElement = secondChildElement.nextSibling().toElement(); secondChild = secondChildElement.localName(); }; generateListForListItemMerge(outputXmlStream, secondChildElement, changeId, endIdCounter, false, true); } void KoTextWriter::Private::handleListItemWithListItemMerge(QTextStream &outputXmlStream, const KoXmlElement &element) { int endIdCounter = 1; KoXmlElement listElement = element.firstChild().toElement(); QString changeId = findChangeIdForListItemMerge(listElement); generateListForListItemMerge(outputXmlStream, listElement, changeId, endIdCounter, false, false); } void KoTextWriter::Private::generateListForListItemMerge(QTextStream &outputXmlStream, KoXmlElement &element, QString &changeId, int &endIdCounter, bool listMergeStart, bool listMergeEnd) { int listEndIdCounter = endIdCounter; if (listMergeStart || listMergeEnd) { endIdCounter++; removeLeavingContentStart(outputXmlStream, element, changeId, listEndIdCounter); if (listMergeStart) { insertAroundContent(outputXmlStream, element, changeId); } } else { outputXmlStream << ""; } KoXmlElement childElement; bool deleteRangeStarted = false; if (listMergeEnd) { deleteRangeStarted = true; } forEachElement(childElement, element) { if ((childElement.localName() == "list-item") || (childElement.localName() == "list-header")) { if (!deleteRangeStarted) { deleteRangeStarted = checkForDeleteStartInListItem(childElement); generateListItemForListItemMerge(outputXmlStream, childElement, changeId, endIdCounter, deleteRangeStarted, false); } else { bool endOfDeleteRange = checkForDeleteEndInListItem(childElement); deleteRangeStarted = !endOfDeleteRange; generateListItemForListItemMerge(outputXmlStream, childElement, changeId, endIdCounter, false, endOfDeleteRange); } } else if (childElement.localName() == "removed-content") { writeNode(outputXmlStream, childElement, false); } } if (listMergeStart || listMergeEnd) { removeLeavingContentEnd(outputXmlStream, listEndIdCounter); if (listMergeEnd) { outputXmlStream << ""; } } else { outputXmlStream << ""; } } void KoTextWriter::Private::generateListItemForListItemMerge(QTextStream &outputXmlStream, KoXmlElement &element, QString &changeId, int &endIdCounter, bool listItemMergeStart, bool listItemMergeEnd) { if (!listItemMergeStart && !listItemMergeEnd) { writeNode(outputXmlStream, element, false); } else { int listItemEndIdCounter = endIdCounter; endIdCounter++; if (listItemMergeStart) { insertAroundContent(outputXmlStream, element, changeId); } removeLeavingContentStart(outputXmlStream, element, changeId, listItemEndIdCounter); KoXmlElement childElement; forEachElement (childElement, element) { if (childElement.localName() == "p") { int paragraphEndIdCounter = endIdCounter; endIdCounter++; if (listItemMergeStart) { insertAroundContent(outputXmlStream, childElement, changeId); } removeLeavingContentStart(outputXmlStream, childElement, changeId, paragraphEndIdCounter); writeNode(outputXmlStream, childElement, true); removeLeavingContentEnd(outputXmlStream, paragraphEndIdCounter); if (listItemMergeEnd) { outputXmlStream << ""; } } else if(childElement.localName() == "list") { generateListForListItemMerge(outputXmlStream, childElement, changeId, endIdCounter, listItemMergeStart ,listItemMergeEnd); } } removeLeavingContentEnd(outputXmlStream, listItemEndIdCounter); if (listItemMergeEnd) { outputXmlStream << ""; } } } bool KoTextWriter::Private::checkForDeleteEndInListItem(KoXmlElement &element, bool checkRecursively) { bool returnValue = false; KoXmlElement childElement; forEachElement(childElement, element) { if (childElement.localName() == "p") { if (childElement.firstChild().toElement().localName() == "removed-content") { returnValue = true; break; } } else if ((childElement.localName() == "list") && (checkRecursively)) { KoXmlElement listItem; forEachElement(listItem, childElement) { returnValue = checkForDeleteStartInListItem(listItem); if (returnValue) break; } } else { } if (returnValue) break; } return returnValue; } QString KoTextWriter::Private::findChangeIdForListItemMerge(const KoXmlElement &element) { QString changeId; KoXmlElement listItemElement; forEachElement (listItemElement, element) { if ((listItemElement.localName() == "list-item") || (listItemElement.localName() == "list-header")) { KoXmlElement childElement; forEachElement (childElement, listItemElement) { if (childElement.localName() == "p") { KoXmlElement removedContentElement = childElement.lastChild().toElement(); if (removedContentElement.localName() == "removed-content") { changeId = removedContentElement.attributeNS(KoXmlNS::delta, "removal-change-idref"); break; } } else if (childElement.localName() == "list") { changeId = findChangeIdForListItemMerge(childElement); break; } else { // Not Needed } } } } return changeId; } void KoTextWriter::Private::writeAttributes(QTextStream &outputXmlStream, KoXmlElement &element) { QList > attributes = element.attributeFullNames(); QPair attributeNamePair; foreach (attributeNamePair, attributes) { if (attributeNamePair.first == KoXmlNS::text) { outputXmlStream << " text:" << attributeNamePair.second << "="; outputXmlStream << "\"" << element.attributeNS(KoXmlNS::text, attributeNamePair.second) << "\""; } else if (attributeNamePair.first == KoXmlNS::delta) { outputXmlStream << " delta:" << attributeNamePair.second << "="; outputXmlStream << "\"" << element.attributeNS(KoXmlNS::delta, attributeNamePair.second) << "\""; } else { //To Be Added when needed } } } void KoTextWriter::Private::writeNode(QTextStream &outputXmlStream, KoXmlNode &node, bool writeOnlyChildren) { if (node.isText()) { outputXmlStream << node.toText().data(); } else if (node.isElement()) { KoXmlElement element = node.toElement(); if ((element.localName() == "removed-content") && !element.childNodesCount()) { return; } if (!writeOnlyChildren) { outputXmlStream << "<" << element.prefix() << ":" << element.localName(); writeAttributes(outputXmlStream,element); outputXmlStream << ">"; } for (KoXmlNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { writeNode(outputXmlStream, node); } if (!writeOnlyChildren) { outputXmlStream << ""; } } } QString KoTextWriter::Private::createXmlId() { QString uuid = QUuid::createUuid().toString(); uuid.remove('{'); uuid.remove('}'); return uuid; } diff --git a/libs/kotext/styles/KoCharacterStyle.cpp b/libs/kotext/styles/KoCharacterStyle.cpp index dec30a95e6f..ccc9ed5c27f 100644 --- a/libs/kotext/styles/KoCharacterStyle.cpp +++ b/libs/kotext/styles/KoCharacterStyle.cpp @@ -1,2275 +1,2275 @@ /* This file is part of the KDE project * Copyright (C) 2006-2009 Thomas Zander * Copyright (C) 2007 Sebastian Sauer * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2011 Stuart Dickson * * 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 "KoCharacterStyle.h" #include "KoTableCellStyle.h" #include "Styles_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoTextSharedLoadingData.h" #include "KoInlineTextObjectManager.h" #include "KoTextDocument.h" #ifdef SHOULD_BUILD_FONT_CONVERSION #include #include #include #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_TYPES_H #include FT_OUTLINE_H #include FT_RENDER_H #include FT_TRUETYPE_TABLES_H #include FT_TRUETYPE_TABLES_H #include FT_SFNT_NAMES_H #endif #include #include "KoTextDebug.h" #ifdef SHOULD_BUILD_FONT_CONVERSION QMap textScaleMap; #endif //SHOULD_BUILD_FONT_CONVERSION class KoCharacterStyle::Private { public: Private(); ~Private() { } void setProperty(int key, const QVariant &value) { stylesPrivate.add(key, value); } qreal propertyDouble(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyDouble(key); else if (defaultStyle) return defaultStyle->d->propertyDouble(key); return 0.0; } return variant.toDouble(); } int propertyInt(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyInt(key); else if (defaultStyle) return defaultStyle->d->propertyInt(key); return 0; } return variant.toInt(); } QString propertyString(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyString(key); else if (defaultStyle) return defaultStyle->d->propertyString(key); return QString(); } return qvariant_cast(variant); } bool propertyBoolean(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyBoolean(key); else if (defaultStyle) return defaultStyle->d->propertyBoolean(key); return false; } return variant.toBool(); } QColor propertyColor(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyColor(key); else if (defaultStyle) return defaultStyle->d->propertyColor(key); return QColor(); } return variant.value(); } // problem with fonts in linux and windows is that true type fonts have more than one metric // they have normal metric placed in font header table // microsoft metric placed in os2 table // apple metric placed in os2 table // ms-word is probably using CreateFontIndirect and GetOutlineTextMetric function to calculate line height // and this functions are using windows gdi environment which is using microsoft font metric placed in os2 table // qt on linux is using normal font metric // this two metrics are different and change from font to font // this font stretch is needed if we want to have exact line height as in ms-word and oo // // font_size * font_stretch = windows_font_height qreal calculateFontYStretch(QString fontFamily); StylePrivate hardCodedDefaultStyle; QString name; StylePrivate stylesPrivate; KoCharacterStyle *parentStyle; KoCharacterStyle *defaultStyle; bool m_inUse; }; KoCharacterStyle::Private::Private() : parentStyle(0), defaultStyle(0), m_inUse(false) { //set the minimal default properties hardCodedDefaultStyle.add(QTextFormat::FontFamily, QString("Sans Serif")); hardCodedDefaultStyle.add(QTextFormat::FontPointSize, 12.0); hardCodedDefaultStyle.add(QTextFormat::ForegroundBrush, QBrush(Qt::black)); hardCodedDefaultStyle.add(KoCharacterStyle::FontYStretch, 1); #if QT_VERSION >= 0x040800 hardCodedDefaultStyle.add(QTextFormat::FontHintingPreference, QFont::PreferNoHinting); #endif } void KoCharacterStyle::ensureMinimalProperties(QTextCharFormat &format) const { if (d->defaultStyle) { QMap props = d->defaultStyle->d->stylesPrivate.properties(); QMap::const_iterator it = props.constBegin(); while (it != props.constEnd()) { // in case there is already a foreground color don't apply the use window font color as then the forground color // should be used. if (it.key() == KoCharacterStyle::UseWindowFontColor && format.hasProperty(QTextFormat::ForegroundBrush)) { ++it; continue; } // in case there is already a use window font color don't apply the forground brush as this overwrite the foreground color if (it.key() == QTextFormat::ForegroundBrush && format.hasProperty(KoCharacterStyle::UseWindowFontColor)) { ++it; continue; } if (!it.value().isNull() && !format.hasProperty(it.key())) { format.setProperty(it.key(), it.value()); } ++it; } } QMap props = d->hardCodedDefaultStyle.properties(); QMap::const_iterator it = props.constBegin(); while (it != props.constEnd()) { if (!it.value().isNull() && !format.hasProperty(it.key())) { if (it.key() == QTextFormat::ForegroundBrush && format.hasProperty(KoCharacterStyle::UseWindowFontColor)) { ++it; continue; } format.setProperty(it.key(), it.value()); } ++it; } } qreal KoCharacterStyle::Private::calculateFontYStretch(QString /*fontFamily*/) { qreal stretch = 1; #ifdef SHOULD_BUILD_FONT_CONVERSION if (textScaleMap.contains(fontFamily)) { return textScaleMap.value(fontFamily); } FcResult result = FcResultMatch; FT_Library library; FT_Face face; int id = 0; int error = 0; - QByteArray fontName = fontFamily.toAscii(); + QByteArray fontName = fontFamily.toLatin1(); //TODO http://freedesktop.org/software/fontconfig/fontconfig-devel/x19.html // we should specify slant and weight too FcPattern *font = FcPatternBuild (0, FC_FAMILY, FcTypeString,fontName.data(), FC_SIZE, FcTypeDouble, (qreal)11, NULL); if (font == 0) { return 1; } // find font FcPattern *matched = 0; matched = FcFontMatch (0, font, &result); if (matched == 0) { FcPatternDestroy (font); return 1; } // get font family name char * str = 0; result = FcPatternGetString (matched, FC_FAMILY, 0,(FcChar8**) &str); if (result != FcResultMatch || str == 0) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // check if right font was found QByteArray foundFontFamily = QByteArray::fromRawData(str, strlen(str)); if (foundFontFamily != fontName) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get path to font str = 0; result = FcPatternGetString (matched, FC_FILE, 0,(FcChar8**) &str); if (result != FcResultMatch) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get index of font inside the font file result = FcPatternGetInteger (matched, FC_INDEX, 0, &id); if (result != FcResultMatch) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // initialize freetype error = FT_Init_FreeType( &library ); if (error) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get font metric error = FT_New_Face (library,(char *) str, id, &face); if (error) { FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get font metric os2 table TT_OS2 *os2; os2 = (TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2); if(os2 == 0) { FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get font metric header table TT_Header *header; header = (TT_Header *) FT_Get_Sfnt_Table (face, ft_sfnt_head); if(header == 0) { FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // check if the data is valid if (header->Units_Per_EM == 0 || (os2->usWinAscent + os2->usWinDescent) == 0) { FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // compute font height stretch // font_size * font_stretch = windows_font_height qreal height = os2->usWinAscent + os2->usWinDescent; height = height * (2048 / header->Units_Per_EM); stretch = (1.215 * height)/2500; stretch = (1.15 * height)/2500; // seems a better guess but probably not right FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); textScaleMap.insert(fontFamily, stretch); #endif //SHOULD_BUILD_FONT_CONVERSION return stretch; } KoCharacterStyle::KoCharacterStyle(QObject *parent) : QObject(parent), d(new Private()) { } KoCharacterStyle::KoCharacterStyle(const QTextCharFormat &format, QObject *parent) : QObject(parent), d(new Private()) { copyProperties(format); } KoCharacterStyle::Type KoCharacterStyle::styleType() const { return KoCharacterStyle::CharacterStyle; } void KoCharacterStyle::copyProperties(const KoCharacterStyle *style) { d->stylesPrivate = style->d->stylesPrivate; setName(style->name()); // make sure we emit property change d->parentStyle = style->d->parentStyle; d->defaultStyle = style->d->defaultStyle; } void KoCharacterStyle::copyProperties(const QTextCharFormat &format) { d->stylesPrivate = format.properties(); } KoCharacterStyle *KoCharacterStyle::clone(QObject *parent) const { KoCharacterStyle *newStyle = new KoCharacterStyle(parent); newStyle->copyProperties(this); return newStyle; } KoCharacterStyle::~KoCharacterStyle() { delete d; } void KoCharacterStyle::setDefaultStyle(KoCharacterStyle *defaultStyle) { d->defaultStyle = defaultStyle; } void KoCharacterStyle::setParentStyle(KoCharacterStyle *parent) { d->parentStyle = parent; } KoCharacterStyle *KoCharacterStyle::parentStyle() const { return d->parentStyle; } QPen KoCharacterStyle::textOutline() const { QVariant variant = value(QTextFormat::TextOutline); if (variant.isNull()) { return QPen(Qt::NoPen); } return qvariant_cast(variant); } QBrush KoCharacterStyle::background() const { QVariant variant = value(QTextFormat::BackgroundBrush); if (variant.isNull()) { return QBrush(); } return qvariant_cast(variant); } void KoCharacterStyle::clearBackground() { d->stylesPrivate.remove(QTextCharFormat::BackgroundBrush); } QBrush KoCharacterStyle::foreground() const { QVariant variant = value(QTextFormat::ForegroundBrush); if (variant.isNull()) { return QBrush(); } return qvariant_cast(variant); } void KoCharacterStyle::clearForeground() { d->stylesPrivate.remove(QTextCharFormat::ForegroundBrush); } void KoCharacterStyle::applyStyle(QTextCharFormat &format, bool emitSignal) const { if (d->parentStyle) { d->parentStyle->applyStyle(format); } bool fontSizeSet = false; // if this style has already set size don't apply the relatives const QMap props = d->stylesPrivate.properties(); QMap::const_iterator it = props.begin(); QList clearProperty; while (it != props.end()) { if (!it.value().isNull()) { if (it.key() == KoCharacterStyle::PercentageFontSize && !fontSizeSet) { qreal size = it.value().toDouble() / 100.0; if (format.hasProperty(QTextFormat::FontPointSize)) { size *= format.doubleProperty(QTextFormat::FontPointSize); } else { size *= 12.0; } format.setProperty(QTextFormat::FontPointSize, size); } else if (it.key() == KoCharacterStyle::AdditionalFontSize && !fontSizeSet) { qreal size = it.value().toDouble() / 100.0; if (format.hasProperty(QTextFormat::FontPointSize)) { size += format.doubleProperty(QTextFormat::FontPointSize); } else { size += 12.0; } format.setProperty(QTextFormat::FontPointSize, size); } else if (it.key() == QTextFormat::FontFamily) { if (!props.contains(QTextFormat::FontStyleHint)) { clearProperty.append(QTextFormat::FontStyleHint); } if (!props.contains(QTextFormat::FontFixedPitch)) { clearProperty.append(QTextFormat::FontFixedPitch); } if (!props.contains(KoCharacterStyle::FontCharset)) { clearProperty.append(KoCharacterStyle::FontCharset); } format.setProperty(it.key(), it.value()); } else { kDebug(32500) << "setProperty" << it.key() << it.value(); format.setProperty(it.key(), it.value()); } if (it.key() == QTextFormat::FontPointSize) { fontSizeSet = true; } if (it.key() == QTextFormat::ForegroundBrush) { clearProperty.append(KoCharacterStyle::UseWindowFontColor); } else if (it.key() == KoCharacterStyle::UseWindowFontColor) { clearProperty.append(QTextFormat::ForegroundBrush); } } ++it; } foreach (int property, clearProperty) { kDebug(32500) << "clearProperty" << property; format.clearProperty(property); } if (emitSignal) { emit styleApplied(this); d->m_inUse = true; } } KoCharacterStyle *KoCharacterStyle::autoStyle(const QTextCharFormat &format, QTextCharFormat blockCharFormat) const { KoCharacterStyle *autoStyle = new KoCharacterStyle(format); applyStyle(blockCharFormat, false); ensureMinimalProperties(blockCharFormat); autoStyle->removeDuplicates(blockCharFormat); autoStyle->setParentStyle(const_cast(this)); // remove StyleId if it is there as it is not a property of the style itself and will not be written out // so it should not be part of the autostyle. As otherwise it can happen that the StyleId is the only // property left and then we write out an empty style which is unneeded. // we also need to remove the properties of links as they are saved differently autoStyle->d->stylesPrivate.remove(StyleId); autoStyle->d->stylesPrivate.remove(QTextFormat::IsAnchor); autoStyle->d->stylesPrivate.remove(QTextFormat::AnchorHref); autoStyle->d->stylesPrivate.remove(QTextFormat::AnchorName); return autoStyle; } struct FragmentData { FragmentData(const QTextCharFormat &format, int position, int length) : format(format) , position(position) , length(length) {} QTextCharFormat format; int position; int length; }; void KoCharacterStyle::applyStyle(QTextBlock &block) const { QTextCursor cursor(block); QTextCharFormat cf = block.charFormat(); if (!cf.isTableCellFormat()) { cf = KoTextDocument(block.document()).frameCharFormat(); } applyStyle(cf); ensureMinimalProperties(cf); cursor.setBlockCharFormat(cf); // be sure that we keep the InlineInstanceId, anchor information and ChangeTrackerId when applying a style QList fragments; for (QTextBlock::iterator it = block.begin(); it != block.end(); ++it) { QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) { QTextCharFormat format(cf); QVariant v = currentFragment.charFormat().property(InlineInstanceId); if (!v.isNull()) { format.setProperty(InlineInstanceId, v); } v = currentFragment.charFormat().property(ChangeTrackerId); if (!v.isNull()) { format.setProperty(ChangeTrackerId, v); } if (currentFragment.charFormat().isAnchor()) { format.setAnchor(true); format.setAnchorHref(currentFragment.charFormat().anchorHref()); } fragments.append(FragmentData(format, currentFragment.position(), currentFragment.length())); } } foreach (const FragmentData &fragment, fragments) { cursor.setPosition(fragment.position); cursor.setPosition(fragment.position + fragment.length, QTextCursor::KeepAnchor); cursor.setCharFormat(fragment.format); } } void KoCharacterStyle::applyStyle(QTextCursor *selection) const { // FIXME below should be done for each frament in the selection QTextCharFormat cf = selection->charFormat(); applyStyle(cf); ensureMinimalProperties(cf); selection->setCharFormat(cf); } void KoCharacterStyle::unapplyStyle(QTextCharFormat &format) const { if (d->parentStyle) d->parentStyle->unapplyStyle(format); QMap props = d->stylesPrivate.properties(); QMap::const_iterator it = props.constBegin(); while (it != props.constEnd()) { if (!it.value().isNull() && it.value() == format.property(it.key())) { format.clearProperty(it.key()); } ++it; } props = d->hardCodedDefaultStyle.properties(); it = props.constBegin(); while (it != props.constEnd()) { if (!it.value().isNull() && !format.hasProperty(it.key())) { format.setProperty(it.key(), it.value()); } ++it; } } bool KoCharacterStyle::isApplied() const { return d->m_inUse; } void KoCharacterStyle::unapplyStyle(QTextBlock &block) const { QTextCursor cursor(block); QTextCharFormat cf = cursor.blockCharFormat(); unapplyStyle(cf); cursor.setBlockCharFormat(cf); if (block.length() == 1) // only the linefeed return; QTextBlock::iterator iter = block.end(); do { --iter; QTextFragment fragment = iter.fragment(); cursor.setPosition(fragment.position() + 1); cf = cursor.charFormat(); unapplyStyle(cf); cursor.setPosition(fragment.position()); cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor); cursor.setCharFormat(cf); } while (iter != block.begin()); } // OASIS 14.2.29 static void parseOdfLineWidth(const QString &width, KoCharacterStyle::LineWeight &lineWeight, qreal &lineWidth) { lineWidth = 0; lineWeight = KoCharacterStyle::AutoLineWeight; if (width.isEmpty() || width == "auto") lineWeight = KoCharacterStyle::AutoLineWeight; else if (width == "normal") lineWeight = KoCharacterStyle::NormalLineWeight; else if (width == "bold") lineWeight = KoCharacterStyle::BoldLineWeight; else if (width == "thin") lineWeight = KoCharacterStyle::ThinLineWeight; else if (width == "dash") lineWeight = KoCharacterStyle::DashLineWeight; else if (width == "medium") lineWeight = KoCharacterStyle::MediumLineWeight; else if (width == "thick") lineWeight = KoCharacterStyle::ThickLineWeight; else if (width.endsWith('%')) { lineWeight = KoCharacterStyle::PercentLineWeight; lineWidth = width.mid(0, width.length() - 1).toDouble(); } else if (width[width.length()-1].isNumber()) { lineWeight = KoCharacterStyle::LengthLineWeight; lineWidth = width.toDouble(); } else { lineWeight = KoCharacterStyle::LengthLineWeight; lineWidth = KoUnit::parseValue(width); } } // OASIS 14.2.29 static void importOdfLine(const QString &type, const QString &style, KoCharacterStyle::LineStyle &lineStyle, KoCharacterStyle::LineType &lineType) { lineStyle = KoCharacterStyle::NoLineStyle; lineType = KoCharacterStyle::NoLineType; QString fixedType = type; QString fixedStyle = style; if (fixedStyle == "none") fixedType.clear(); else if (fixedType.isEmpty() && !fixedStyle.isEmpty()) fixedType = "single"; else if (!fixedType.isEmpty() && fixedType != "none" && fixedStyle.isEmpty()) { // don't set a style when the type is none fixedStyle = "solid"; } if (fixedType == "single") lineType = KoCharacterStyle::SingleLine; else if (fixedType == "double") lineType = KoCharacterStyle::DoubleLine; if (fixedStyle == "solid") lineStyle = KoCharacterStyle::SolidLine; else if (fixedStyle == "dotted") lineStyle = KoCharacterStyle::DottedLine; else if (fixedStyle == "dash") lineStyle = KoCharacterStyle::DashLine; else if (fixedStyle == "long-dash") lineStyle = KoCharacterStyle::LongDashLine; else if (fixedStyle == "dot-dash") lineStyle = KoCharacterStyle::DotDashLine; else if (fixedStyle == "dot-dot-dash") lineStyle = KoCharacterStyle::DotDotDashLine; else if (fixedStyle == "wave") lineStyle = KoCharacterStyle::WaveLine; } static QString exportOdfLineType(KoCharacterStyle::LineType lineType) { switch (lineType) { case KoCharacterStyle::NoLineType: return "none"; case KoCharacterStyle::SingleLine: return "single"; case KoCharacterStyle::DoubleLine: return "double"; default: return ""; } } static QString exportOdfLineStyle(KoCharacterStyle::LineStyle lineStyle) { switch (lineStyle) { case KoCharacterStyle::NoLineStyle: return "none"; case KoCharacterStyle::SolidLine: return "solid"; case KoCharacterStyle::DottedLine: return "dotted"; case KoCharacterStyle::DashLine: return "dash"; case KoCharacterStyle::LongDashLine: return "long-dash"; case KoCharacterStyle::DotDashLine: return "dot-dash"; case KoCharacterStyle::DotDotDashLine: return "dot-dot-dash"; case KoCharacterStyle::WaveLine: return "wave"; default: return ""; } } static QString exportOdfLineMode(KoCharacterStyle::LineMode lineMode) { switch (lineMode) { case KoCharacterStyle::ContinuousLineMode: return "continuous"; case KoCharacterStyle::SkipWhiteSpaceLineMode: return "skip-white-space"; default: return ""; } } static QString exportOdfLineWidth(KoCharacterStyle::LineWeight lineWeight, qreal lineWidth) { switch (lineWeight) { case KoCharacterStyle::AutoLineWeight: return "auto"; case KoCharacterStyle::NormalLineWeight: return "normal"; case KoCharacterStyle::BoldLineWeight: return "bold"; case KoCharacterStyle::ThinLineWeight: return "thin"; case KoCharacterStyle::DashLineWeight: return "dash"; case KoCharacterStyle::MediumLineWeight: return "medium"; case KoCharacterStyle::ThickLineWeight: return "thick"; case KoCharacterStyle::PercentLineWeight: return QString("%1%").arg(lineWidth); case KoCharacterStyle::LengthLineWeight: return QString("%1pt").arg(lineWidth); default: return QString(); } } static QString exportOdfFontStyleHint(QFont::StyleHint hint) { switch (hint) { case QFont::Serif: return "roman"; case QFont::SansSerif: return "swiss"; case QFont::TypeWriter: return "modern"; case QFont::Decorative: return "decorative"; case QFont::System: return "system"; /*case QFont::Script */ default: return ""; } } void KoCharacterStyle::setFontFamily(const QString &family) { d->setProperty(QTextFormat::FontFamily, family); setFontYStretch(d->calculateFontYStretch(family)); } QString KoCharacterStyle::fontFamily() const { return d->propertyString(QTextFormat::FontFamily); } void KoCharacterStyle::setFontPointSize(qreal size) { d->setProperty(QTextFormat::FontPointSize, size); } void KoCharacterStyle::clearFontPointSize() { d->stylesPrivate.remove(QTextFormat::FontPointSize); } qreal KoCharacterStyle::fontPointSize() const { return d->propertyDouble(QTextFormat::FontPointSize); } void KoCharacterStyle::setFontWeight(int weight) { d->setProperty(QTextFormat::FontWeight, weight); } int KoCharacterStyle::fontWeight() const { return d->propertyInt(QTextFormat::FontWeight); } void KoCharacterStyle::setFontItalic(bool italic) { d->setProperty(QTextFormat::FontItalic, italic); } bool KoCharacterStyle::fontItalic() const { return d->propertyBoolean(QTextFormat::FontItalic); } ///TODO Review legacy fontOverline functions and testing (consider removal) /* void KoCharacterStyle::setFontOverline(bool overline) { d->setProperty(QTextFormat::FontOverline, overline); } bool KoCharacterStyle::fontOverline() const { return d->propertyBoolean(QTextFormat::FontOverline); } */ void KoCharacterStyle::setFontFixedPitch(bool fixedPitch) { d->setProperty(QTextFormat::FontFixedPitch, fixedPitch); } bool KoCharacterStyle::fontFixedPitch() const { return d->propertyBoolean(QTextFormat::FontFixedPitch); } void KoCharacterStyle::setFontStyleHint(QFont::StyleHint styleHint) { d->setProperty(QTextFormat::FontStyleHint, styleHint); } QFont::StyleHint KoCharacterStyle::fontStyleHint() const { return static_cast(d->propertyInt(QTextFormat::FontStyleHint)); } void KoCharacterStyle::setFontKerning(bool enable) { d->setProperty(QTextFormat::FontKerning, enable); } bool KoCharacterStyle::fontKerning() const { return d->propertyBoolean(QTextFormat::FontKerning); } void KoCharacterStyle::setVerticalAlignment(QTextCharFormat::VerticalAlignment alignment) { d->setProperty(QTextFormat::TextVerticalAlignment, alignment); } QTextCharFormat::VerticalAlignment KoCharacterStyle::verticalAlignment() const { return static_cast(d->propertyInt(QTextFormat::TextVerticalAlignment)); } void KoCharacterStyle::setTextOutline(const QPen &pen) { d->setProperty(QTextFormat::TextOutline, pen); } void KoCharacterStyle::setBackground(const QBrush &brush) { d->setProperty(QTextFormat::BackgroundBrush, brush); } void KoCharacterStyle::setForeground(const QBrush &brush) { d->setProperty(QTextFormat::ForegroundBrush, brush); } void KoCharacterStyle::setFontAutoColor(bool use) { d->setProperty(KoCharacterStyle::UseWindowFontColor, use); } QString KoCharacterStyle::name() const { return d->name; } void KoCharacterStyle::setName(const QString &name) { if (name == d->name) return; d->name = name; emit nameChanged(name); } int KoCharacterStyle::styleId() const { return d->propertyInt(StyleId); } void KoCharacterStyle::setStyleId(int id) { d->setProperty(StyleId, id); } QFont KoCharacterStyle::font() const { QFont font; if (d->stylesPrivate.contains(QTextFormat::FontFamily)) font.setFamily(fontFamily()); if (d->stylesPrivate.contains(QTextFormat::FontPointSize)) font.setPointSizeF(fontPointSize()); if (d->stylesPrivate.contains(QTextFormat::FontWeight)) font.setWeight(fontWeight()); if (d->stylesPrivate.contains(QTextFormat::FontItalic)) font.setItalic(fontItalic()); return font; } void KoCharacterStyle::setHasHyphenation(bool on) { d->setProperty(HasHyphenation, on); } bool KoCharacterStyle::hasHyphenation() const { return d->propertyBoolean(HasHyphenation); } void KoCharacterStyle::setHyphenationPushCharCount(int count) { if (count > 0) d->setProperty(HyphenationPushCharCount, count); else d->stylesPrivate.remove(HyphenationPushCharCount); } int KoCharacterStyle::hyphenationPushCharCount() const { if (hasProperty(HyphenationPushCharCount)) return d->propertyInt(HyphenationPushCharCount); return 0; } void KoCharacterStyle::setHyphenationRemainCharCount(int count) { if (count > 0) d->setProperty(HyphenationRemainCharCount, count); else d->stylesPrivate.remove(HyphenationRemainCharCount); } int KoCharacterStyle::hyphenationRemainCharCount() const { if (hasProperty(HyphenationRemainCharCount)) return d->propertyInt(HyphenationRemainCharCount); return 0; } void KoCharacterStyle::setStrikeOutStyle(KoCharacterStyle::LineStyle strikeOut) { d->setProperty(StrikeOutStyle, strikeOut); } KoCharacterStyle::LineStyle KoCharacterStyle::strikeOutStyle() const { return (KoCharacterStyle::LineStyle) d->propertyInt(StrikeOutStyle); } void KoCharacterStyle::setStrikeOutType(LineType lineType) { d->setProperty(StrikeOutType, lineType); } KoCharacterStyle::LineType KoCharacterStyle::strikeOutType() const { return (KoCharacterStyle::LineType) d->propertyInt(StrikeOutType); } void KoCharacterStyle::setStrikeOutColor(const QColor &color) { d->setProperty(StrikeOutColor, color); } QColor KoCharacterStyle::strikeOutColor() const { return d->propertyColor(StrikeOutColor); } void KoCharacterStyle::setStrikeOutWidth(LineWeight weight, qreal width) { d->setProperty(KoCharacterStyle::StrikeOutWeight, weight); d->setProperty(KoCharacterStyle::StrikeOutWidth, width); } void KoCharacterStyle::strikeOutWidth(LineWeight &weight, qreal &width) const { weight = (KoCharacterStyle::LineWeight) d->propertyInt(KoCharacterStyle::StrikeOutWeight); width = d->propertyDouble(KoCharacterStyle::StrikeOutWidth); } void KoCharacterStyle::setStrikeOutMode(LineMode lineMode) { d->setProperty(StrikeOutMode, lineMode); } void KoCharacterStyle::setStrikeOutText(const QString &text) { d->setProperty(StrikeOutText, text); } QString KoCharacterStyle::strikeOutText() const { return d->propertyString(StrikeOutText); } KoCharacterStyle::LineMode KoCharacterStyle::strikeOutMode() const { return (KoCharacterStyle::LineMode) d->propertyInt(StrikeOutMode); } void KoCharacterStyle::setOverlineStyle(KoCharacterStyle::LineStyle overline) { d->setProperty(OverlineStyle, overline); } KoCharacterStyle::LineStyle KoCharacterStyle::overlineStyle() const { return (KoCharacterStyle::LineStyle) d->propertyInt(OverlineStyle); } void KoCharacterStyle::setOverlineType(LineType lineType) { d->setProperty(OverlineType, lineType); } KoCharacterStyle::LineType KoCharacterStyle::overlineType() const { return (KoCharacterStyle::LineType) d->propertyInt(OverlineType); } void KoCharacterStyle::setOverlineColor(const QColor &color) { d->setProperty(KoCharacterStyle::OverlineColor, color); } QColor KoCharacterStyle::overlineColor() const { return d->propertyColor(KoCharacterStyle::OverlineColor); } void KoCharacterStyle::setOverlineWidth(LineWeight weight, qreal width) { d->setProperty(KoCharacterStyle::OverlineWeight, weight); d->setProperty(KoCharacterStyle::OverlineWidth, width); } void KoCharacterStyle::overlineWidth(LineWeight &weight, qreal &width) const { weight = (KoCharacterStyle::LineWeight) d->propertyInt(KoCharacterStyle::OverlineWeight); width = d->propertyDouble(KoCharacterStyle::OverlineWidth); } void KoCharacterStyle::setOverlineMode(LineMode mode) { d->setProperty(KoCharacterStyle::OverlineMode, mode); } KoCharacterStyle::LineMode KoCharacterStyle::overlineMode() const { return static_cast(d->propertyInt(KoCharacterStyle::OverlineMode)); } void KoCharacterStyle::setUnderlineStyle(KoCharacterStyle::LineStyle underline) { d->setProperty(UnderlineStyle, underline); } KoCharacterStyle::LineStyle KoCharacterStyle::underlineStyle() const { return (KoCharacterStyle::LineStyle) d->propertyInt(UnderlineStyle); } void KoCharacterStyle::setUnderlineType(LineType lineType) { d->setProperty(UnderlineType, lineType); } KoCharacterStyle::LineType KoCharacterStyle::underlineType() const { return (KoCharacterStyle::LineType) d->propertyInt(UnderlineType); } void KoCharacterStyle::setUnderlineColor(const QColor &color) { d->setProperty(QTextFormat::TextUnderlineColor, color); } QColor KoCharacterStyle::underlineColor() const { return d->propertyColor(QTextFormat::TextUnderlineColor); } void KoCharacterStyle::setUnderlineWidth(LineWeight weight, qreal width) { d->setProperty(KoCharacterStyle::UnderlineWeight, weight); d->setProperty(KoCharacterStyle::UnderlineWidth, width); } void KoCharacterStyle::underlineWidth(LineWeight &weight, qreal &width) const { weight = (KoCharacterStyle::LineWeight) d->propertyInt(KoCharacterStyle::UnderlineWeight); width = d->propertyDouble(KoCharacterStyle::UnderlineWidth); } void KoCharacterStyle::setUnderlineMode(LineMode mode) { d->setProperty(KoCharacterStyle::UnderlineMode, mode); } KoCharacterStyle::LineMode KoCharacterStyle::underlineMode() const { return static_cast(d->propertyInt(KoCharacterStyle::UnderlineMode)); } void KoCharacterStyle::setFontLetterSpacing(qreal spacing) { d->setProperty(KoCharacterStyle::FontLetterSpacing, spacing); } qreal KoCharacterStyle::fontLetterSpacing() const { return d->propertyDouble(KoCharacterStyle::FontLetterSpacing); } void KoCharacterStyle::setFontWordSpacing(qreal spacing) { d->setProperty(QTextCharFormat::FontWordSpacing, spacing); } qreal KoCharacterStyle::fontWordSpacing() const { return d->propertyDouble(QTextCharFormat::FontWordSpacing); } void KoCharacterStyle::setFontCapitalization(QFont::Capitalization capitalization) { d->setProperty(QTextFormat::FontCapitalization, capitalization); } QFont::Capitalization KoCharacterStyle::fontCapitalization() const { return (QFont::Capitalization) d->propertyInt(QTextFormat::FontCapitalization); } void KoCharacterStyle::setFontYStretch(qreal stretch) { d->setProperty(KoCharacterStyle::FontYStretch, stretch); } qreal KoCharacterStyle::fontYStretch() const { return d->propertyDouble(KoCharacterStyle::FontYStretch); } void KoCharacterStyle::setCountry(const QString &country) { if (country.isEmpty()) d->stylesPrivate.remove(KoCharacterStyle::Country); else d->setProperty(KoCharacterStyle::Country, country); } void KoCharacterStyle::setLanguage(const QString &language) { if (language.isEmpty()) d->stylesPrivate.remove(KoCharacterStyle::Language); else d->setProperty(KoCharacterStyle::Language, language); } QString KoCharacterStyle::country() const { return value(KoCharacterStyle::Country).toString(); } QString KoCharacterStyle::language() const { return d->propertyString(KoCharacterStyle::Language); } bool KoCharacterStyle::blinking() const { return d->propertyBoolean(Blink); } void KoCharacterStyle::setBlinking(bool blink) { d->setProperty(KoCharacterStyle::Blink, blink); } bool KoCharacterStyle::hasProperty(int key) const { return d->stylesPrivate.contains(key); } static QString rotationScaleToString(KoCharacterStyle::RotationScale rotationScale) { QString scale = "line-height"; if (rotationScale == KoCharacterStyle::Fixed) { scale = "fixed"; } return scale; } static KoCharacterStyle::RotationScale stringToRotationScale(const QString &scale) { KoCharacterStyle::RotationScale rotationScale = KoCharacterStyle::LineHeight; if (scale == "fixed") { rotationScale = KoCharacterStyle::Fixed; } return rotationScale; } void KoCharacterStyle::setTextRotationAngle(qreal angle) { d->setProperty(TextRotationAngle, angle); } qreal KoCharacterStyle::textRotationAngle() const { return d->propertyDouble(TextRotationAngle); } void KoCharacterStyle::setTextRotationScale(RotationScale scale) { d->setProperty(TextRotationScale, rotationScaleToString(scale)); } KoCharacterStyle::RotationScale KoCharacterStyle::textRotationScale() const { return stringToRotationScale(d->propertyString(TextRotationScale)); } void KoCharacterStyle::setTextScale(int scale) { d->setProperty(TextScale, scale); } int KoCharacterStyle::textScale() const { return d->propertyInt(TextScale); } void KoCharacterStyle::setTextShadow(const KoShadowStyle& shadow) { d->setProperty(TextShadow, qVariantFromValue(shadow)); } KoShadowStyle KoCharacterStyle::textShadow() const { if (hasProperty(TextShadow)) { QVariant shadow = value(TextShadow); if (shadow.canConvert()) return shadow.value(); } return KoShadowStyle(); } void KoCharacterStyle::setTextCombine(KoCharacterStyle::TextCombineType type) { d->setProperty(TextCombine, type); } KoCharacterStyle::TextCombineType KoCharacterStyle::textCombine() const { if (hasProperty(TextCombine)) { return (KoCharacterStyle::TextCombineType) d->propertyInt(TextCombine); } return NoTextCombine; } QChar KoCharacterStyle::textCombineEndChar() const { if (hasProperty(TextCombineEndChar)) { QString val = d->propertyString(TextCombineEndChar); if (val.length() > 0) return val.at(0); } return QChar(); } void KoCharacterStyle::setTextCombineEndChar(const QChar& character) { d->setProperty(TextCombineEndChar, character); } QChar KoCharacterStyle::textCombineStartChar() const { if (hasProperty(TextCombineStartChar)) { QString val = d->propertyString(TextCombineStartChar); if (val.length() > 0) return val.at(0); } return QChar(); } void KoCharacterStyle::setTextCombineStartChar(const QChar& character) { d->setProperty(TextCombineStartChar, character); } void KoCharacterStyle::setFontRelief(KoCharacterStyle::ReliefType relief) { d->setProperty(FontRelief, relief); } KoCharacterStyle::ReliefType KoCharacterStyle::fontRelief() const { if (hasProperty(FontRelief)) return (KoCharacterStyle::ReliefType) d->propertyInt(FontRelief); return KoCharacterStyle::NoRelief; } KoCharacterStyle::EmphasisPosition KoCharacterStyle::textEmphasizePosition() const { if (hasProperty(TextEmphasizePosition)) return (KoCharacterStyle::EmphasisPosition) d->propertyInt(TextEmphasizePosition); return KoCharacterStyle::EmphasisAbove; } void KoCharacterStyle::setTextEmphasizePosition(KoCharacterStyle::EmphasisPosition position) { d->setProperty(TextEmphasizePosition, position); } KoCharacterStyle::EmphasisStyle KoCharacterStyle::textEmphasizeStyle() const { if (hasProperty(TextEmphasizeStyle)) return (KoCharacterStyle::EmphasisStyle) d->propertyInt(TextEmphasizeStyle); return KoCharacterStyle::NoEmphasis; } void KoCharacterStyle::setTextEmphasizeStyle(KoCharacterStyle::EmphasisStyle emphasis) { d->setProperty(TextEmphasizeStyle, emphasis); } void KoCharacterStyle::setPercentageFontSize(qreal percent) { d->setProperty(KoCharacterStyle::PercentageFontSize, percent); } qreal KoCharacterStyle::percentageFontSize() const { return d->propertyDouble(KoCharacterStyle::PercentageFontSize); } void KoCharacterStyle::setAdditionalFontSize(qreal percent) { d->setProperty(KoCharacterStyle::AdditionalFontSize, percent); } qreal KoCharacterStyle::additionalFontSize() const { return d->propertyDouble(KoCharacterStyle::AdditionalFontSize); } void KoCharacterStyle::loadOdf(const KoXmlElement *element, KoShapeLoadingContext &scontext, bool loadParents) { KoOdfLoadingContext &context = scontext.odfLoadingContext(); const QString name(element->attributeNS(KoXmlNS::style, "display-name", QString())); if (!name.isEmpty()) { d->name = name; } else { d->name = element->attributeNS(KoXmlNS::style, "name", QString()); } QString family = element->attributeNS(KoXmlNS::style, "family", "text"); context.styleStack().save(); if (loadParents) { context.addStyles(element, family.toLocal8Bit().constData()); // Load all parent } else { context.styleStack().push(*element); } context.styleStack().setTypeProperties("text"); // load the style:text-properties loadOdfProperties(scontext); context.styleStack().restore(); } void KoCharacterStyle::loadOdfProperties(KoShapeLoadingContext &scontext) { KoStyleStack &styleStack = scontext.odfLoadingContext().styleStack(); // The fo:color attribute specifies the foreground color of text. const QString color(styleStack.property(KoXmlNS::fo, "color")); if (!color.isEmpty()) { QColor c(color); if (c.isValid()) { // 3.10.3 setForeground(QBrush(c)); } } QString fontName(styleStack.property(KoXmlNS::fo, "font-family")); if (!fontName.isEmpty()) { // Specify whether a font has a fixed or variable width. // These attributes are ignored if there is no corresponding fo:font-family attribute attached to the same formatting properties element. const QString fontPitch(styleStack.property(KoXmlNS::style, "font-pitch")); if (!fontPitch.isEmpty()) { setFontFixedPitch(fontPitch == "fixed"); } const QString genericFamily(styleStack.property(KoXmlNS::style, "font-family-generic")); if (!genericFamily.isEmpty()) { if (genericFamily == "roman") setFontStyleHint(QFont::Serif); else if (genericFamily == "swiss") setFontStyleHint(QFont::SansSerif); else if (genericFamily == "modern") setFontStyleHint(QFont::TypeWriter); else if (genericFamily == "decorative") setFontStyleHint(QFont::Decorative); else if (genericFamily == "system") setFontStyleHint(QFont::System); else if (genericFamily == "script") { ; // TODO: no hint available in Qt yet, we should at least store it as a property internally! } } const QString fontCharset(styleStack.property(KoXmlNS::style, "font-charset")); if (!fontCharset.isEmpty()) { // this property is not required by Qt, since Qt auto selects the right font based on the text // The only charset of interest to us is x-symbol - this should disable spell checking d->setProperty(KoCharacterStyle::FontCharset, fontCharset); } } const QString fontFamily(styleStack.property(KoXmlNS::style, "font-family")); if (!fontFamily.isEmpty()) fontName = fontFamily; if (styleStack.hasProperty(KoXmlNS::style, "font-name")) { // This font name is a reference to a font face declaration. KoOdfStylesReader &stylesReader = scontext.odfLoadingContext().stylesReader(); const KoXmlElement *fontFace = stylesReader.findStyle(styleStack.property(KoXmlNS::style, "font-name")); if (fontFace != 0) { fontName = fontFace->attributeNS(KoXmlNS::svg, "font-family", ""); KoXmlElement fontFaceElem; forEachElement(fontFaceElem, (*fontFace)) { if (fontFaceElem.tagName() == "font-face-src") { KoXmlElement fontUriElem; forEachElement(fontUriElem, fontFaceElem) { if (fontUriElem.tagName() == "font-face-uri") { QString filename = fontUriElem.attributeNS(KoXmlNS::xlink, "href"); KoStore *store = scontext.odfLoadingContext().store(); if (store->open(filename)) { KoStoreDevice device(store); QByteArray data = device.readAll(); if (device.open(QIODevice::ReadOnly)) { QFontDatabase::addApplicationFontFromData(data); } } } } } } } } if (!fontName.isEmpty()) { // Hmm, the remove "'" could break it's in the middle of the fontname... fontName = fontName.remove('\''); // 'Thorndale' is not known outside OpenOffice so we substitute it // with 'Times New Roman' that looks nearly the same. if (fontName == "Thorndale") fontName = "Times New Roman"; // 'StarSymbol' is written by OpenOffice but they actually mean // 'OpenSymbol'. if (fontName == "StarSymbol") fontName = "OpenSymbol"; fontName.remove(QRegExp("\\sCE$")); // Arial CE -> Arial setFontFamily(fontName); } // Specify the size of a font. The value of these attribute is either an absolute length or a percentage if (styleStack.hasProperty(KoXmlNS::fo, "font-size")) { const QString fontSize(styleStack.property(KoXmlNS::fo, "font-size")); if (!fontSize.isEmpty()) { if (fontSize.endsWith('%')) { setPercentageFontSize(fontSize.left(fontSize.length() - 1).toDouble()); } else { setFontPointSize(KoUnit::parseValue(fontSize)); } } } else { const QString fontSizeRel(styleStack.property(KoXmlNS::style, "font-size-rel")); if (!fontSizeRel.isEmpty()) { setAdditionalFontSize(KoUnit::parseValue(fontSizeRel)); } } // Specify the weight of a font. The permitted values are normal, bold, and numeric values 100-900, in steps of 100. Unsupported numerical values are rounded off to the next supported value. const QString fontWeight(styleStack.property(KoXmlNS::fo, "font-weight")); if (!fontWeight.isEmpty()) { // 3.10.24 int boldness; if (fontWeight == "normal") boldness = 50; else if (fontWeight == "bold") boldness = 75; else // XSL/CSS has 100,200,300...900. Not the same scale as Qt! // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight boldness = fontWeight.toInt() / 10; setFontWeight(boldness); } // Specify whether to use normal or italic font face. const QString fontStyle(styleStack.property(KoXmlNS::fo, "font-style" )); if (!fontStyle.isEmpty()) { // 3.10.19 if (fontStyle == "italic" || fontStyle == "oblique") { // no difference in kotext setFontItalic(true); } else { setFontItalic(false); } } //TODO #if 0 d->m_bWordByWord = styleStack.property(KoXmlNS::style, "text-underline-mode") == "skip-white-space"; // TODO style:text-line-through-mode /* // OO compat code, to move to OO import filter d->m_bWordByWord = (styleStack.hasProperty( KoXmlNS::fo, "score-spaces")) // 3.10.25 && (styleStack.property( KoXmlNS::fo, "score-spaces") == "false"); if( styleStack.hasProperty( KoXmlNS::style, "text-crossing-out" )) { // 3.10.6 QString strikeOutType = styleStack.property( KoXmlNS::style, "text-crossing-out" ); if( strikeOutType =="double-line") m_strikeOutType = S_DOUBLE; else if( strikeOutType =="single-line") m_strikeOutType = S_SIMPLE; else if( strikeOutType =="thick-line") m_strikeOutType = S_SIMPLE_BOLD; // not supported by Words: "slash" and "X" // not supported by OO: stylelines (solid, dash, dot, dashdot, dashdotdot) } */ #endif // overline modes const QString textOverlineMode(styleStack.property( KoXmlNS::style, "text-overline-mode")); if (!textOverlineMode.isEmpty()) { if (textOverlineMode == "skip-white-space") { setOverlineMode(SkipWhiteSpaceLineMode); } else if (textOverlineMode == "continuous") { setOverlineMode(ContinuousLineMode); } } // Specifies whether text is overlined, and if so, whether a single or qreal line will be used for overlining. const QString textOverlineType(styleStack.property(KoXmlNS::style, "text-overline-type")); const QString textOverlineStyle(styleStack.property(KoXmlNS::style, "text-overline-style")); if (!textOverlineType.isEmpty() || !textOverlineStyle.isEmpty()) { // OASIS 14.4.28 LineStyle overlineStyle; LineType overlineType; importOdfLine(textOverlineType, textOverlineStyle, overlineStyle, overlineType); setOverlineStyle(overlineStyle); setOverlineType(overlineType); } const QString textOverlineWidth(styleStack.property(KoXmlNS::style, "text-overline-width")); if (!textOverlineWidth.isEmpty()) { qreal overlineWidth; LineWeight overlineWeight; parseOdfLineWidth(textOverlineWidth, overlineWeight, overlineWidth); setOverlineWidth(overlineWeight, overlineWidth); } // Specifies the color that is used to overline text. The value of this attribute is either font-color or a color. If the value is font-color, the current text color is used for overlining. QString overLineColor = styleStack.property(KoXmlNS::style, "text-overline-color"); // OO 3.10.23, OASIS 14.4.31 if (!overLineColor.isEmpty() && overLineColor != "font-color") { setOverlineColor(QColor(overLineColor)); } else if (overLineColor == "font-color") { setOverlineColor(QColor()); } // underline modes const QString textUndelineMode(styleStack.property( KoXmlNS::style, "text-underline-mode")); if (!textUndelineMode.isEmpty()) { if (textUndelineMode == "skip-white-space") { setUnderlineMode(SkipWhiteSpaceLineMode); } else if (textUndelineMode == "continuous") { setUnderlineMode(ContinuousLineMode); } } // Specifies whether text is underlined, and if so, whether a single or qreal line will be used for underlining. const QString textUnderlineType(styleStack.property(KoXmlNS::style, "text-underline-type")); const QString textUnderlineStyle(styleStack.property(KoXmlNS::style, "text-underline-style")); if (!textUnderlineType.isEmpty() || !textUnderlineStyle.isEmpty()) { // OASIS 14.4.28 LineStyle underlineStyle; LineType underlineType; importOdfLine(textUnderlineType, textUnderlineStyle, underlineStyle, underlineType); setUnderlineStyle(underlineStyle); setUnderlineType(underlineType); } const QString textUnderlineWidth(styleStack.property(KoXmlNS::style, "text-underline-width")); if (!textUnderlineWidth.isEmpty()) { qreal underlineWidth; LineWeight underlineWeight; parseOdfLineWidth(textUnderlineWidth, underlineWeight, underlineWidth); setUnderlineWidth(underlineWeight, underlineWidth); } // Specifies the color that is used to underline text. The value of this attribute is either font-color or a color. If the value is font-color, the current text color is used for underlining. QString underLineColor = styleStack.property(KoXmlNS::style, "text-underline-color"); // OO 3.10.23, OASIS 14.4.31 if (!underLineColor.isEmpty() && underLineColor != "font-color") { setUnderlineColor(QColor(underLineColor)); } else if (underLineColor == "font-color") { setUnderlineColor(QColor()); } const QString textLineThroughType(styleStack.property(KoXmlNS::style, "text-line-through-type")); const QString textLineThroughStyle(styleStack.property(KoXmlNS::style, "text-line-through-style")); if (!textLineThroughType.isEmpty() || !textLineThroughStyle.isEmpty()) { // OASIS 14.4.7 KoCharacterStyle::LineStyle throughStyle; LineType throughType; importOdfLine(textLineThroughType,textLineThroughStyle, throughStyle, throughType); setStrikeOutStyle(throughStyle); setStrikeOutType(throughType); const QString textLineThroughText(styleStack.property(KoXmlNS::style, "text-line-through-text")); if (!textLineThroughText.isEmpty()) { setStrikeOutText(textLineThroughText); } } const QString textLineThroughWidth(styleStack.property(KoXmlNS::style, "text-line-through-width")); if (!textLineThroughWidth.isEmpty()) { qreal throughWidth; LineWeight throughWeight; parseOdfLineWidth(textLineThroughWidth, throughWeight, throughWidth); setStrikeOutWidth(throughWeight, throughWidth); } const QString lineThroughColor(styleStack.property(KoXmlNS::style, "text-line-through-color")); // OO 3.10.23, OASIS 14.4.31 if (!lineThroughColor.isEmpty() && lineThroughColor != "font-color") { setStrikeOutColor(QColor(lineThroughColor)); } const QString lineThroughMode(styleStack.property(KoXmlNS::style, "text-line-through-mode")); if (lineThroughMode == "continuous") { setStrikeOutMode(ContinuousLineMode); } else if (lineThroughMode == "skip-white-space") { setStrikeOutMode(SkipWhiteSpaceLineMode); } const QString textPosition(styleStack.property(KoXmlNS::style, "text-position")); if (!textPosition.isEmpty()) { // OO 3.10.7 if (textPosition.startsWith("super")) setVerticalAlignment(QTextCharFormat::AlignSuperScript); else if (textPosition.startsWith("sub")) setVerticalAlignment(QTextCharFormat::AlignSubScript); else { QRegExp re("(-?[\\d.]+)%.*"); if (re.exactMatch(textPosition)) { int percent = re.capturedTexts()[1].toInt(); if (percent > 0) setVerticalAlignment(QTextCharFormat::AlignSuperScript); else if (percent < 0) setVerticalAlignment(QTextCharFormat::AlignSubScript); else // set explicit to overwrite inherited text-position's setVerticalAlignment(QTextCharFormat::AlignNormal); } } } // The fo:font-variant attribute provides the option to display text as small capitalized letters. const QString textVariant(styleStack.property(KoXmlNS::fo, "font-variant")); if (!textVariant.isEmpty()) { if (textVariant == "small-caps") setFontCapitalization(QFont::SmallCaps); else if (textVariant == "normal") setFontCapitalization(QFont::MixedCase); } // The fo:text-transform attribute specifies text transformations to uppercase, lowercase, and capitalization. else { const QString textTransform(styleStack.property(KoXmlNS::fo, "text-transform")); if (!textTransform.isEmpty()) { if (textTransform == "uppercase") setFontCapitalization(QFont::AllUppercase); else if (textTransform == "lowercase") setFontCapitalization(QFont::AllLowercase); else if (textTransform == "capitalize") setFontCapitalization(QFont::Capitalize); else if (textTransform == "none") setFontCapitalization(QFont::MixedCase); } } const QString foLanguage(styleStack.property(KoXmlNS::fo, "language")); if (!foLanguage.isEmpty()) { setLanguage(foLanguage); } const QString foCountry(styleStack.property(KoXmlNS::fo, "country")); if (!foCountry.isEmpty()) { setCountry(foCountry); } // The fo:background-color attribute specifies the background color of a paragraph. const QString bgcolor(styleStack.property(KoXmlNS::fo, "background-color")); if (!bgcolor.isEmpty()) { QBrush brush = background(); if (bgcolor == "transparent") brush.setStyle(Qt::NoBrush); else { if (brush.style() == Qt::NoBrush) brush.setStyle(Qt::SolidPattern); brush.setColor(bgcolor); // #rrggbb format } setBackground(brush); } // The style:use-window-font-color attribute specifies whether or not the window foreground color should be as used as the foreground color for a light background color and white for a dark background color. const QString useWindowFont(styleStack.property(KoXmlNS::style, "use-window-font-color")); if (!useWindowFont.isEmpty()) { setFontAutoColor(useWindowFont == "true"); } const QString letterKerning(styleStack.property( KoXmlNS::style, "letter-kerning")); if (!letterKerning.isEmpty()) { setFontKerning(letterKerning == "true"); } const QString letterSpacing(styleStack.property(KoXmlNS::fo, "letter-spacing")); if ((!letterSpacing.isEmpty()) && (letterSpacing != "normal")) { qreal space = KoUnit::parseValue(letterSpacing); setFontLetterSpacing(space); } const QString textOutline(styleStack.property(KoXmlNS::style, "text-outline")); if (!textOutline.isEmpty()) { if (textOutline == "true") { setTextOutline(QPen((foreground().style() != Qt::NoBrush)?foreground():QBrush(Qt::black) , 0)); setForeground(Qt::transparent); } else { setTextOutline(QPen(Qt::NoPen)); } } const QString textRotationAngle(styleStack.property(KoXmlNS::style, "text-rotation-angle")); if (!textRotationAngle.isEmpty()) { setTextRotationAngle(KoUnit::parseAngle(textRotationAngle)); } const QString textRotationScale(styleStack.property(KoXmlNS::style, "text-rotation-scale")); if (!textRotationScale.isEmpty()) { setTextRotationScale(stringToRotationScale(textRotationScale)); } const QString textScale(styleStack.property(KoXmlNS::style, "text-scale")); if (!textScale.isEmpty()) { const int scale = (textScale.endsWith('%') ? textScale.left(textScale.length()-1) : textScale).toInt(); setTextScale(scale); } const QString textShadow(styleStack.property(KoXmlNS::fo, "text-shadow")); if (!textShadow.isEmpty()) { KoShadowStyle shadow; if (shadow.loadOdf(textShadow)) setTextShadow(shadow); } const QString textCombine(styleStack.property(KoXmlNS::style, "text-combine")); if (!textCombine.isEmpty()) { if (textCombine == "letters") setTextCombine(TextCombineLetters); else if (textCombine == "lines") setTextCombine(TextCombineLines); else if (textCombine == "none") setTextCombine(NoTextCombine); } const QString textCombineEndChar(styleStack.property(KoXmlNS::style, "text-combine-end-char")); if (!textCombineEndChar.isEmpty()) { setTextCombineEndChar(textCombineEndChar.at(0)); } const QString textCombineStartChar(styleStack.property(KoXmlNS::style, "text-combine-start-char")); if (!textCombineStartChar.isEmpty()) { setTextCombineStartChar(textCombineStartChar.at(0)); } const QString fontRelief(styleStack.property(KoXmlNS::style, "font-relief")); if (!fontRelief.isEmpty()) { if (fontRelief == "none") setFontRelief(KoCharacterStyle::NoRelief); else if (fontRelief == "embossed") setFontRelief(KoCharacterStyle::Embossed); else if (fontRelief == "engraved") setFontRelief(KoCharacterStyle::Engraved); } const QString fontEmphasize(styleStack.property(KoXmlNS::style, "text-emphasize")); if (!fontEmphasize.isEmpty()) { QString style, position; QStringList parts = fontEmphasize.split(' '); style = parts[0]; if (parts.length() > 1) position = parts[1]; if (style == "none") { setTextEmphasizeStyle(NoEmphasis); } else if (style == "accent") { setTextEmphasizeStyle(AccentEmphasis); } else if (style == "circle") { setTextEmphasizeStyle(CircleEmphasis); } else if (style == "disc") { setTextEmphasizeStyle(DiscEmphasis); } else if (style == "dot") { setTextEmphasizeStyle(DotEmphasis); } if (position == "below") { setTextEmphasizePosition(EmphasisBelow); } else if (position == "above") { setTextEmphasizePosition(EmphasisAbove); } } if (styleStack.hasProperty(KoXmlNS::fo, "hyphenate")) setHasHyphenation(styleStack.property(KoXmlNS::fo, "hyphenate") == "true"); if (styleStack.hasProperty(KoXmlNS::fo, "hyphenation-remain-char-count")) { bool ok = false; int count = styleStack.property(KoXmlNS::fo, "hyphenation-remain-char-count").toInt(&ok); if (ok) setHyphenationRemainCharCount(count); } if (styleStack.hasProperty(KoXmlNS::fo, "hyphenation-push-char-count")) { bool ok = false; int count = styleStack.property(KoXmlNS::fo, "hyphenation-push-char-count").toInt(&ok); if (ok) setHyphenationPushCharCount(count); } if (styleStack.hasProperty(KoXmlNS::style, "text-blinking")) { setBlinking(styleStack.property(KoXmlNS::style, "text-blinking") == "true"); } //TODO #if 0 /* Missing properties: style:font-style-name, 3.10.11 - can be ignored, says DV, the other ways to specify a font are more precise fo:letter-spacing, 3.10.16 - not implemented in kotext style:text-relief, 3.10.20 - not implemented in kotext style:text-blinking, 3.10.27 - not implemented in kotext IIRC style:text-combine, 3.10.29/30 - not implemented, see http://www.w3.org/TR/WD-i18n-format/ style:text-emphasis, 3.10.31 - not implemented in kotext style:text-scale, 3.10.33 - not implemented in kotext style:text-rotation-angle, 3.10.34 - not implemented in kotext (kpr rotates whole objects) style:text-rotation-scale, 3.10.35 - not implemented in kotext (kpr rotates whole objects) style:punctuation-wrap, 3.10.36 - not implemented in kotext */ d->m_underLineWidth = 1.0; generateKey(); addRef(); #endif } bool KoCharacterStyle::operator==(const KoCharacterStyle &other) const { return compareCharacterProperties(other); } bool KoCharacterStyle::operator!=(const KoCharacterStyle &other) const { return !compareCharacterProperties(other); } bool KoCharacterStyle::compareCharacterProperties(const KoCharacterStyle &other) const { return other.d->stylesPrivate == d->stylesPrivate; } void KoCharacterStyle::removeDuplicates(const KoCharacterStyle &other) { // In case the current style doesn't have the flag UseWindowFontColor set but the other has it set and they use the same color // remove duplicates will remove the color. However to make it work correctly we need to store the color with the style so it // will be loaded again. We don't store a use-window-font-color="false" as that is not compatible to the way OO/LO does work. // So save the color and restore it after the remove duplicates QBrush brush; if (other.d->propertyBoolean(KoCharacterStyle::UseWindowFontColor) && !d->propertyBoolean(KoCharacterStyle::UseWindowFontColor)) { brush = foreground(); } // this properties should need to be kept if there is a font family defined as these are only evaluated if there is also a font family int keepProperties[] = { QTextFormat::FontStyleHint, QTextFormat::FontFixedPitch, KoCharacterStyle::FontCharset }; QMap keep; for (unsigned int i = 0; i < sizeof(keepProperties); ++i) { if (hasProperty(keepProperties[i])) { keep.insert(keepProperties[i], value(keepProperties[i])); } } this->d->stylesPrivate.removeDuplicates(other.d->stylesPrivate); if (brush.style() != Qt::NoBrush) { setForeground(brush); } // in case the char style has any of the following properties it also needs to have the fontFamily as otherwise // these values will be ignored when loading according to the odf spec if (!hasProperty(QTextFormat::FontFamily)) { if (hasProperty(QTextFormat::FontStyleHint) || hasProperty(QTextFormat::FontFixedPitch) || hasProperty(KoCharacterStyle::FontCharset)) { QString fontFamily = other.fontFamily(); if (!fontFamily.isEmpty()) { setFontFamily(fontFamily); } } } else { for (QMap::const_iterator it(keep.constBegin()); it != keep.constEnd(); ++it) { this->d->stylesPrivate.add(it.key(), it.value()); } } } void KoCharacterStyle::removeDuplicates(const QTextCharFormat &otherFormat) { KoCharacterStyle other(otherFormat); removeDuplicates(other); } void KoCharacterStyle::remove(int key) { d->stylesPrivate.remove(key); } bool KoCharacterStyle::isEmpty() const { return d->stylesPrivate.isEmpty(); } void KoCharacterStyle::saveOdf(KoGenStyle &style) const { if (!d->name.isEmpty() && !style.isDefaultStyle()) { style.addAttribute("style:display-name", d->name); } QList keys = d->stylesPrivate.keys(); foreach(int key, keys) { if (key == QTextFormat::FontWeight) { bool ok = false; int boldness = d->stylesPrivate.value(key).toInt(&ok); if (ok) { if (boldness == QFont::Normal) { style.addProperty("fo:font-weight", "normal", KoGenStyle::TextType); } else if (boldness == QFont::Bold) { style.addProperty("fo:font-weight", "bold", KoGenStyle::TextType); } else { // Remember : Qt and CSS/XSL doesn't have the same scale. Its 100-900 instead of Qts 0-100 style.addProperty("fo:font-weight", qBound(10, boldness, 90) * 10, KoGenStyle::TextType); } } } else if (key == QTextFormat::FontItalic) { if (d->stylesPrivate.value(key).toBool()) { style.addProperty("fo:font-style", "italic", KoGenStyle::TextType); } else { style.addProperty("fo:font-style", "normal", KoGenStyle::TextType); } } else if (key == QTextFormat::FontFamily) { QString fontFamily = d->stylesPrivate.value(key).toString(); style.addProperty("fo:font-family", fontFamily, KoGenStyle::TextType); } else if (key == QTextFormat::FontFixedPitch) { bool fixedPitch = d->stylesPrivate.value(key).toBool(); style.addProperty("style:font-pitch", fixedPitch ? "fixed" : "variable", KoGenStyle::TextType); // if this property is saved we also need to save the fo:font-family attribute as otherwise it will be ignored on loading as defined in the spec style.addProperty("fo:font-family", fontFamily(), KoGenStyle::TextType); } else if (key == QTextFormat::FontStyleHint) { bool ok = false; int styleHint = d->stylesPrivate.value(key).toInt(&ok); if (ok) { QString generic = exportOdfFontStyleHint((QFont::StyleHint) styleHint); if (!generic.isEmpty()) { style.addProperty("style:font-family-generic", generic, KoGenStyle::TextType); } // if this property is saved we also need to save the fo:font-family attribute as otherwise it will be ignored on loading as defined in the spec style.addProperty("fo:font-family", fontFamily(), KoGenStyle::TextType); } } else if (key == QTextFormat::FontKerning) { style.addProperty("style:letter-kerning", fontKerning() ? "true" : "false", KoGenStyle::TextType); } else if (key == QTextFormat::FontCapitalization) { switch (fontCapitalization()) { case QFont::SmallCaps: style.addProperty("fo:font-variant", "small-caps", KoGenStyle::TextType); break; case QFont::MixedCase: style.addProperty("fo:font-variant", "normal", KoGenStyle::TextType); style.addProperty("fo:text-transform", "none", KoGenStyle::TextType); break; case QFont::AllUppercase: style.addProperty("fo:text-transform", "uppercase", KoGenStyle::TextType); break; case QFont::AllLowercase: style.addProperty("fo:text-transform", "lowercase", KoGenStyle::TextType); break; case QFont::Capitalize: style.addProperty("fo:text-transform", "capitalize", KoGenStyle::TextType); break; } } else if (key == OverlineStyle) { bool ok = false; int styleId = d->stylesPrivate.value(key).toInt(&ok); if (ok) { style.addProperty("style:text-overline-style", exportOdfLineStyle((KoCharacterStyle::LineStyle) styleId), KoGenStyle::TextType); } } else if (key == OverlineType) { bool ok = false; int type = d->stylesPrivate.value(key).toInt(&ok); if (ok) { style.addProperty("style:text-overline-type", exportOdfLineType((KoCharacterStyle::LineType) type), KoGenStyle::TextType); } } else if (key == OverlineColor) { QColor color = d->stylesPrivate.value(key).value(); if (color.isValid()) style.addProperty("style:text-overline-color", color.name(), KoGenStyle::TextType); else style.addProperty("style:text-overline-color", "font-color", KoGenStyle::TextType); } else if (key == OverlineMode) { bool ok = false; int mode = d->stylesPrivate.value(key).toInt(&ok); if (ok) { style.addProperty("style:text-overline-mode", exportOdfLineMode((KoCharacterStyle::LineMode) mode), KoGenStyle::TextType); } } else if (key == OverlineWidth) { KoCharacterStyle::LineWeight weight; qreal width; overlineWidth(weight, width); style.addProperty("style:text-overline-width", exportOdfLineWidth(weight, width), KoGenStyle::TextType); } else if (key == UnderlineStyle) { bool ok = false; int styleId = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-underline-style", exportOdfLineStyle((KoCharacterStyle::LineStyle) styleId), KoGenStyle::TextType); } else if (key == UnderlineType) { bool ok = false; int type = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-underline-type", exportOdfLineType((KoCharacterStyle::LineType) type), KoGenStyle::TextType); } else if (key == QTextFormat::TextUnderlineColor) { QColor color = d->stylesPrivate.value(key).value(); if (color.isValid()) style.addProperty("style:text-underline-color", color.name(), KoGenStyle::TextType); else style.addProperty("style:text-underline-color", "font-color", KoGenStyle::TextType); } else if (key == UnderlineMode) { bool ok = false; int mode = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-underline-mode", exportOdfLineMode((KoCharacterStyle::LineMode) mode), KoGenStyle::TextType); } else if (key == UnderlineWidth) { KoCharacterStyle::LineWeight weight; qreal width; underlineWidth(weight, width); style.addProperty("style:text-underline-width", exportOdfLineWidth(weight, width), KoGenStyle::TextType); } else if (key == StrikeOutStyle) { bool ok = false; int styleId = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-line-through-style", exportOdfLineStyle((KoCharacterStyle::LineStyle) styleId), KoGenStyle::TextType); } else if (key == StrikeOutType) { bool ok = false; int type = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-line-through-type", exportOdfLineType((KoCharacterStyle::LineType) type), KoGenStyle::TextType); } else if (key == StrikeOutText) { style.addProperty("style:text-line-through-text", d->stylesPrivate.value(key).toString(), KoGenStyle::TextType); } else if (key == StrikeOutColor) { QColor color = d->stylesPrivate.value(key).value(); if (color.isValid()) style.addProperty("style:text-line-through-color", color.name(), KoGenStyle::TextType); } else if (key == StrikeOutMode) { bool ok = false; int mode = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-line-through-mode", exportOdfLineMode((KoCharacterStyle::LineMode) mode), KoGenStyle::TextType); } else if (key == StrikeOutWidth) { KoCharacterStyle::LineWeight weight; qreal width; strikeOutWidth(weight, width); style.addProperty("style:text-line-through-width", exportOdfLineWidth(weight, width), KoGenStyle::TextType); } else if (key == QTextFormat::BackgroundBrush) { QBrush brush = d->stylesPrivate.value(key).value(); if (brush.style() == Qt::NoBrush) style.addProperty("fo:background-color", "transparent", KoGenStyle::TextType); else style.addProperty("fo:background-color", brush.color().name(), KoGenStyle::TextType); } else if (key == QTextFormat::ForegroundBrush) { QBrush brush = d->stylesPrivate.value(key).value(); if (brush.style() != Qt::NoBrush) { style.addProperty("fo:color", brush.color().name(), KoGenStyle::TextType); } } else if (key == KoCharacterStyle::UseWindowFontColor) { bool use = d->stylesPrivate.value(key).toBool(); style.addProperty("style:use-window-font-color", use ? "true" : "false", KoGenStyle::TextType); } else if (key == QTextFormat::TextVerticalAlignment) { if (verticalAlignment() == QTextCharFormat::AlignSuperScript) style.addProperty("style:text-position", "super", KoGenStyle::TextType); else if (verticalAlignment() == QTextCharFormat::AlignSubScript) style.addProperty("style:text-position", "sub", KoGenStyle::TextType); else if (d->stylesPrivate.contains(QTextFormat::TextVerticalAlignment)) // no superscript or subscript style.addProperty("style:text-position", "0% 100%", KoGenStyle::TextType); } else if (key == QTextFormat::FontPointSize) { // when there is percentageFontSize!=100% property ignore the fontSize property and store the percentage property if ( (!hasProperty(KoCharacterStyle::PercentageFontSize)) || (percentageFontSize()==100)) style.addPropertyPt("fo:font-size", fontPointSize(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::PercentageFontSize) { if(percentageFontSize()!=100) { style.addProperty("fo:font-size", QString::number(percentageFontSize()) + '%', KoGenStyle::TextType); } } else if (key == KoCharacterStyle::Country) { style.addProperty("fo:country", d->stylesPrivate.value(KoCharacterStyle::Country).toString(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::Language) { style.addProperty("fo:language", d->stylesPrivate.value(KoCharacterStyle::Language).toString(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::FontLetterSpacing) { qreal space = fontLetterSpacing(); style.addPropertyPt("fo:letter-spacing", space, KoGenStyle::TextType); } else if (key == QTextFormat::TextOutline) { QPen outline = textOutline(); style.addProperty("style:text-outline", outline.style() == Qt::NoPen ? "false" : "true", KoGenStyle::TextType); } else if (key == KoCharacterStyle::FontCharset) { style.addProperty("style:font-charset", d->stylesPrivate.value(KoCharacterStyle::FontCharset).toString(), KoGenStyle::TextType); // if this property is saved we also need to save the fo:font-family attribute as otherwise it will be ignored on loading as defined in the spec style.addProperty("fo:font-family", fontFamily(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextRotationAngle) { style.addProperty("style:text-rotation-angle", QString::number(textRotationAngle()), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextRotationScale) { RotationScale scale = textRotationScale(); style.addProperty("style:text-rotation-scale", rotationScaleToString(scale), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextScale) { int scale = textScale(); style.addProperty("style:text-scale", QString::number(scale) + '%', KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextShadow) { KoShadowStyle shadow = textShadow(); style.addProperty("fo:text-shadow", shadow.saveOdf(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextCombine) { KoCharacterStyle::TextCombineType textCombineType = textCombine(); switch (textCombineType) { case KoCharacterStyle::NoTextCombine: style.addProperty("style:text-combine", "none", KoGenStyle::TextType); break; case KoCharacterStyle::TextCombineLetters: style.addProperty("style:text-combine", "letters", KoGenStyle::TextType); break; case KoCharacterStyle::TextCombineLines: style.addProperty("style:text-combine", "lines", KoGenStyle::TextType); break; } } else if (key == KoCharacterStyle::TextCombineEndChar) { style.addProperty("style:text-combine-end-char", textCombineEndChar(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextCombineStartChar) { style.addProperty("style:text-combine-start-char", textCombineStartChar(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::FontRelief) { KoCharacterStyle::ReliefType relief = fontRelief(); switch (relief) { case KoCharacterStyle::NoRelief: style.addProperty("style:font-relief", "none", KoGenStyle::TextType); break; case KoCharacterStyle::Embossed: style.addProperty("style:font-relief", "embossed", KoGenStyle::TextType); break; case KoCharacterStyle::Engraved: style.addProperty("style:font-relief", "engraved", KoGenStyle::TextType); break; } } else if (key == KoCharacterStyle::TextEmphasizeStyle) { KoCharacterStyle::EmphasisStyle emphasisStyle = textEmphasizeStyle(); KoCharacterStyle::EmphasisPosition position = textEmphasizePosition(); QString odfEmphasis; switch (emphasisStyle) { case KoCharacterStyle::NoEmphasis: odfEmphasis = "none"; break; case KoCharacterStyle::AccentEmphasis: odfEmphasis = "accent"; break; case KoCharacterStyle::CircleEmphasis: odfEmphasis = "circle"; break; case KoCharacterStyle::DiscEmphasis: odfEmphasis = "disc"; break; case KoCharacterStyle::DotEmphasis: odfEmphasis = "dot"; break; } if (hasProperty(KoCharacterStyle::TextEmphasizePosition)) { if (position == KoCharacterStyle::EmphasisAbove) odfEmphasis += " above"; else odfEmphasis += " below"; } style.addProperty("style:text-emphasize", odfEmphasis, KoGenStyle::TextType); } else if (key == KoCharacterStyle::HasHyphenation) { if (hasHyphenation()) style.addProperty("fo:hyphenate", "true", KoGenStyle::TextType); else style.addProperty("fo:hyphenate", "false", KoGenStyle::TextType); } else if (key == KoCharacterStyle::HyphenationPushCharCount) { style.addProperty("fo:hyphenation-push-char-count", hyphenationPushCharCount(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::HyphenationRemainCharCount) { style.addProperty("fo:hyphenation-remain-char-count", hyphenationRemainCharCount(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::Blink) { style.addProperty("style:text-blinking", blinking(), KoGenStyle::TextType); } } //TODO: font name and family } QVariant KoCharacterStyle::value(int key) const { QVariant variant = d->stylesPrivate.value(key); if (variant.isNull()) { if (d->parentStyle) variant = d->parentStyle->value(key); else if (d->defaultStyle) variant = d->defaultStyle->value(key); } return variant; } void KoCharacterStyle::removeHardCodedDefaults() { d->hardCodedDefaultStyle.clearAll(); } #include diff --git a/libs/main/KoDocumentInfo.cpp b/libs/main/KoDocumentInfo.cpp index 7e0f2210a38..18cb0ff3a5a 100644 --- a/libs/main/KoDocumentInfo.cpp +++ b/libs/main/KoDocumentInfo.cpp @@ -1,459 +1,459 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2004 David Faure 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 "KoDocumentInfo.h" #include "KoDocument.h" #include "calligraversion.h" #include "KoOdfWriteStore.h" #include #include "KoXmlNS.h" #include #include #include #include #include #include #include #include #include #include KoDocumentInfo::KoDocumentInfo(QObject *parent) : QObject(parent) { m_aboutTags << "title" << "description" << "subject" << "comments" << "keyword" << "initial-creator" << "editing-cycles" << "date" << "creation-date" << "language"; m_authorTags << "creator" << "initial" << "author-title" << "email" << "telephone" << "telephone-work" << "fax" << "country" << "postal-code" << "city" << "street" << "position" << "company"; setAboutInfo("editing-cycles", "0"); setAboutInfo("initial-creator", i18n("Unknown")); setAboutInfo("creation-date", QDateTime::currentDateTime() .toString(Qt::ISODate)); } KoDocumentInfo::~KoDocumentInfo() { } bool KoDocumentInfo::load(const KoXmlDocument &doc) { if (!loadAboutInfo(doc.documentElement())) return false; if (!loadAuthorInfo(doc.documentElement())) return false; return true; } bool KoDocumentInfo::loadOasis(const KoXmlDocument &metaDoc) { KoXmlNode t = KoXml::namedItemNS(metaDoc, KoXmlNS::office, "document-meta"); KoXmlNode office = KoXml::namedItemNS(t, KoXmlNS::office, "meta"); if (office.isNull()) return false; if (!loadOasisAboutInfo(office)) return false; if (!loadOasisAuthorInfo(office)) return false; return true; } QDomDocument KoDocumentInfo::save() { saveParameters(); QDomDocument doc = KoDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); QDomElement s = saveAboutInfo(doc); if (!s.isNull()) doc.documentElement().appendChild(s); s = saveAuthorInfo(doc); if (!s.isNull()) doc.documentElement().appendChild(s); if (doc.documentElement().isNull()) return QDomDocument(); return doc; } bool KoDocumentInfo::saveOasis(KoStore *store) { saveParameters(); KoStoreDevice dev(store); KoXmlWriter* xmlWriter = KoOdfWriteStore::createOasisXmlWriter(&dev, "office:document-meta"); xmlWriter->startElement("office:meta"); xmlWriter->startElement("meta:generator"); xmlWriter->addTextNode(QString("Calligra/%1") .arg(CALLIGRA_VERSION_STRING)); xmlWriter->endElement(); if (!saveOasisAboutInfo(*xmlWriter)) return false; if (!saveOasisAuthorInfo(*xmlWriter)) return false; xmlWriter->endElement(); xmlWriter->endElement(); // root element xmlWriter->endDocument(); delete xmlWriter; return true; } void KoDocumentInfo::setAuthorInfo(const QString &info, const QString &data) { if (!m_authorTags.contains(info)) { return; } m_authorInfoOverride.insert(info, data); } void KoDocumentInfo::setActiveAuthorInfo(const QString &info, const QString &data) { if (!m_authorTags.contains(info)) { return; } if (data.isEmpty()) { m_authorInfo.remove(info); } else { m_authorInfo.insert(info, data); } } QString KoDocumentInfo::authorInfo(const QString &info) const { if (!m_authorTags.contains(info)) return QString(); return m_authorInfo[ info ]; } void KoDocumentInfo::setAboutInfo(const QString &info, const QString &data) { if (!m_aboutTags.contains(info)) return; m_aboutInfo.insert(info, data); emit infoUpdated(info, data); } QString KoDocumentInfo::aboutInfo(const QString &info) const { if (!m_aboutTags.contains(info)) { return QString(); } return m_aboutInfo[info]; } bool KoDocumentInfo::saveOasisAuthorInfo(KoXmlWriter &xmlWriter) { foreach(const QString & tag, m_authorTags) { if (!authorInfo(tag).isEmpty() && tag == "creator") { xmlWriter.startElement("dc:creator"); xmlWriter.addTextNode(authorInfo("creator")); xmlWriter.endElement(); } else if (!authorInfo(tag).isEmpty()) { xmlWriter.startElement("meta:user-defined"); xmlWriter.addAttribute("meta:name", tag); xmlWriter.addTextNode(authorInfo(tag)); xmlWriter.endElement(); } } return true; } bool KoDocumentInfo::loadOasisAuthorInfo(const KoXmlNode &metaDoc) { KoXmlElement e = KoXml::namedItemNS(metaDoc, KoXmlNS::dc, "creator"); if (!e.isNull() && !e.text().isEmpty()) setActiveAuthorInfo("creator", e.text()); KoXmlNode n = metaDoc.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (!n.isElement()) continue; KoXmlElement e = n.toElement(); if (!(e.namespaceURI() == KoXmlNS::meta && e.localName() == "user-defined" && !e.text().isEmpty())) continue; QString name = e.attributeNS(KoXmlNS::meta, "name", QString()); setActiveAuthorInfo(name, e.text()); } return true; } bool KoDocumentInfo::loadAuthorInfo(const KoXmlElement &e) { KoXmlNode n = e.namedItem("author").firstChild(); for (; !n.isNull(); n = n.nextSibling()) { KoXmlElement e = n.toElement(); if (e.isNull()) continue; if (e.tagName() == "full-name") setActiveAuthorInfo("creator", e.text().trimmed()); else setActiveAuthorInfo(e.tagName(), e.text().trimmed()); } return true; } QDomElement KoDocumentInfo::saveAuthorInfo(QDomDocument &doc) { QDomElement e = doc.createElement("author"); QDomElement t; foreach(const QString &tag, m_authorTags) { if (tag == "creator") t = doc.createElement("full-name"); else t = doc.createElement(tag); e.appendChild(t); t.appendChild(doc.createTextNode(authorInfo(tag))); } return e; } bool KoDocumentInfo::saveOasisAboutInfo(KoXmlWriter &xmlWriter) { foreach(const QString &tag, m_aboutTags) { if (!aboutInfo(tag).isEmpty() || tag == "title") { if (tag == "keyword") { foreach(const QString & tmp, aboutInfo("keyword").split(';')) { xmlWriter.startElement("meta:keyword"); xmlWriter.addTextNode(tmp); xmlWriter.endElement(); } } else if (tag == "title" || tag == "description" || tag == "subject" || tag == "date" || tag == "language") { QByteArray elementName(QString("dc:" + tag).toLatin1()); xmlWriter.startElement(elementName); xmlWriter.addTextNode(aboutInfo(tag)); xmlWriter.endElement(); } else { QByteArray elementName(QString("meta:" + tag).toLatin1()); xmlWriter.startElement(elementName); xmlWriter.addTextNode(aboutInfo(tag)); xmlWriter.endElement(); } } } return true; } bool KoDocumentInfo::loadOasisAboutInfo(const KoXmlNode &metaDoc) { QStringList keywords; KoXmlElement e; forEachElement(e, metaDoc) { QString tag(e.localName()); if (! m_aboutTags.contains(tag) && tag != "generator") { continue; } //kDebug( 30003 )<<"localName="< 0) { setAboutInfo("keyword", keywords.join(", ")); } return true; } bool KoDocumentInfo::loadAboutInfo(const KoXmlElement &e) { KoXmlNode n = e.namedItem("about").firstChild(); KoXmlElement tmp; for (; !n.isNull(); n = n.nextSibling()) { tmp = n.toElement(); if (tmp.isNull()) continue; if (tmp.tagName() == "abstract") setAboutInfo("comments", tmp.text()); setAboutInfo(tmp.tagName(), tmp.text()); } return true; } QDomElement KoDocumentInfo::saveAboutInfo(QDomDocument &doc) { QDomElement e = doc.createElement("about"); QDomElement t; foreach(const QString &tag, m_aboutTags) { if (tag == "comments") { t = doc.createElement("abstract"); e.appendChild(t); t.appendChild(doc.createCDATASection(aboutInfo(tag))); } else { t = doc.createElement(tag); e.appendChild(t); t.appendChild(doc.createTextNode(aboutInfo(tag))); } } return e; } void KoDocumentInfo::saveParameters() { KoDocument *doc = dynamic_cast< KoDocument *>(parent()); if (doc && doc->isAutosaving()) { return; } setAboutInfo("editing-cycles", QString::number(aboutInfo("editing-cycles").toInt() + 1)); setAboutInfo("date", QDateTime::currentDateTime().toString(Qt::ISODate)); KConfig *config = KoGlobal::calligraConfig(); config->reparseConfiguration(); KConfigGroup authorGroup(config, "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); KGlobal::config()->reparseConfiguration(); KConfigGroup appAuthorGroup(KGlobal::config(), "Author"); QString profile = appAuthorGroup.readEntry("active-profile", ""); if (profiles.contains(profile)) { KConfigGroup cgs(&authorGroup, "Author-" + profile); setActiveAuthorInfo("creator", cgs.readEntry("creator")); setActiveAuthorInfo("initial", cgs.readEntry("initial")); setActiveAuthorInfo("author-title", cgs.readEntry("author-title")); setActiveAuthorInfo("email", cgs.readEntry("email")); setActiveAuthorInfo("telephone", cgs.readEntry("telephone")); setActiveAuthorInfo("telephone-work", cgs.readEntry("telephone-work")); setActiveAuthorInfo("fax", cgs.readEntry("fax")); setActiveAuthorInfo("country",cgs.readEntry("country")); setActiveAuthorInfo("postal-code",cgs.readEntry("postal-code")); setActiveAuthorInfo("city", cgs.readEntry("city")); setActiveAuthorInfo("street", cgs.readEntry("street")); setActiveAuthorInfo("position", cgs.readEntry("position")); setActiveAuthorInfo("company", cgs.readEntry("company")); } else { if (profile == "anonymous") { setActiveAuthorInfo("creator", QString()); setActiveAuthorInfo("telephone", QString()); setActiveAuthorInfo("telephone-work", QString()); setActiveAuthorInfo("email", QString()); } else { KUser user(KUser::UseRealUserID); setActiveAuthorInfo("creator", user.property(KUser::FullName).toString()); setActiveAuthorInfo("telephone-work", user.property(KUser::WorkPhone).toString()); setActiveAuthorInfo("telephone", user.property(KUser::HomePhone).toString()); KEMailSettings eMailSettings; setActiveAuthorInfo("email", eMailSettings.getSetting(KEMailSettings::EmailAddress)); } setActiveAuthorInfo("initial", ""); setActiveAuthorInfo("author-title", ""); setActiveAuthorInfo("fax", ""); setActiveAuthorInfo("country", ""); setActiveAuthorInfo("postal-code", ""); setActiveAuthorInfo("city", ""); setActiveAuthorInfo("street", ""); setActiveAuthorInfo("position", ""); setActiveAuthorInfo("company", ""); } //alllow author info set programatically to override info from author profile foreach(const QString &tag, m_authorTags) { if (m_authorInfoOverride.contains(tag)) { setActiveAuthorInfo(tag, m_authorInfoOverride.value(tag)); } } } void KoDocumentInfo::resetMetaData() { setAboutInfo("editing-cycles", QString::number(0)); setAboutInfo("initial-creator", authorInfo("creator")); setAboutInfo("creation-date", QDateTime::currentDateTime().toString(Qt::ISODate)); } QString KoDocumentInfo::originalGenerator() const { return m_generator; } void KoDocumentInfo::setOriginalGenerator(const QString &generator) { m_generator = generator; } #include diff --git a/libs/main/KoFilterChain.cpp b/libs/main/KoFilterChain.cpp index d10d7b6a2c5..3456ee1358a 100644 --- a/libs/main/KoFilterChain.cpp +++ b/libs/main/KoFilterChain.cpp @@ -1,542 +1,542 @@ /* This file is part of the Calligra libraries Copyright (C) 2001 Werner Trobin 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 "KoFilterChain.h" #include "KoFilterManager.h" // KoFilterManager::filterAvailable, private API #include "KoDocumentEntry.h" #include "KoFilterEntry.h" #include "KoDocument.h" #include "KoPart.h" #include "PriorityQueue_p.h" #include "KoFilterGraph.h" #include "KoFilterEdge.h" #include "KoFilterChainLink.h" #include "KoFilterVertex.h" #include #include #include #include #include // UINT_MAX // Those "defines" are needed in the setupConnections method below. // Please always keep the strings and the length in sync! using namespace CalligraFilter; KoFilterChain::KoFilterChain(const KoFilterManager* manager) : m_manager(manager), m_state(Beginning), m_inputStorage(0), m_inputStorageDevice(0), m_outputStorage(0), m_outputStorageDevice(0), m_inputDocument(0), m_outputDocument(0), m_inputTempFile(0), m_outputTempFile(0), m_inputQueried(Nil), m_outputQueried(Nil), d(0) { } KoFilterChain::~KoFilterChain() { m_chainLinks.deleteAll(); if (filterManagerParentChain() && filterManagerParentChain()->m_outputStorage) filterManagerParentChain()->m_outputStorage->leaveDirectory(); manageIO(); // Called for the 2nd time in a row -> clean up } KoFilter::ConversionStatus KoFilterChain::invokeChain() { KoFilter::ConversionStatus status = KoFilter::OK; m_state = Beginning; int count = m_chainLinks.count(); // This is needed due to nasty Microsoft design const ChainLink* parentChainLink = 0; if (filterManagerParentChain()) parentChainLink = filterManagerParentChain()->m_chainLinks.current(); // No iterator here, as we need m_chainLinks.current() in outputDocument() m_chainLinks.first(); for (; count > 1 && m_chainLinks.current() && status == KoFilter::OK; m_chainLinks.next(), --count) { status = m_chainLinks.current()->invokeFilter(parentChainLink); m_state = Middle; manageIO(); } if (!m_chainLinks.current()) { kWarning(30500) << "Huh?? Found a null pointer in the chain"; return KoFilter::StupidError; } if (status == KoFilter::OK) { if (m_state & Beginning) m_state |= End; else m_state = End; status = m_chainLinks.current()->invokeFilter(parentChainLink); manageIO(); } m_state = Done; if (status == KoFilter::OK) finalizeIO(); return status; } QString KoFilterChain::chainOutput() const { if (m_state == Done) return m_inputFile; // as we already called manageIO() return QString(); } QString KoFilterChain::inputFile() { if (m_inputQueried == File) return m_inputFile; else if (m_inputQueried != Nil) { kWarning(30500) << "You already asked for some different source."; return QString(); } m_inputQueried = File; if (m_state & Beginning) { if (static_cast(filterManagerDirection()) == KoFilterManager::Import) m_inputFile = filterManagerImportFile(); else inputFileHelper(filterManagerKoDocument(), filterManagerImportFile()); } else if (m_inputFile.isEmpty()) inputFileHelper(m_inputDocument, QString()); return m_inputFile; } QString KoFilterChain::outputFile() { // sanity check: No embedded filter should ask for a plain file // ###### CHECK: This will break as soon as we support exporting embedding filters if (filterManagerParentChain()) kWarning(30500) << "An embedded filter has to use storageFile()!"; if (m_outputQueried == File) return m_outputFile; else if (m_outputQueried != Nil) { kWarning(30500) << "You already asked for some different destination."; return QString(); } m_outputQueried = File; if (m_state & End) { if (static_cast(filterManagerDirection()) == KoFilterManager::Import) outputFileHelper(false); // This (last) one gets deleted by the caller else m_outputFile = filterManagerExportFile(); } else outputFileHelper(true); return m_outputFile; } KoStoreDevice* KoFilterChain::storageFile(const QString& name, KoStore::Mode mode) { // Plain normal use case if (m_inputQueried == Storage && mode == KoStore::Read && m_inputStorage && m_inputStorage->mode() == KoStore::Read) return storageNewStreamHelper(&m_inputStorage, &m_inputStorageDevice, name); else if (m_outputQueried == Storage && mode == KoStore::Write && m_outputStorage && m_outputStorage->mode() == KoStore::Write) return storageNewStreamHelper(&m_outputStorage, &m_outputStorageDevice, name); else if (m_inputQueried == Nil && mode == KoStore::Read) return storageHelper(inputFile(), name, KoStore::Read, &m_inputStorage, &m_inputStorageDevice); else if (m_outputQueried == Nil && mode == KoStore::Write) return storageHelper(outputFile(), name, KoStore::Write, &m_outputStorage, &m_outputStorageDevice); else { kWarning(30500) << "Oooops, how did we get here? You already asked for a" << " different source/destination?" << endl; return 0; } } KoDocument* KoFilterChain::inputDocument() { if (m_inputQueried == Document) return m_inputDocument; else if (m_inputQueried != Nil) { kWarning(30500) << "You already asked for some different source."; return 0; } if ((m_state & Beginning) && static_cast(filterManagerDirection()) == KoFilterManager::Export && filterManagerKoDocument()) m_inputDocument = filterManagerKoDocument(); else if (!m_inputDocument) m_inputDocument = createDocument(inputFile()); m_inputQueried = Document; return m_inputDocument; } KoDocument* KoFilterChain::outputDocument() { // sanity check: No embedded filter should ask for a document // ###### CHECK: This will break as soon as we support exporting embedding filters if (filterManagerParentChain()) { kWarning(30500) << "An embedded filter has to use storageFile()!"; return 0; } if (m_outputQueried == Document) return m_outputDocument; else if (m_outputQueried != Nil) { kWarning(30500) << "You already asked for some different destination."; return 0; } if ((m_state & End) && static_cast(filterManagerDirection()) == KoFilterManager::Import && filterManagerKoDocument()) m_outputDocument = filterManagerKoDocument(); else m_outputDocument = createDocument(m_chainLinks.current()->to()); m_outputQueried = Document; return m_outputDocument; } void KoFilterChain::dump() { kDebug(30500) << "########## KoFilterChain with" << m_chainLinks.count() << " members:"; ChainLink* link = m_chainLinks.first(); while (link) { link->dump(); link = m_chainLinks.next(); } kDebug(30500) << "########## KoFilterChain (done) ##########"; } void KoFilterChain::appendChainLink(KoFilterEntry::Ptr filterEntry, const QByteArray& from, const QByteArray& to) { m_chainLinks.append(new ChainLink(this, filterEntry, from, to)); } void KoFilterChain::prependChainLink(KoFilterEntry::Ptr filterEntry, const QByteArray& from, const QByteArray& to) { m_chainLinks.prepend(new ChainLink(this, filterEntry, from, to)); } QString KoFilterChain::filterManagerImportFile() const { return m_manager->importFile(); } QString KoFilterChain::filterManagerExportFile() const { return m_manager->exportFile(); } KoDocument* KoFilterChain::filterManagerKoDocument() const { return m_manager->document(); } int KoFilterChain::filterManagerDirection() const { return m_manager->direction(); } KoFilterChain* KoFilterChain::filterManagerParentChain() const { return m_manager->parentChain(); } void KoFilterChain::manageIO() { m_inputQueried = Nil; m_outputQueried = Nil; delete m_inputStorageDevice; m_inputStorageDevice = 0; if (m_inputStorage) { m_inputStorage->close(); delete m_inputStorage; m_inputStorage = 0; } delete m_inputTempFile; // autodelete m_inputTempFile = 0; m_inputFile.clear(); if (!m_outputFile.isEmpty()) { if (m_outputTempFile == 0) { m_inputTempFile = new KTemporaryFile; m_inputTempFile->setAutoRemove(true); m_inputTempFile->setFileName(m_outputFile); } else { m_inputTempFile = m_outputTempFile; m_outputTempFile = 0; } m_inputFile = m_outputFile; m_outputFile.clear(); m_inputTempFile = m_outputTempFile; m_outputTempFile = 0; delete m_outputStorageDevice; m_outputStorageDevice = 0; if (m_outputStorage) { m_outputStorage->close(); // Don't delete the storage if we're just pointing to the // storage of the parent filter chain if (!filterManagerParentChain() || m_outputStorage->mode() != KoStore::Write) delete m_outputStorage; m_outputStorage = 0; } } if (m_inputDocument != filterManagerKoDocument()) delete m_inputDocument; m_inputDocument = m_outputDocument; m_outputDocument = 0; } void KoFilterChain::finalizeIO() { // In case we export (to a file, of course) and the last // filter chose to output a KoDocument we have to save it. // Should be very rare, but well... // Note: m_*input*Document as we already called manageIO() if (m_inputDocument && static_cast(filterManagerDirection()) == KoFilterManager::Export) { kDebug(30500) << "Saving the output document to the export file " << m_chainLinks.current()->to(); m_inputDocument->setOutputMimeType(m_chainLinks.current()->to()); m_inputDocument->saveNativeFormat(filterManagerExportFile()); m_inputFile = filterManagerExportFile(); } } bool KoFilterChain::createTempFile(KTemporaryFile** tempFile, bool autoDelete) { if (*tempFile) { kError(30500) << "Ooops, why is there already a temp file???" << endl; return false; } *tempFile = new KTemporaryFile(); (*tempFile)->setAutoRemove(autoDelete); return (*tempFile)->open(); } /* Note about Windows & usage of KTemporaryFile The KTemporaryFile objects m_inputTempFile and m_outputTempFile are just used to reserve a temporary file with a unique name which then can be used to store an intermediate format. The filters themselves do not get access to these objects, but can query KoFilterChain only for the filename and then have to open the files themselves with their own file handlers (TODO: change this). On Windows this seems to be a problem and results in content not sync'ed to disk etc. So unless someone finds out which flags might be needed on opening the files on Windows to prevent this behaviour (unless these details are hidden away by the Qt abstraction and cannot be influenced), a workaround is to destruct the KTemporaryFile objects right after creation again and just take the name, to avoid having two file handlers on the same file. A better fix might be to use the KTemporaryFile objects also by the filters, instead of having them open the same file on their own again, but that needs more work and is left for... you :) */ void KoFilterChain::inputFileHelper(KoDocument* document, const QString& alternativeFile) { if (document) { if (!createTempFile(&m_inputTempFile)) { delete m_inputTempFile; m_inputTempFile = 0; m_inputFile.clear(); return; } m_inputFile = m_inputTempFile->fileName(); // See "Note about Windows & usage of KTemporaryFile" above #ifdef Q_OS_WIN m_inputTempFile->close(); m_inputTempFile->setAutoRemove(true); delete m_inputTempFile; m_inputTempFile = 0; #endif document->setOutputMimeType(m_chainLinks.current()->from()); if (!document->saveNativeFormat(m_inputFile)) { delete m_inputTempFile; m_inputTempFile = 0; m_inputFile.clear(); return; } } else m_inputFile = alternativeFile; } void KoFilterChain::outputFileHelper(bool autoDelete) { if (!createTempFile(&m_outputTempFile, autoDelete)) { delete m_outputTempFile; m_outputTempFile = 0; m_outputFile.clear(); } else { m_outputFile = m_outputTempFile->fileName(); // See "Note about Windows & usage of KTemporaryFile" above #ifdef Q_OS_WIN m_outputTempFile->close(); m_outputTempFile->setAutoRemove(true); delete m_outputTempFile; m_outputTempFile = 0; #endif } } KoStoreDevice* KoFilterChain::storageNewStreamHelper(KoStore** storage, KoStoreDevice** device, const QString& name) { delete *device; *device = 0; if ((*storage)->isOpen()) (*storage)->close(); if ((*storage)->bad()) return storageCleanupHelper(storage); if (!(*storage)->open(name)) return 0; *device = new KoStoreDevice(*storage); return *device; } KoStoreDevice* KoFilterChain::storageHelper(const QString& file, const QString& streamName, KoStore::Mode mode, KoStore** storage, KoStoreDevice** device) { if (file.isEmpty()) return 0; if (*storage) { kDebug(30500) << "Uh-oh, we forgot to clean up..."; return 0; } storageInit(file, mode, storage); if ((*storage)->bad()) return storageCleanupHelper(storage); // Seems that we got a valid storage, at least. Even if we can't open // the stream the "user" asked us to open, we nonetheless change the // IOState from File to Storage, as it might be possible to open other streams if (mode == KoStore::Read) m_inputQueried = Storage; else // KoStore::Write m_outputQueried = Storage; return storageCreateFirstStream(streamName, storage, device); } void KoFilterChain::storageInit(const QString& file, KoStore::Mode mode, KoStore** storage) { QByteArray appIdentification(""); if (mode == KoStore::Write) { // To create valid storages we also have to add the mimetype // magic "applicationIndentifier" to the storage. // As only filters with a Calligra destination should query // for a storage to write to, we don't check the content of // the mimetype here. It doesn't do a lot of harm if someome // "abuses" this method. appIdentification = m_chainLinks.current()->to(); } *storage = KoStore::createStore(file, mode, appIdentification); } KoStoreDevice* KoFilterChain::storageCreateFirstStream(const QString& streamName, KoStore** storage, KoStoreDevice** device) { if (!(*storage)->open(streamName)) return 0; if (*device) { kDebug(30500) << "Uh-oh, we forgot to clean up the storage device!"; (*storage)->close(); return storageCleanupHelper(storage); } *device = new KoStoreDevice(*storage); return *device; } KoStoreDevice* KoFilterChain::storageCleanupHelper(KoStore** storage) { // Take care not to delete the storage of the parent chain if (*storage != m_outputStorage || !filterManagerParentChain() || (*storage)->mode() != KoStore::Write) delete *storage; *storage = 0; return 0; } KoDocument* KoFilterChain::createDocument(const QString& file) { KUrl url; url.setPath(file); KMimeType::Ptr t = KMimeType::findByUrl(url, 0, true); if (t->name() == KMimeType::defaultMimeType()) { kError(30500) << "No mimetype found for " << file << endl; return 0; } - KoDocument *doc = createDocument(QByteArray(t->name().toLatin1())); + KoDocument *doc = createDocument(t->name().toLatin1()); if (!doc || !doc->loadNativeFormat(file)) { kError(30500) << "Couldn't load from the file" << endl; delete doc; return 0; } return doc; } KoDocument* KoFilterChain::createDocument(const QByteArray& mimeType) { KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(mimeType); if (entry.isEmpty()) { kError(30500) << "Couldn't find a part that can handle mimetype " << mimeType << endl; } QString errorMsg; KoPart *part = entry.createKoPart(&errorMsg); if (!part) { kError(30500) << "Couldn't create the document: " << errorMsg << endl; return 0; } return part->document(); } int KoFilterChain::weight() const { return m_chainLinks.count(); } diff --git a/libs/main/KoMainWindow.cpp b/libs/main/KoMainWindow.cpp index b10eb26e6ae..7d454947d7d 100644 --- a/libs/main/KoMainWindow.cpp +++ b/libs/main/KoMainWindow.cpp @@ -1,2064 +1,2064 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port 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 "KoMainWindow.h" #ifdef __APPLE__ #include "MacSupport.h" #endif #include "KoView.h" #include "KoDocument.h" #include "KoFilterManager.h" #include "KoDocumentInfo.h" #include "KoDocumentInfoDlg.h" #include "KoFileDialog.h" #include "KoVersionDialog.h" #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoPrintJob.h" #include "KoDocumentEntry.h" #include "KoDockerManager.h" #include "KoServiceProvider.h" #include "KoPart.h" #include #include #include #include #if KDE_IS_VERSION(4,6,0) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KACTIVITIES #include #endif // // qt includes #include #include #include #include #include #include #include #include #include #include #include "thememanager.h" #include "calligraversion.h" class KoPartManager : public KParts::PartManager { public: KoPartManager(QWidget * parent) : KParts::PartManager(parent) { setSelectionPolicy(KParts::PartManager::TriState); setAllowNestedParts(false); setIgnoreScrollBars(true); } virtual bool eventFilter(QObject *obj, QEvent *ev) { if (!obj || !ev || !obj->isWidgetType()) return false; return KParts::PartManager::eventFilter(obj, ev); } }; class KoMainWindowPrivate { public: KoMainWindowPrivate(KoMainWindow *w) { parent = w; rootDocument = 0; rootPart = 0; partToOpen = 0; manager = 0; mainWindowGuiIsBuilt = false; forQuit = false; activePart = 0; activeView = 0; firstTime = true; progress = 0; showDocumentInfo = 0; saveAction = 0; saveActionAs = 0; printAction = 0; printActionPreview = 0; statusBarLabel = 0; sendFileAction = 0; exportPdf = 0; closeFile = 0; reloadFile = 0; showFileVersions = 0; importFile = 0; exportFile = 0; isImporting = false; isExporting = false; windowSizeDirty = false; lastExportSpecialOutputFlag = 0; readOnly = false; dockWidgetMenu = 0; dockerManager = 0; deferredClosingEvent = 0; #ifdef HAVE_KACTIVITIES activityResource = 0; #endif themeManager = 0; } ~KoMainWindowPrivate() { qDeleteAll(toolbarList); } void applyDefaultSettings(QPrinter &printer) { QString title = rootDocument->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { title = rootDocument->url().fileName(); // strip off the native extension (I don't want foobar.kwd.ps when printing into a file) KMimeType::Ptr mime = KMimeType::mimeType(rootDocument->outputMimeType()); if (mime) { QString extension = mime->property("X-KDE-NativeExtension").toString(); if (title.endsWith(extension)) title.truncate(title.length() - extension.length()); } } if (title.isEmpty()) { // #139905 const QString programName = parent->componentData().aboutData() ? parent->componentData().aboutData()->programName() : parent->componentData().componentName(); title = i18n("%1 unsaved document (%2)", programName, KGlobal::locale()->formatDate(QDate::currentDate(), KLocale::ShortDate)); } printer.setDocName(title); } KoMainWindow *parent; KoDocument *rootDocument; KoPart *rootPart; KoPart *partToOpen; QList rootViews; KParts::PartManager *manager; KParts::Part *activePart; KoView *activeView; QLabel * statusBarLabel; QProgressBar *progress; QList toolbarList; bool mainWindowGuiIsBuilt; bool forQuit; bool firstTime; bool windowSizeDirty; bool readOnly; KAction *showDocumentInfo; KAction *saveAction; KAction *saveActionAs; KAction *printAction; KAction *printActionPreview; KAction *sendFileAction; KAction *exportPdf; KAction *closeFile; KAction *reloadFile; KAction *showFileVersions; KAction *importFile; KAction *exportFile; KToggleAction *toggleDockers; KRecentFilesAction *recent; bool isImporting; bool isExporting; KUrl lastExportUrl; QByteArray lastExportedFormat; int lastExportSpecialOutputFlag; QMap dockWidgetsMap; KActionMenu *dockWidgetMenu; QMap dockWidgetVisibilityMap; KoDockerManager *dockerManager; QList dockWidgets; QByteArray m_dockerStateBeforeHiding; QCloseEvent *deferredClosingEvent; #ifdef HAVE_KACTIVITIES KActivities::ResourceInstance *activityResource; #endif Digikam::ThemeManager *themeManager; }; KoMainWindow::KoMainWindow(const KComponentData &componentData) : KParts::MainWindow() , d(new KoMainWindowPrivate(this)) { #ifdef __APPLE__ setUnifiedTitleAndToolBarOnMac(true); MacSupport::addFullscreen(this); #endif setStandardToolBarMenuEnabled(true); Q_ASSERT(componentData.isValid()); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts())); d->manager = new KoPartManager(this); connect(d->manager, SIGNAL(activePartChanged(KParts::Part *)), this, SLOT(slotActivePartChanged(KParts::Part *))); if (componentData.isValid()) { setComponentData(componentData, false); // don't load plugins! we don't want // the part's plugins with this shell, even though we are using the // part's componentData! (Simon) KGlobal::setActiveComponent(componentData); } QString doc; QStringList allFiles = KGlobal::dirs()->findAllResources("data", "calligra/calligra_shell.rc"); setXMLFile(findMostRecentXMLFile(allFiles, doc)); setLocalXMLFile(KStandardDirs::locateLocal("data", "calligra/calligra_shell.rc")); actionCollection()->addAction(KStandardAction::New, "file_new", this, SLOT(slotFileNew())); actionCollection()->addAction(KStandardAction::Open, "file_open", this, SLOT(slotFileOpen())); d->recent = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(const KUrl&)), actionCollection()); connect(d->recent, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); d->saveAction = actionCollection()->addAction(KStandardAction::Save, "file_save", this, SLOT(slotFileSave())); d->saveActionAs = actionCollection()->addAction(KStandardAction::SaveAs, "file_save_as", this, SLOT(slotFileSaveAs())); d->printAction = actionCollection()->addAction(KStandardAction::Print, "file_print", this, SLOT(slotFilePrint())); d->printActionPreview = actionCollection()->addAction(KStandardAction::PrintPreview, "file_print_preview", this, SLOT(slotFilePrintPreview())); d->exportPdf = new KAction(i18n("Export as PDF..."), this); d->exportPdf->setIcon(koIcon("application-pdf")); actionCollection()->addAction("file_export_pdf", d->exportPdf); connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); d->sendFileAction = actionCollection()->addAction(KStandardAction::Mail, "file_send_file", this, SLOT(slotEmailFile())); d->closeFile = actionCollection()->addAction(KStandardAction::Close, "file_close", this, SLOT(slotFileClose())); actionCollection()->addAction(KStandardAction::Quit, "file_quit", this, SLOT(slotFileQuit())); d->reloadFile = new KAction(i18n("Reload"), this); actionCollection()->addAction("file_reload_file", d->reloadFile); connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->showFileVersions = new KAction(i18n("Versions..."), this); actionCollection()->addAction("file_versions_file", d->showFileVersions); connect(d->showFileVersions, SIGNAL(triggered(bool)), this, SLOT(slotVersionsFile())); d->importFile = new KAction(koIcon("document-import"), i18n("Open ex&isting Document as Untitled Document..."), this); actionCollection()->addAction("file_import_file", d->importFile); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = new KAction(koIcon("document-export"), i18n("E&xport..."), this); actionCollection()->addAction("file_export_file", d->exportFile); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = new KAction(koIcon("document-properties"), i18n("Document Information"), this); actionCollection()->addAction("file_documentinfo", d->showDocumentInfo); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(slotConfigureToolbars()), actionCollection()); d->showDocumentInfo->setEnabled(false); d->saveActionAs->setEnabled(false); d->reloadFile->setEnabled(false); d->showFileVersions->setEnabled(false); d->importFile->setEnabled(true); // always enabled like File --> Open d->exportFile->setEnabled(false); d->saveAction->setEnabled(false); d->printAction->setEnabled(false); d->printActionPreview->setEnabled(false); d->sendFileAction->setEnabled(false); d->exportPdf->setEnabled(false); d->closeFile->setEnabled(false); // populate theme menu d->themeManager = new Digikam::ThemeManager(this); KConfigGroup group(KGlobal::config(), "theme"); d->themeManager->setThemeMenuAction(new KActionMenu(i18n("&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); d->themeManager->setCurrentTheme(group.readEntry("Theme", d->themeManager->defaultThemeName())); // set up the action "list" for "Close all Views" (hacky :) (Werner) KToggleAction *fullscreenAction = new KToggleAction(koIcon("view-fullscreen"), i18n("Full Screen Mode"), this); actionCollection()->addAction("view_fullscreen", fullscreenAction); fullscreenAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F)); connect(fullscreenAction, SIGNAL(toggled(bool)), this, SLOT(viewFullscreen(bool))); d->toggleDockers = new KToggleAction(i18n("Show Dockers"), this); d->toggleDockers->setChecked(true); actionCollection()->addAction("view_toggledockers", d->toggleDockers); d->toggleDockers->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H)); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->dockWidgetMenu = new KActionMenu(i18n("Dockers"), this); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); d->dockWidgetMenu->setVisible(false); d->dockWidgetMenu->setDelayed(false); // Load list of recent files KSharedConfigPtr configPtr = componentData.isValid() ? componentData.config() : KGlobal::config(); d->recent->loadEntries(configPtr->group("RecentFiles")); createShellGUI(); d->mainWindowGuiIsBuilt = true; // if the user didn's specify the geometry on the command line (does anyone do that still?), // we first figure out some good default size and restore the x,y position. See bug 285804Z. if (!initialGeometrySet()) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen()); desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = ( deskWidth / 3 ) * 2; h = desk.height(); } else { w = desk.width(); h = desk.height(); } // KDE doesn't restore the x,y position, so let's do that ourselves KConfigGroup cfg(KGlobal::config(), "MainWindow"); x = cfg.readEntry("ko_x", x); y = cfg.readEntry("ko_y", y); setGeometry(x, y, w, h); } // Now ask kde to restore the size of the window; this could probably be replaced by // QWidget::saveGeometry asnd QWidget::restoreGeometry, but let's stay with the KDE // way of doing things. KConfigGroup config(KGlobal::config(), "MainWindow"); restoreWindowSize( config ); d->dockerManager = new KoDockerManager(this); } KoMainWindow::~KoMainWindow() { KConfigGroup cfg(KGlobal::config(), "MainWindow"); cfg.writeEntry("ko_x", frameGeometry().x()); cfg.writeEntry("ko_y", frameGeometry().y()); { KConfigGroup group(KGlobal::config(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); } // Explicitly delete the docker manager to ensure that it is deleted before the dockers delete d->dockerManager; d->dockerManager = 0; // The doc and view might still exist (this is the case when closing the window) if (d->rootPart) d->rootPart->removeShell(this); if (d->partToOpen) { d->partToOpen->removeShell(this); delete d->partToOpen; } // safety first ;) d->manager->setActivePart(0); if (d->rootViews.indexOf(d->activeView) == -1) { delete d->activeView; d->activeView = 0; } while(!d->rootViews.isEmpty()) { delete d->rootViews.takeFirst(); } // We have to check if this was a root document. // This has to be checked from queryClose, too :) if (d->rootPart && d->rootPart->viewCount() == 0) { //kDebug(30003) <<"Destructor. No more views, deleting old doc" << d->rootDoc; delete d->rootDocument; delete d->rootPart; } delete d->manager; delete d; } void KoMainWindow::setRootDocument(KoDocument *doc, KoPart *rootPart) { if (d->rootDocument == doc) return; if (d->partToOpen && d->partToOpen->document() != doc) { d->partToOpen->removeShell(this); delete d->partToOpen; } d->partToOpen = 0; //kDebug(30003) <<"KoMainWindow::setRootDocument this =" << this <<" doc =" << doc; QList oldRootViews = d->rootViews; d->rootViews.clear(); KoDocument *oldRootDoc = d->rootDocument; KoPart *oldRootPart = d->rootPart; if (oldRootDoc) { oldRootPart->removeShell(this); if (dockerManager()) { dockerManager()->resetToolDockerWidgets(); } // Hide all dockwidgets and remember their old state d->dockWidgetVisibilityMap.clear(); foreach(QDockWidget* dockWidget, d->dockWidgetsMap) { d->dockWidgetVisibilityMap.insert(dockWidget, dockWidget->isVisible()); dockWidget->setVisible(false); } d->dockWidgetMenu->setVisible(false); } d->rootDocument = doc; // XXX remove this after the splitting if (!rootPart && doc) { d->rootPart = doc->documentPart(); } else { d->rootPart = rootPart; } if (doc) { d->dockWidgetMenu->setVisible(true); //d->manager->addPart( doc, false ); // done by KoView::setPartManager KoView *view = d->rootPart->createView(this); setCentralWidget(view); d->rootViews.append(view); view->setPartManager(d->manager); view->show(); view->setFocus(); // The addShell has been done already if using openUrl if (!d->rootPart->shells().contains(this)) { d->rootPart->addShell(this); } } bool enable = d->rootDocument != 0 ? true : false; d->showDocumentInfo->setEnabled(enable); d->saveAction->setEnabled(enable); d->saveActionAs->setEnabled(enable); d->importFile->setEnabled(enable); d->exportFile->setEnabled(enable); d->printAction->setEnabled(enable); d->printActionPreview->setEnabled(enable); d->sendFileAction->setEnabled(enable); d->exportPdf->setEnabled(enable); d->closeFile->setEnabled(enable); updateCaption(); d->manager->setActivePart(d->rootPart, doc ? d->rootViews.first() : 0); emit restoringDone(); while(!oldRootViews.isEmpty()) { delete oldRootViews.takeFirst(); } if (oldRootPart && oldRootPart->viewCount() == 0) { //kDebug(30003) <<"No more views, deleting old doc" << oldRootDoc; oldRootDoc->clearUndoHistory(); delete oldRootDoc; } if (doc && !d->dockWidgetVisibilityMap.isEmpty()) { foreach(QDockWidget* dockWidget, d->dockWidgetsMap) { dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } if (!d->rootDocument) { statusBar()->setVisible(false); } } void KoMainWindow::updateReloadFileAction(KoDocument *doc) { d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KoMainWindow::updateVersionsFileAction(KoDocument *doc) { //TODO activate it just when we save it in oasis file format d->showFileVersions->setEnabled(doc && !doc->url().isEmpty() && (doc->outputMimeType() == doc->nativeOasisMimeType() || doc->outputMimeType() == doc->nativeOasisMimeType() + "-template")); } void KoMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KoMainWindow::addRecentURL(const KUrl& url) { kDebug(30003) << "KoMainWindow::addRecentURL url=" << url.prettyUrl(); // Add entry to recent documents list // (call coming from KoDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.toLocalFile(KUrl::RemoveTrailingSlash); const QStringList tmpDirs = KGlobal::dirs()->resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) if (path.contains(*it)) ok = false; // it's in the tmp resource if (ok) { KRecentDocument::add(path); #if KDE_IS_VERSION(4,6,0) KRecentDirs::add(":OpenDialog", QFileInfo(path).dir().canonicalPath()); #endif } } else { KRecentDocument::add(url.url(KUrl::RemoveTrailingSlash), true); } if (ok) { d->recent->addUrl(url); } saveRecentFiles(); #ifdef HAVE_KACTIVITIES if (!d->activityResource) { d->activityResource = new KActivities::ResourceInstance(winId(), this); } d->activityResource->setUri(url); #endif } } void KoMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = componentData().isValid() ? componentData().config() : KGlobal::config(); kDebug(30003) << this << " Saving recent files list into config. componentData()=" << componentData().componentName(); d->recent->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start foreach(KMainWindow* window, KMainWindow::memberList()) static_cast(window)->reloadRecentFileList(); } void KoMainWindow::reloadRecentFileList() { KSharedConfigPtr config = componentData().isValid() ? componentData().config() : KGlobal::config(); d->recent->loadEntries(config->group("RecentFiles")); } KoPart* KoMainWindow::createPart() const { KoDocumentEntry entry = KoDocumentEntry(KoServiceProvider::readNativeService()); QString errorMsg; KoPart *part = entry.createKoPart(&errorMsg); if (!part || !errorMsg.isEmpty()) { return 0; } return part; } void KoMainWindow::updateCaption() { kDebug(30003) << "KoMainWindow::updateCaption()"; if (!d->rootDocument) { updateCaption(QString(), false); } else { QString caption( d->rootDocument->caption() ); if (d->readOnly) caption += ' ' + i18n("(write protected)"); updateCaption(caption, d->rootDocument->isModified()); if (!rootDocument()->url().fileName(KUrl::ObeyTrailingSlash).isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", d->rootDocument->url().fileName(KUrl::ObeyTrailingSlash))); else d->saveAction->setToolTip(i18n("Save")); } } void KoMainWindow::updateCaption(const QString & caption, bool mod) { kDebug(30003) << "KoMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef CALLIGRA_ALPHA setCaption(QString("ALPHA %1: %2").arg(CALLIGRA_ALPHA).arg(caption), mod); return; #endif #ifdef CALLIGRA_BETA setCaption(QString("BETA %1: %2").arg(CALLIGRA_BETA).arg(caption), mod); return; #endif #ifdef CALLIGRA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(CALLIGRA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KoDocument *KoMainWindow::rootDocument() const { return d->rootDocument; } KoView *KoMainWindow::rootView() const { if (d->rootViews.indexOf(d->activeView) != -1) return d->activeView; return d->rootViews.first(); } bool KoMainWindow::openDocument(const KUrl & url) { if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0)) { KMessageBox::error(0, i18n("The file %1 does not exist.", url.url())); d->recent->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url); } bool KoMainWindow::openDocument(KoPart *newPart, KoDocument *newdoc, const KUrl & url) { if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0)) { // the part always has a document; the document doesn't know about the part. if (!newdoc) { newdoc = newPart->document(); } newdoc->initEmpty(); //create an empty document setRootDocument(newdoc, newPart); newdoc->setUrl(url); QString mime = KMimeType::findByUrl(url)->name(); if (mime.isEmpty() || mime == KMimeType::defaultMimeType()) mime = newdoc->nativeFormatMimeType(); newdoc->setMimeTypeAfterLoading(mime); updateCaption(); return true; } return openDocumentInternal(url, newPart, newdoc); } bool KoMainWindow::openDocumentInternal(const KUrl & url, KoPart *newpart, KoDocument *newdoc) { //kDebug(30003) <<"KoMainWindow::openDocument" << url.url(); if (!newpart) newpart = createPart(); if (!newpart) return false; if (!newdoc) newdoc = newpart->document(); d->firstTime = true; connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(newpart, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newpart, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); newpart->addShell(this); // used by openUrl bool openRet = (!isImporting()) ? newdoc->openUrl(url) : newdoc->importDocument(url); if (!openRet) { newpart->removeShell(this); delete newdoc; delete newpart; return false; } updateReloadFileAction(newdoc); updateVersionsFileAction(newdoc); KFileItem file(url, newdoc->mimeType(), KFileItem::Unknown); if (!file.isWritable()) newdoc->setReadWrite(false); return true; } // Separate from openDocument to handle async loading (remote URLs) void KoMainWindow::slotLoadCompleted() { kDebug(30003) << "KoMainWindow::slotLoadCompleted"; KoPart *newpart = qobject_cast(sender()); KoDocument *newdoc = newpart->document(); if (d->rootDocument && d->rootDocument->isEmpty()) { // Replace current empty document setRootDocument(newdoc); } else if (d->rootDocument && !d->rootDocument->isEmpty()) { // Open in a new shell // (Note : could create the shell first and the doc next for this // particular case, that would give a better user feedback...) KoMainWindow *s = new KoMainWindow(newpart->componentData()); s->show(); newpart->removeShell(this); s->setRootDocument(newdoc, newpart); } else { // We had no document, set the new one setRootDocument(newdoc); } disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(newpart, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newpart, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KoMainWindow::slotLoadCanceled(const QString & errMsg) { kDebug(30003) << "KoMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user KMessageBox::error(this, errMsg); // ... can't delete the document, it's the one who emitted the signal... KoPart* newpart = qobject_cast(sender()); Q_ASSERT(newpart); disconnect(newpart->document(), SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(newpart, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newpart, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KoMainWindow::slotSaveCanceled(const QString &errMsg) { kDebug(30003) << "KoMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user KMessageBox::error(this, errMsg); slotSaveCompleted(); } void KoMainWindow::slotSaveCompleted() { kDebug(30003) << "KoMainWindow::slotSaveCompleted"; KoPart* pPart = (KoPart *)(sender()); disconnect(pPart->document(), SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(pPart, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(pPart, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); if (d->deferredClosingEvent) { KParts::MainWindow::closeEvent(d->deferredClosingEvent); } } // returns true if we should save, false otherwise. bool KoMainWindow::exportConfirmation(const QByteArray &outputFormat) { KConfigGroup group = KGlobal::config()->group(d->rootPart->componentData().componentName()); if (!group.readEntry("WantExportConfirmation", true)) { return true; } KMimeType::Ptr mime = KMimeType::mimeType(outputFormat); QString comment = mime ? mime->comment() : i18n("%1 (unknown file type)", QString::fromLatin1(outputFormat)); // Warn the user int ret; if (!isExporting()) { // File --> Save ret = KMessageBox::warningContinueCancel ( this, i18n("Saving as a %1 may result in some loss of formatting." "

Do you still want to save in this format?", QString("%1").arg(comment)), // in case we want to remove the bold later i18n("Confirm Save"), KStandardGuiItem::save(), KStandardGuiItem::cancel(), "NonNativeSaveConfirmation" ); } else { // File --> Export ret = KMessageBox::warningContinueCancel ( this, i18n("Exporting as a %1 may result in some loss of formatting." "

Do you still want to export to this format?", QString("%1").arg(comment)), // in case we want to remove the bold later i18n("Confirm Export"), KGuiItem(i18n("Export")), KStandardGuiItem::cancel(), "NonNativeExportConfirmation" // different to the one used for Save (above) ); } return (ret == KMessageBox::Continue); } bool KoMainWindow::saveDocument(bool saveas, bool silent) { if (!d->rootDocument || !d->rootPart) { return true; } bool reset_url; if (d->rootPart->url().isEmpty()) { emit saveDialogShown(); reset_url = true; saveas = true; } else { reset_url = false; } connect(d->rootDocument, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(d->rootPart, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(d->rootPart, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); KUrl oldURL = d->rootPart->url(); QString oldFile = d->rootPart->localFilePath(); QByteArray _native_format = d->rootDocument->nativeFormatMimeType(); QByteArray oldOutputFormat = d->rootDocument->outputMimeType(); int oldSpecialOutputFlag = d->rootDocument->specialOutputFlag(); KUrl suggestedURL = d->rootPart->url(); QStringList mimeFilter = KoFilterManager::mimeFilter(_native_format, KoFilterManager::Export, d->rootDocument->extraNativeMimeTypes(KoDocument::ForExport)); if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) { kDebug(30003) << "KoMainWindow::saveDocument no export filter for" << oldOutputFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = suggestedURL.fileName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name int c = suggestedFilename.lastIndexOf('.'); KMimeType::Ptr mime = KMimeType::mimeType(_native_format); if (! mime) mime = KMimeType::defaultMimeTypePtr(); QString ext = mime->property("X-KDE-NativeExtension").toString(); if (!ext.isEmpty()) { if (c < 0) suggestedFilename += ext; else suggestedFilename = suggestedFilename.left(c) + ext; } else { // current filename extension wrong anyway if (c > 0) { // this assumes that a . signifies an extension, not just a . suggestedFilename = suggestedFilename.left(c); } } suggestedURL.setFileName(suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (d->rootPart->url().isEmpty() || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog *dialog = new KoFileDialog( (isExporting() && !d->lastExportUrl.isEmpty()) ? d->lastExportUrl.url() : suggestedURL.url(), this); if (!isExporting()) dialog->setCaption(i18n("Save Document As")); else dialog->setCaption(i18n("Export Document As")); dialog->setOperationMode(KFileDialog::Saving); dialog->setMode(KFile::File); dialog->setSpecialMimeFilter(mimeFilter, isExporting() ? d->lastExportedFormat : d->rootDocument->mimeType(), isExporting() ? d->lastExportSpecialOutputFlag : oldSpecialOutputFlag, _native_format, d->rootDocument->supportedSpecialFormats()); KUrl newURL; QByteArray outputFormat = _native_format; int specialOutputFlag = 0; bool bOk; do { bOk = true; if (dialog->exec() == QDialog::Accepted) { newURL = dialog->selectedUrl(); - QString outputFormatString = dialog->currentMimeFilter().toLatin1(); + QString outputFormatString = dialog->currentMimeFilter(); if (outputFormatString.isNull()) { KMimeType::Ptr mime = KMimeType::findByUrl(newURL); outputFormatString = mime->name(); } outputFormat = outputFormatString.toLatin1(); specialOutputFlag = dialog->specialEntrySelected(); kDebug(30003) << "KoMainWindow::saveDocument outputFormat =" << outputFormat; if (!isExporting()) justChangingFilterOptions = (newURL == d->rootPart->url()) && (outputFormat == d->rootDocument->mimeType()) && (specialOutputFlag == oldSpecialOutputFlag); else justChangingFilterOptions = (newURL == d->lastExportUrl) && (outputFormat == d->lastExportedFormat) && (specialOutputFlag == d->lastExportSpecialOutputFlag); } else { bOk = false; break; } if (newURL.isEmpty()) { bOk = false; break; } // adjust URL before doing checks on whether the file exists. if (specialOutputFlag == KoDocument::SaveAsDirectoryStore) { QString fileName = newURL.fileName(); if (fileName != "content.xml") { newURL.addPath("content.xml"); } } // this file exists and we are not just clicking "Save As" to change filter options // => ask for confirmation if (KIO::NetAccess::exists(newURL, KIO::NetAccess::DestinationSide, this) && !justChangingFilterOptions) { bOk = KMessageBox::questionYesNo(this, i18n("A document with this name already exists.\n"\ "Do you want to overwrite it?"), i18n("Warning")) == KMessageBox::Yes; } } while (!bOk); delete dialog; if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions || d->rootDocument->confirmNonNativeSave(isExporting())) { if (!d->rootDocument->isNativeFormat(outputFormat, KoDocument::ForExport)) wantToSave = exportConfirmation(outputFormat); } if (wantToSave) { // // Note: // If the user is stupid enough to Export to the current URL, // we do _not_ change this operation into a Save As. Reasons // follow: // // 1. A check like "isExporting() && oldURL == newURL" // doesn't _always_ work on case-insensitive filesystems // and inconsistent behaviour is bad. // 2. It is probably not a good idea to change d->rootDocument->mimeType // and friends because the next time the user File/Save's, // (not Save As) they won't be expecting that they are // using their File/Export settings // // As a bad side-effect of this, the modified flag will not // be updated and it is possible that what is currently on // their screen is not what is stored on disk (through loss // of formatting). But if you are dumb enough to change // mimetype but not the filename, then arguably, _you_ are // the "bug" :) // // - Clarence // d->rootDocument->setOutputMimeType(outputFormat, specialOutputFlag); if (!isExporting()) { // Save As ret = d->rootPart->saveAs(newURL); if (ret) { kDebug(30003) << "Successful Save As!"; addRecentURL(newURL); setReadWrite(true); } else { kDebug(30003) << "Failed Save As!"; d->rootDocument->setUrl(oldURL); d->rootPart->setLocalFilePath(oldFile); d->rootDocument->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } } else { // Export ret = d->rootDocument->exportDocument(newURL); if (ret) { // a few file dialog convenience things d->lastExportUrl = newURL; d->lastExportedFormat = outputFormat; d->lastExportSpecialOutputFlag = specialOutputFlag; } // always restore output format d->rootDocument->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } if (silent) // don't let the document change the window caption d->rootDocument->setTitleModified(); } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving bool needConfirm = d->rootDocument->confirmNonNativeSave(false) && !d->rootDocument->isNativeFormat(oldOutputFormat, KoDocument::ForExport); if (!needConfirm || (needConfirm && exportConfirmation(oldOutputFormat /* not so old :) */)) ) { // be sure d->rootDocument has the correct outputMimeType! ret = d->rootPart->save(); if (!ret) { kDebug(30003) << "Failed Save!"; d->rootPart->setUrl(oldURL); d->rootPart->setLocalFilePath(oldFile); } } else ret = false; } // Now that there's a File/Export option, this is no longer necessary. // If you continue to use File/Save to export to a foreign format, // this signals your intention to continue working in a foreign format. // You have already been warned by the DoNotAskAgain exportConfirmation // about losing formatting when you first saved so don't set modified // here or else it will be reported as a bug by some MSOffice user. // You have been warned! Do not click DoNotAskAgain!!! #if 0 if (ret && !isExporting()) { // When exporting to a non-native format, we don't reset modified. // This way the user will be reminded to save it again in the native format, // if he/she doesn't want to lose formatting. if (wasModified && d->rootDocument->outputMimeType() != _native_format) d->rootDocument->setModified(true); } #endif if (!ret && reset_url) d->rootDocument->resetURL(); //clean the suggested filename as the save dialog was rejected updateCaption(); return ret; } void KoMainWindow::closeEvent(QCloseEvent *e) { if(rootDocument() && rootDocument()->isLoading()) { e->setAccepted(false); return; } if (queryClose()) { d->deferredClosingEvent = e; if (d->partToOpen) { // The open pane is visible d->partToOpen->deleteOpenPane(true); } if (!d->m_dockerStateBeforeHiding.isEmpty()) { restoreState(d->m_dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); setRootDocument(0); if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency foreach(QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KoMainWindow::saveWindowSettings() { KSharedConfigPtr config = componentData().config(); if (d->windowSizeDirty ) { // Save window size into the config file of our componentData kDebug(30003) << "KoMainWindow::saveWindowSettings"; saveWindowSize(config->group("MainWindow")); config->sync(); d->windowSizeDirty = false; } if ( rootDocument()) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KGlobal::config()->group(d->rootPart->componentData().componentName()); //kDebug(30003) <<"KoMainWindow::closeEvent -> saveMainWindowSettings rootdoc's componentData=" << d->rootPart->componentData().componentName(); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); } } } KGlobal::config()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KoMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KParts::MainWindow::resizeEvent(e); } bool KoMainWindow::queryClose() { if (rootDocument() == 0) return true; //kDebug(30003) <<"KoMainWindow::queryClose() viewcount=" << rootDocument()->viewCount() // << " shellcount=" << rootDocument()->shellCount() << endl; if (!d->forQuit && d->rootPart->shellCount() > 1) // there are more open, and we are closing just one, so no problem for closing return true; // main doc + internally stored child documents if (d->rootDocument->isModified()) { QString name; if (rootDocument()->documentInfo()) { name = rootDocument()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = rootDocument()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = KMessageBox::warningYesNoCancel(this, i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch (res) { case KMessageBox::Yes : { bool isNative = (d->rootDocument->outputMimeType() == d->rootDocument->nativeFormatMimeType()); if (!saveDocument(!isNative)) return false; break; } case KMessageBox::No : rootDocument()->removeAutoSaveFiles(); rootDocument()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case KMessageBox::Cancel : return false; } } return true; } // Helper method for slotFileNew and slotFileClose void KoMainWindow::chooseNewDocument(InitDocFlags initDocFlags) { KoDocument* doc = rootDocument(); KoPart *newpart = createPart(); KoDocument *newdoc = newpart->document(); if (!newdoc) return; disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); if ((!doc && initDocFlags == InitDocFileNew) || (doc && !doc->isEmpty())) { KoMainWindow *s = new KoMainWindow(newpart->componentData()); s->show(); newpart->addShell(s); newpart->showStartUpWidget(s, true /*Always show widget*/); return; } if (doc) { setRootDocument(0); if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; } newpart->addShell(this); newpart->showStartUpWidget(this, true /*Always show widget*/); } void KoMainWindow::slotFileNew() { chooseNewDocument(InitDocFileNew); } void KoMainWindow::slotFileOpen() { #ifdef Q_WS_WIN // "kfiledialog:///OpenDialog" forces KDE style open dialog in Windows // TODO provide support for "last visited" directory KFileDialog *dialog = new KFileDialog(KUrl(""), QString(), this); #else KFileDialog *dialog = new KFileDialog(KUrl("kfiledialog:///OpenDialog"), QString(), this); #endif dialog->setObjectName("file dialog"); dialog->setMode(KFile::File); if (!isImporting()) dialog->setCaption(i18n("Open Document")); else dialog->setCaption(i18n("Import Document")); const QStringList mimeFilter = KoFilterManager::mimeFilter(KoServiceProvider::readNativeFormatMimeType(), KoFilterManager::Import, KoServiceProvider::readExtraNativeMimeTypes()); dialog->setMimeFilter(mimeFilter); if (dialog->exec() != QDialog::Accepted) { delete dialog; return; } KUrl url(dialog->selectedUrl()); delete dialog; if (url.isEmpty()) return; (void) openDocument(url); } void KoMainWindow::slotFileOpenRecent(const KUrl & url) { // Create a copy, because the original KUrl in the map of recent files in // KRecentFilesAction may get deleted. (void) openDocument(KUrl(url)); } void KoMainWindow::slotFileSave() { if (saveDocument()) emit documentSaved(); } void KoMainWindow::slotFileSaveAs() { if (saveDocument(true)) emit documentSaved(); } void KoMainWindow::slotDocumentInfo() { if (!rootDocument()) return; KoDocumentInfo *docInfo = rootDocument()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = new KoDocumentInfoDlg(this, docInfo, rootDocument()->documentRdf()); if (dlg->exec()) { if (dlg->isDocumentSaved()) { rootDocument()->setModified(false); } else { rootDocument()->setModified(true); } rootDocument()->setTitleModified(); } delete dlg; } void KoMainWindow::slotFileClose() { if (queryClose()) { saveWindowSettings(); setRootDocument(0); // don't delete this shell when deleting the document if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; chooseNewDocument(InitDocFileClose); } } void KoMainWindow::slotFileQuit() { close(); } void KoMainWindow::slotFilePrint() { if (!rootView()) return; KoPrintJob *printJob = rootView()->createPrintJob(); if (printJob == 0) return; d->applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = rootView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) printJob->startPrinting(KoPrintJob::DeleteWhenDone); else delete printJob; delete printDialog; } void KoMainWindow::slotFilePrintPreview() { if (!rootView()) return; KoPrintJob *printJob = rootView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KoPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } class ExportPdfDialog : public KPageDialog { public: ExportPdfDialog(const KUrl &startUrl, const KoPageLayout &pageLayout) : KPageDialog() { setFaceType(KPageDialog::List); setCaption(i18n("Export to PDF")); m_fileWidget = new KFileWidget(startUrl, this); m_fileWidget->setOperationMode(KFileWidget::Saving); m_fileWidget->setMode(KFile::File); m_fileWidget->setMimeFilter(QStringList() << "application/pdf"); connect(m_fileWidget, SIGNAL(accepted()), this, SLOT(accept())); KPageWidgetItem *fileItem = new KPageWidgetItem(m_fileWidget, i18n( "File" )); fileItem->setIcon(koIcon("document-open")); addPage(fileItem); m_pageLayoutWidget = new KoPageLayoutWidget(this, pageLayout); m_pageLayoutWidget->showUnitchooser(false); KPageWidgetItem *optionsItem = new KPageWidgetItem(m_pageLayoutWidget, i18n("Configure")); optionsItem->setIcon(koIcon("configure")); addPage(optionsItem); resize(QSize(800, 600).expandedTo(minimumSizeHint())); } KUrl selectedUrl() const { // selectedUrl()( does not return the expected result. So, build up the KUrl the more complicated way //return m_fileWidget->selectedUrl(); KUrl url = m_fileWidget->dirOperator()->url(); url.adjustPath(KUrl::AddTrailingSlash); url.setFileName(m_fileWidget->locationEdit()->currentText()); return url; } KoPageLayout pageLayout() const { return m_pageLayoutWidget->pageLayout(); } protected: virtual void slotButtonClicked(int button) { if (button == KDialog::Ok) { m_fileWidget->slotOk(); } else { KPageDialog::slotButtonClicked(button); } } private: KFileWidget *m_fileWidget; KoPageLayoutWidget *m_pageLayoutWidget; }; KoPrintJob* KoMainWindow::exportToPdf(QString pdfFileName) { if (!rootView()) return 0; KoPageLayout pageLayout; pageLayout = rootView()->pageLayout(); return exportToPdf(pageLayout, pdfFileName); } KoPrintJob* KoMainWindow::exportToPdf(KoPageLayout pageLayout, QString pdfFileName) { if (!rootView()) return 0; if (pdfFileName.isEmpty()) { KUrl startUrl = KUrl("kfiledialog:///SavePdfDialog"); KoDocument* pDoc = rootDocument(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.fileName(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl.setFileName( fileName ); } QPointer dialog(new ExportPdfDialog(startUrl, pageLayout)); if (dialog->exec() != QDialog::Accepted || !dialog) { delete dialog; return 0; } KUrl url = dialog->selectedUrl(); pageLayout = dialog->pageLayout(); delete dialog; if (KIO::NetAccess::exists(url, KIO::NetAccess::DestinationSide, this)) { bool overwrite = KMessageBox::questionYesNo(this, i18n("A document with this name already exists.\n"\ "Do you want to overwrite it?"), i18n("Warning")) == KMessageBox::Yes; if (!overwrite) { return 0; } } pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KoPrintJob *printJob = rootView()->createPdfPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } d->applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); //before printing check if the printer can handle printing if (!printJob->canPrint()) { KMessageBox::error(this, i18n("Cannot export to the specified file")); } printJob->startPrinting(KoPrintJob::DeleteWhenDone); return printJob; } void KoMainWindow::slotConfigureKeys() { QAction* undoAction=0; QAction* redoAction=0; QString oldUndoText; QString oldRedoText; if(currentView()) { //The undo/redo action text is "undo" + command, replace by simple text while inside editor undoAction = currentView()->actionCollection()->action("edit_undo"); redoAction = currentView()->actionCollection()->action("edit_redo"); oldUndoText = undoAction->text(); oldRedoText = redoAction->text(); undoAction->setText(i18n("Undo")); redoAction->setText(i18n("Redo")); } guiFactory()->configureShortcuts(); if(currentView()) { undoAction->setText(oldUndoText); redoAction->setText(oldRedoText); } } void KoMainWindow::slotConfigureToolbars() { if (rootDocument()) saveMainWindowSettings(KGlobal::config()->group(d->rootPart->componentData().componentName())); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); } void KoMainWindow::slotNewToolbarConfig() { if (rootDocument()) { applyMainWindowSettings(KGlobal::config()->group(d->rootPart->componentData().componentName())); } KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); } void KoMainWindow::slotToolbarToggled(bool toggle) { //kDebug(30003) <<"KoMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) bar->show(); else bar->hide(); if (rootDocument()) saveMainWindowSettings(KGlobal::config()->group(d->rootPart->componentData().componentName())); } else kWarning(30003) << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } bool KoMainWindow::toolbarIsVisible(const char *tbName) { QWidget *tb = toolBar(tbName); return !tb->isHidden(); } void KoMainWindow::showToolbar(const char * tbName, bool shown) { QWidget * tb = toolBar(tbName); if (!tb) { kWarning(30003) << "KoMainWindow: toolbar " << tbName << " not found."; return; } if (shown) tb->show(); else tb->hide(); // Update the action appropriately foreach(QAction* action, d->toolbarList) { if (action->objectName() != tbName) { //kDebug(30003) <<"KoMainWindow::showToolbar setChecked" << shown; static_cast(action)->setChecked(shown); break; } } } void KoMainWindow::viewFullscreen(bool fullScreen) { if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } } void KoMainWindow::slotProgress(int value) { kDebug(30003) << "KoMainWindow::slotProgress" << value; if (value <= -1) { if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->firstTime = true; return; } if (d->firstTime || !d->progress) { // The statusbar might not even be created yet. // So check for that first, and create it if necessary QStatusBar *bar = findChild(); if (!bar) { statusBar()->show(); QApplication::sendPostedEvents(this, QEvent::ChildAdded); } if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->progress = new QProgressBar(statusBar()); d->progress->setMaximumHeight(statusBar()->fontMetrics().height()); d->progress->setRange(0, 100); statusBar()->addPermanentWidget(d->progress); d->progress->show(); d->firstTime = false; } d->progress->setValue(value); qApp->processEvents(); } void KoMainWindow::slotActivePartChanged(KParts::Part *newPart) { // This looks very much like KParts::MainWindow::createGUI, but we have // to reimplement it because it works with an active part, whereas we work // with an active view _and_ an active part, depending for what. // Both are KXMLGUIClients, but e.g. the plugin query needs a QObject. //kDebug(30003) <<"KoMainWindow::slotActivePartChanged( Part * newPart) newPart =" << newPart; //kDebug(30003) <<"current active part is" << d->activePart; if (d->activePart && d->activePart == newPart) { //kDebug(30003) <<"no need to change the GUI"; return; } KXMLGUIFactory *factory = guiFactory(); // ### setUpdatesEnabled( false ); if (d->activeView) { KParts::GUIActivateEvent ev(false); QApplication::sendEvent(d->activePart, &ev); QApplication::sendEvent(d->activeView, &ev); factory->removeClient(d->activeView); unplugActionList("toolbarlist"); qDeleteAll(d->toolbarList); d->toolbarList.clear(); } if (!d->mainWindowGuiIsBuilt) { // Load mainwindow plugins KParts::Plugin::loadPlugins(this, this, componentData(), true); createShellGUI(); } if (newPart && d->manager->activeWidget() && d->manager->activeWidget()->inherits("KoView")) { d->activeView = (KoView *)d->manager->activeWidget(); d->activePart = newPart; //kDebug(30003) <<"new active part is" << d->activePart; factory->addClient(d->activeView); // Position and show toolbars according to user's preference setAutoSaveSettings(newPart->componentData().componentName(), false); foreach (QDockWidget *wdg, d->dockWidgets) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { wdg->setVisible(true); } } // Create and plug toolbar list for Settings menu //QPtrListIterator it = toolBarIterator(); foreach(QWidget* it, factory->containers("ToolBar")) { KToolBar * tb = ::qobject_cast(it); if (tb) { KToggleAction * act = new KToggleAction(i18n("Show %1 Toolbar", tb->windowTitle()), this); actionCollection()->addAction(tb->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", tb->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!tb->isHidden()); d->toolbarList.append(act); } else kWarning(30003) << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } plugActionList("toolbarlist", d->toolbarList); // Send the GUIActivateEvent only now, since it might show/hide toolbars too // (and this has priority over applyMainWindowSettings) KParts::GUIActivateEvent ev(true); QApplication::sendEvent(d->activePart, &ev); QApplication::sendEvent(d->activeView, &ev); } else { d->activeView = 0; d->activePart = 0; } // ### setUpdatesEnabled( true ); } QLabel * KoMainWindow::statusBarLabel() { if (!d->statusBarLabel) { d->statusBarLabel = new QLabel(statusBar()); statusBar()->addPermanentWidget(d->statusBarLabel, 1); } return d->statusBarLabel; } void KoMainWindow::setMaxRecentItems(uint _number) { d->recent->setMaxItems(_number); } void KoMainWindow::slotEmailFile() { if (!rootDocument()) return; // Subject = Document file name // Attachment = The current file // Message Body = The current document in HTML export? <-- This may be an option. QString theSubject; QStringList urls; QString fileURL; if (rootDocument()->url().isEmpty() || rootDocument()->isModified()) { //Save the file as a temporary file bool const tmp_modified = rootDocument()->isModified(); KUrl const tmp_url = rootDocument()->url(); QByteArray const tmp_mimetype = rootDocument()->outputMimeType(); // a little open, close, delete dance to make sure we have a nice filename // to use, but won't block windows from creating a new file with this name. KTemporaryFile *tmpfile = new KTemporaryFile(); tmpfile->open(); QString fileName = tmpfile->fileName(); tmpfile->close(); delete tmpfile; KUrl u; u.setPath(fileName); rootDocument()->setUrl(u); rootDocument()->setModified(true); rootDocument()->setOutputMimeType(rootDocument()->nativeFormatMimeType()); saveDocument(false, true); fileURL = fileName; theSubject = i18n("Document"); urls.append(fileURL); rootDocument()->setUrl(tmp_url); rootDocument()->setModified(tmp_modified); rootDocument()->setOutputMimeType(tmp_mimetype); } else { fileURL = rootDocument()->url().url(); theSubject = i18n("Document - %1", rootDocument()->url().fileName(KUrl::ObeyTrailingSlash)); urls.append(fileURL); } kDebug(30003) << "(" << fileURL << ")"; if (!fileURL.isEmpty()) { KToolInvocation::invokeMailer(QString(), QString(), QString(), theSubject, QString(), //body QString(), urls); // attachments } } void KoMainWindow::slotVersionsFile() { if (!rootDocument()) return; KoVersionDialog *dlg = new KoVersionDialog(this, rootDocument()); dlg->exec(); delete dlg; } void KoMainWindow::slotReloadFile() { KoDocument* pDoc = rootDocument(); if (!pDoc || pDoc->url().isEmpty() || !pDoc->isModified()) return; bool bOk = KMessageBox::questionYesNo(this, i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), i18n("Warning")) == KMessageBox::Yes; if (!bOk) return; KUrl url = pDoc->url(); if (!pDoc->isEmpty()) { setRootDocument(0); // don't delete this shell when deleting the document if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; } openDocument(url); return; } void KoMainWindow::slotImportFile() { kDebug(30003) << "slotImportFile()"; d->isImporting = true; slotFileOpen(); d->isImporting = false; } void KoMainWindow::slotExportFile() { kDebug(30003) << "slotExportFile()"; d->isExporting = true; slotFileSaveAs(); d->isExporting = false; } bool KoMainWindow::isImporting() const { return d->isImporting; } bool KoMainWindow::isExporting() const { return d->isExporting; } void KoMainWindow::setDocToOpen(KoPart *part) { d->partToOpen = part; } QDockWidget* KoMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) return 0; d->dockWidgets.push_back(dockWidget); KoDockWidgetTitleBar *titleBar = 0; // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } if (rootDocument()) { KConfigGroup group = KGlobal::config()->group(d->rootPart->componentData().componentName()).group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; } addDockWidget(side, dockWidget); if (dockWidget->features() & QDockWidget::DockWidgetClosable) { d->dockWidgetMenu->addAction(dockWidget->toggleViewAction()); if (!visible) dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); if (rootDocument()) { KConfigGroup group = KGlobal::config()->group(d->rootPart->componentData().componentName()).group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); } if (titleBar && collapsed) titleBar->setCollapsed(true); d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[ factory->id()]; } KConfigGroup group(KGlobal::config(), "GUI"); QFont dockWidgetFont = KGlobalSettings::generalFont(); qreal pointSize = group.readEntry("palettefontsize", dockWidgetFont.pointSize() * 0.75); pointSize = qMax(pointSize, KGlobalSettings::smallestReadableFont().pointSizeF()); dockWidgetFont.setPointSizeF(pointSize); #ifdef Q_WS_MAC dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(dockWidgetFont); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KoMainWindow::forceDockTabFonts() { QObjectList chis = children(); for (int i = 0; i < chis.size(); ++i) { if (chis.at(i)->inherits("QTabBar")) { QFont dockWidgetFont = KGlobalSettings::generalFont(); qreal pointSize = KGlobalSettings::smallestReadableFont().pointSizeF(); dockWidgetFont.setPointSizeF(pointSize); ((QTabBar *)chis.at(i))->setFont(dockWidgetFont); } } } QList KoMainWindow::dockWidgets() { return d->dockWidgetsMap.values(); } QList KoMainWindow::canvasObservers() { QList observers; foreach(QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } } return observers; } KoDockerManager * KoMainWindow::dockerManager() const { return d->dockerManager; } void KoMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->m_dockerStateBeforeHiding = saveState(); foreach(QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->m_dockerStateBeforeHiding); } } KRecentFilesAction *KoMainWindow::recentAction() const { return d->recent; } KoView* KoMainWindow::currentView() const { // XXX if (d->activeView) { return d->activeView; } else if (!d->rootViews.isEmpty()) { return d->rootViews.first(); } return 0; } #include diff --git a/libs/odf/KoEncryptedStore.cpp b/libs/odf/KoEncryptedStore.cpp index 6a4b1a7cd63..315df1aa6f4 100644 --- a/libs/odf/KoEncryptedStore.cpp +++ b/libs/odf/KoEncryptedStore.cpp @@ -1,818 +1,818 @@ /* This file is part of the KDE project Copyright (C) 2006 Thomas Schaap Copyright (C) 2010 C. Boemann 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. */ #ifdef QCA2 #include "KoEncryptedStore.h" #include "KoEncryptionChecker.h" #include "KoStore_p.h" #include "KoXmlReader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct KoEncryptedStore_EncryptionData { // Needed for Key Derivation QCA::SecureArray salt; unsigned int iterationCount; // Needed for enc/decryption QCA::SecureArray initVector; // Needed for (optional) password-checking QCA::SecureArray checksum; // checksumShort is set to true if the checksum-algorithm is SHA1/1K, which basically means we only use the first 1024 bytes of the unencrypted file to check against (see also http://www.openoffice.org/servlets/ReadMsg?list=dev&msgNo=17498) bool checksumShort; // The size of the uncompressed file qint64 filesize; }; // TODO: Discuss naming of this filer in saving-dialogues // TODO: Discuss possibility of allowing programs to remember the password after opening to enable them to supply it when saving // TODO: Discuss autosaving and password/leakage-problem (currently: hardcoded no autosave) namespace { const char* MANIFEST_FILE = "META-INF/manifest.xml"; const char* META_FILE = "meta.xml"; const char* THUMBNAIL_FILE = "Thumbnails/thumbnail.png"; } KoEncryptedStore::KoEncryptedStore(const QString & filename, Mode mode, const QByteArray & appIdentification) : m_qcaInit(QCA::Initializer()), m_password(QCA::SecureArray()), m_filename(QString(filename)), m_manifestBuffer(QByteArray()), m_tempFile(NULL), m_bPasswordUsed(false), m_bPasswordDeclined(false), m_currentDir(NULL) { Q_D(KoStore); m_pZip = new KZip(filename); d->good = true; d->localFileName = filename; init(mode, appIdentification); } KoEncryptedStore::KoEncryptedStore(QIODevice *dev, Mode mode, const QByteArray & appIdentification) : m_qcaInit(QCA::Initializer()), m_password(QCA::SecureArray()), m_filename(QString()), m_manifestBuffer(QByteArray()), m_tempFile(NULL), m_bPasswordUsed(false), m_bPasswordDeclined(false), m_currentDir(NULL) { Q_D(KoStore); m_pZip = new KZip(dev); d->good = true; init(mode, appIdentification); } KoEncryptedStore::KoEncryptedStore(QWidget* window, const KUrl& url, const QString & filename, Mode mode, const QByteArray & appIdentification) : m_qcaInit(QCA::Initializer()), m_password(QCA::SecureArray()), m_filename(QString(url.url())), m_manifestBuffer(QByteArray()), m_tempFile(NULL), m_bPasswordUsed(false), m_bPasswordDeclined(false), m_currentDir(NULL) { Q_D(KoStore); d->window = window; d->good = true; if (mode == Read) { d->fileMode = KoStorePrivate::RemoteRead; d->localFileName = filename; m_pZip = new KZip(d->localFileName); } else { d->fileMode = KoStorePrivate::RemoteWrite; m_tempFile = new KTemporaryFile(); if (!m_tempFile->open()) { d->good = false; } else { d->localFileName = m_tempFile->fileName(); m_pZip = new KZip(m_tempFile); } } d->url = url; init(mode, appIdentification); } bool KoEncryptedStore::init(Mode mode, const QByteArray & appIdentification) { Q_D(KoStore); bool checksumErrorShown = false; bool unreadableErrorShown = false; if (!KoStore::init(mode) || !d->good) { // This Store is already bad d->good = false; return false; } d->mode = mode; if (mode == Write) { d->good = KoEncryptionChecker::isEncryptionSupported(); if (d->good) { if (!m_pZip->open(QIODevice::WriteOnly)) { d->good = false; return false; } m_pZip->setCompression(KZip::NoCompression); m_pZip->setExtraField(KZip::NoExtraField); // Write identification (void)m_pZip->writeFile("mimetype", "", "", appIdentification.data(), appIdentification.length()); m_pZip->setCompression(KZip::DeflateCompression); // We don't need the extra field in Calligra - so we leave it as "no extra field". } } else { d->good = m_pZip->open(QIODevice::ReadOnly); d->good &= m_pZip->directory() != 0; if (!d->good) { return false; } // Read the manifest-file, so we can get the data we'll need to decrypt the other files in the store const KArchiveEntry* manifestArchiveEntry = m_pZip->directory()->entry(MANIFEST_FILE); if (!manifestArchiveEntry || !manifestArchiveEntry->isFile()) { // No manifest file? OK, *I* won't complain return true; } QIODevice *dev = (static_cast< const KArchiveFile* >(manifestArchiveEntry))->createDevice(); KoXmlDocument xmldoc; bool namespaceProcessing = true; // for the manifest ignore the namespace (bug #260515) if (!xmldoc.setContent(dev, namespaceProcessing) || xmldoc.documentElement().localName() != "manifest" || xmldoc.documentElement().namespaceURI() != KoXmlNS::manifest) { KMessage::message(KMessage::Warning, i18n("The manifest file seems to be corrupted. The document could not be opened.")); dev->close(); delete dev; m_pZip->close(); d->good = false; return false; } KoXmlElement xmlroot = xmldoc.documentElement(); if (xmlroot.hasChildNodes()) { QCA::Base64 base64decoder(QCA::Decode); KoXmlNode xmlnode = xmlroot.firstChild(); while (!xmlnode.isNull()) { // Search for files if (!xmlnode.isElement() || xmlroot.namespaceURI() != KoXmlNS::manifest || xmlnode.toElement().localName() != "file-entry" || !xmlnode.toElement().hasAttribute("full-path") || !xmlnode.hasChildNodes()) { xmlnode = xmlnode.nextSibling(); continue; } // Build a structure to hold the data and fill it with defaults KoEncryptedStore_EncryptionData encData; encData.filesize = 0; encData.checksum = QCA::SecureArray(); encData.checksumShort = false; encData.salt = QCA::SecureArray(); encData.iterationCount = 0; encData.initVector = QCA::SecureArray(); // Get some info about the file QString fullpath = xmlnode.toElement().attribute("full-path"); if (xmlnode.toElement().hasAttribute("size")) { encData.filesize = xmlnode.toElement().attribute("size").toUInt(); } // Find the embedded encryption-data block KoXmlNode xmlencnode = xmlnode.firstChild(); while (!xmlencnode.isNull() && (!xmlencnode.isElement() || xmlencnode.toElement().localName() != "encryption-data" || !xmlencnode.hasChildNodes())) { xmlencnode = xmlencnode.nextSibling(); } if (xmlencnode.isNull()) { xmlnode = xmlnode.nextSibling(); continue; } // Find some things about the checksum if (xmlencnode.toElement().hasAttribute("checksum")) { base64decoder.clear(); - encData.checksum = base64decoder.decode(QCA::SecureArray(xmlencnode.toElement().attribute("checksum").toAscii())); + encData.checksum = base64decoder.decode(QCA::SecureArray(xmlencnode.toElement().attribute("checksum").toLatin1())); if (xmlencnode.toElement().hasAttribute("checksum-type")) { QString checksumType = xmlencnode.toElement().attribute("checksum-type"); if (checksumType == "SHA1") { encData.checksumShort = false; } // For this particual hash-type: check KoEncryptedStore_encryptionData.checksumShort else if (checksumType == "SHA1/1K") { encData.checksumShort = true; } else { // Checksum type unknown if (!checksumErrorShown) { KMessage::message(KMessage::Warning, i18n("This document contains an unknown checksum. When you give a password it might not be verified.")); checksumErrorShown = true; } encData.checksum = QCA::SecureArray(); } } else { encData.checksumShort = false; } } KoXmlNode xmlencattr = xmlencnode.firstChild(); bool algorithmFound = false; bool keyDerivationFound = false; // Search all data about encrption while (!xmlencattr.isNull()) { if (!xmlencattr.isElement()) { xmlencattr = xmlencattr.nextSibling(); continue; } // Find some things about the encryption algorithm if (xmlencattr.toElement().localName() == "algorithm" && xmlencattr.toElement().hasAttribute("initialisation-vector")) { algorithmFound = true; - encData.initVector = base64decoder.decode(QCA::SecureArray(xmlencattr.toElement().attribute("initialisation-vector").toAscii())); + encData.initVector = base64decoder.decode(QCA::SecureArray(xmlencattr.toElement().attribute("initialisation-vector").toLatin1())); if (xmlencattr.toElement().hasAttribute("algorithm-name") && xmlencattr.toElement().attribute("algorithm-name") != "Blowfish CFB") { if (!unreadableErrorShown) { KMessage::message(KMessage::Warning, i18n("This document contains an unknown encryption method. Some parts may be unreadable.")); unreadableErrorShown = true; } encData.initVector = QCA::SecureArray(); } } // Find some things about the key derivation if (xmlencattr.toElement().localName() == "key-derivation" && xmlencattr.toElement().hasAttribute("salt")) { keyDerivationFound = true; - encData.salt = base64decoder.decode(QCA::SecureArray(xmlencattr.toElement().attribute("salt").toAscii())); + encData.salt = base64decoder.decode(QCA::SecureArray(xmlencattr.toElement().attribute("salt").toLatin1())); encData.iterationCount = 1024; if (xmlencattr.toElement().hasAttribute("iteration-count")) { encData.iterationCount = xmlencattr.toElement().attribute("iteration-count").toUInt(); } if (xmlencattr.toElement().hasAttribute("key-derivation-name") && xmlencattr.toElement().attribute("key-derivation-name") != "PBKDF2") { if (!unreadableErrorShown) { KMessage::message(KMessage::Warning, i18n("This document contains an unknown encryption method. Some parts may be unreadable.")); unreadableErrorShown = true; } encData.salt = QCA::SecureArray(); } } xmlencattr = xmlencattr.nextSibling(); } // Only use this encryption data if it makes sense to use it if (!(encData.salt.isEmpty() || encData.initVector.isEmpty())) { m_encryptionData.insert(fullpath, encData); if (!(algorithmFound && keyDerivationFound)) { if (!unreadableErrorShown) { KMessage::message(KMessage::Warning, i18n("This document contains incomplete encryption data. Some parts may be unreadable.")); unreadableErrorShown = true; } } } xmlnode = xmlnode.nextSibling(); } } dev->close(); delete dev; if (isEncrypted() && !(QCA::isSupported("sha1") && QCA::isSupported("pbkdf2(sha1)") && QCA::isSupported("blowfish-cfb"))) { d->good = false; KMessage::message(KMessage::Error, i18n("QCA has currently no support for SHA1 or PBKDF2 using SHA1. The document can not be opened.")); } } return d->good; } bool KoEncryptedStore::doFinalize() { Q_D(KoStore); if (d->good) { if (isOpen()) { close(); } if (d->mode == Write) { // First change the manifest file and write it // We'll use the QDom classes here, since KoXmlReader and KoXmlWriter have no way of copying a complete xml-file // other than parsing it completely and rebuilding it. // Errorhandling here is done to prevent data from being lost whatever happens // TODO: Convert this to KoXML when KoXML is extended enough // Note: right now this is impossible due to lack of possibilities to copy an element as-is QDomDocument document; if (m_manifestBuffer.isEmpty()) { // No manifest? Better create one document = QDomDocument(); QDomElement rootElement = document.createElement("manifest:manifest"); rootElement.setAttribute("xmlns:manifest", "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"); document.appendChild(rootElement); } if (!m_manifestBuffer.isEmpty() && !document.setContent(m_manifestBuffer)) { // Oi! That's fresh XML we should have here! // This is the only case we can't fix KMessage::message(KMessage::Error, i18n("The manifest file seems to be corrupted. It cannot be modified and the document will remain unreadable. Please try and save the document again to prevent losing your work.")); m_pZip->close(); return false; } QDomElement documentElement = document.documentElement(); QDomNodeList fileElements = documentElement.elementsByTagName("manifest:file-entry"); // Search all files in the manifest QStringList foundFiles; for (int i = 0; i < fileElements.size(); i++) { QDomElement fileElement = fileElements.item(i).toElement(); QString fullpath = fileElement.toElement().attribute("manifest:full-path"); // See if it's encrypted if (fullpath.isEmpty() || !m_encryptionData.contains(fullpath)) { continue; } foundFiles += fullpath; KoEncryptedStore_EncryptionData encData = m_encryptionData.value(fullpath); // Set the unencrypted size of the file fileElement.setAttribute("manifest:size", encData.filesize); // See if the user of this store has already provided (old) encryption data QDomNodeList childElements = fileElement.elementsByTagName("manifest:encryption-data"); QDomElement encryptionElement; QDomElement algorithmElement; QDomElement keyDerivationElement; if (childElements.isEmpty()) { encryptionElement = document.createElement("manifest:encryption-data"); fileElement.appendChild(encryptionElement); } else { encryptionElement = childElements.item(0).toElement(); } childElements = encryptionElement.elementsByTagName("manifest:algorithm"); if (childElements.isEmpty()) { algorithmElement = document.createElement("manifest:algorithm"); encryptionElement.appendChild(algorithmElement); } else { algorithmElement = childElements.item(0).toElement(); } childElements = encryptionElement.elementsByTagName("manifest:key-derivation"); if (childElements.isEmpty()) { keyDerivationElement = document.createElement("manifest:key-derivation"); encryptionElement.appendChild(keyDerivationElement); } else { keyDerivationElement = childElements.item(0).toElement(); } // Set the right encryption data QCA::Base64 encoder; QCA::SecureArray checksum = encoder.encode(encData.checksum); if (encData.checksumShort) { encryptionElement.setAttribute("manifest:checksum-type", "SHA1/1K"); } else { encryptionElement.setAttribute("manifest:checksum-type", "SHA1"); } encryptionElement.setAttribute("manifest:checksum", QString(checksum.toByteArray())); QCA::SecureArray initVector = encoder.encode(encData.initVector); algorithmElement.setAttribute("manifest:algorithm-name", "Blowfish CFB"); algorithmElement.setAttribute("manifest:initialisation-vector", QString(initVector.toByteArray())); QCA::SecureArray salt = encoder.encode(encData.salt); keyDerivationElement.setAttribute("manifest:key-derivation-name", "PBKDF2"); keyDerivationElement.setAttribute("manifest:iteration-count", QString::number(encData.iterationCount)); keyDerivationElement.setAttribute("manifest:salt", QString(salt.toByteArray())); } if (foundFiles.size() < m_encryptionData.size()) { QList keys = m_encryptionData.keys(); for (int i = 0; i < keys.size(); i++) { if (!foundFiles.contains(keys.value(i))) { KoEncryptedStore_EncryptionData encData = m_encryptionData.value(keys.value(i)); QDomElement fileElement = document.createElement("manifest:file-entry"); fileElement.setAttribute("manifest:full-path", keys.value(i)); fileElement.setAttribute("manifest:size", encData.filesize); fileElement.setAttribute("manifest:media-type", ""); documentElement.appendChild(fileElement); QDomElement encryptionElement = document.createElement("manifest:encryption-data"); QCA::Base64 encoder; QCA::SecureArray checksum = encoder.encode(encData.checksum); QCA::SecureArray initVector = encoder.encode(encData.initVector); QCA::SecureArray salt = encoder.encode(encData.salt); if (encData.checksumShort) { encryptionElement.setAttribute("manifest:checksum-type", "SHA1/1K"); } else { encryptionElement.setAttribute("manifest:checksum-type", "SHA1"); } encryptionElement.setAttribute("manifest:checksum", QString(checksum.toByteArray())); fileElement.appendChild(encryptionElement); QDomElement algorithmElement = document.createElement("manifest:algorithm"); algorithmElement.setAttribute("manifest:algorithm-name", "Blowfish CFB"); algorithmElement.setAttribute("manifest:initialisation-vector", QString(initVector.toByteArray())); encryptionElement.appendChild(algorithmElement); QDomElement keyDerivationElement = document.createElement("manifest:key-derivation"); keyDerivationElement.setAttribute("manifest:key-derivation-name", "PBKDF2"); keyDerivationElement.setAttribute("manifest:iteration-count", QString::number(encData.iterationCount)); keyDerivationElement.setAttribute("manifest:salt", QString(salt.toByteArray())); encryptionElement.appendChild(keyDerivationElement); } } } m_manifestBuffer = document.toByteArray(); m_pZip->setCompression(KZip::DeflateCompression); if (!m_pZip->writeFile(MANIFEST_FILE, "", "", m_manifestBuffer.data(), m_manifestBuffer.size())) { KMessage::message(KMessage::Error, i18n("The manifest file cannot be written. The document will remain unreadable. Please try and save the document again to prevent losing your work.")); m_pZip->close(); return false; } } } if (m_pZip) return m_pZip->close(); else return true; } KoEncryptedStore::~KoEncryptedStore() { Q_D(KoStore); /* Finalization of an encrypted store must happen earlier than deleting the zip. This rule normally is executed by KoStore, but too late to do any good.*/ if (!d->finalized) { finalize(); } delete m_pZip; if (d->fileMode == KoStorePrivate::RemoteWrite) { KIO::NetAccess::upload(d->localFileName, d->url, d->window); delete m_tempFile; } else if (d->fileMode == KoStorePrivate::RemoteRead) { KIO::NetAccess::removeTempFile(d->localFileName); } delete d->stream; } bool KoEncryptedStore::isEncrypted() { Q_D(KoStore); if (d->mode == Read) { return !m_encryptionData.isEmpty(); } return true; } bool KoEncryptedStore::isToBeEncrypted(const QString& name) { return !(name == META_FILE || name == MANIFEST_FILE || name == THUMBNAIL_FILE); } bool KoEncryptedStore::openRead(const QString& name) { Q_D(KoStore); if (bad()) return false; const KArchiveEntry* fileArchiveEntry = m_pZip->directory()->entry(name); if (!fileArchiveEntry) { return false; } if (fileArchiveEntry->isDirectory()) { kWarning(30002) << name << " is a directory!"; return false; } const KZipFileEntry* fileZipEntry = static_cast(fileArchiveEntry); delete d->stream; d->stream = fileZipEntry->createDevice(); d->size = fileZipEntry->size(); if (m_encryptionData.contains(name)) { // This file is encrypted, do some decryption first if (m_bPasswordDeclined) { // The user has already declined to give a password // Open the file as empty d->stream->close(); delete d->stream; d->stream = new QBuffer(); d->stream->open(QIODevice::ReadOnly); d->size = 0; return true; } QCA::SecureArray encryptedFile(d->stream->readAll()); if (encryptedFile.size() != d->size) { // Read error detected d->stream->close(); delete d->stream; d->stream = NULL; kWarning(30002) << "read error"; return false; } d->stream->close(); delete d->stream; d->stream = NULL; KoEncryptedStore_EncryptionData encData = m_encryptionData.value(name); QCA::SecureArray decrypted; // If we don't have a password yet, try and find one if (m_password.isEmpty()) { findPasswordInKWallet(); } while (true) { QByteArray pass; QCA::SecureArray password; bool keepPass = false; // I already have a password! Let's test it. If it's not good, we can dump it, anyway. if (!m_password.isEmpty()) { password = m_password; m_password = QCA::SecureArray(); } else { if (!m_filename.isNull()) keepPass = false; KPasswordDialog dlg(d->window , keepPass ? KPasswordDialog::ShowKeepPassword : static_cast(0)); dlg.setPrompt(i18n("Please enter the password to open this file.")); if (! dlg.exec()) { m_bPasswordDeclined = true; d->stream = new QBuffer(); d->stream->open(QIODevice::ReadOnly); d->size = 0; return true; } password = QCA::SecureArray(dlg.password().toUtf8()); if (keepPass) keepPass = dlg.keepPassword(); if (password.isEmpty()) { continue; } } decrypted = decryptFile(encryptedFile, encData, password); if (decrypted.isEmpty()) { kError(30002) << "empty decrypted file" << endl; return false; } if (!encData.checksum.isEmpty()) { QCA::SecureArray checksum; if (encData.checksumShort && decrypted.size() > 1024) { // TODO: Eww!!!! I don't want to convert via insecure arrays to get the first 1K characters of a secure array <- fix QCA? checksum = QCA::Hash("sha1").hash(QCA::SecureArray(decrypted.toByteArray().left(1024))); } else { checksum = QCA::Hash("sha1").hash(decrypted); } if (checksum != encData.checksum) { continue; } } // The password passed all possible tests, so let's accept it m_password = password; m_bPasswordUsed = true; if (keepPass) { savePasswordInKWallet(); } break; } QByteArray *resultArray = new QByteArray(decrypted.toByteArray()); QIODevice *resultDevice = KFilterDev::device(new QBuffer(resultArray, NULL), "application/x-gzip"); if (!resultDevice) { delete resultArray; return false; } static_cast(resultDevice)->setSkipHeaders(); d->stream = resultDevice; d->size = encData.filesize; } if (!d->stream->isOpen()) { d->stream->open(QIODevice::ReadOnly); } return true; } bool KoEncryptedStore::closeRead() { Q_D(KoStore); delete d->stream; d->stream = NULL; return true; } void KoEncryptedStore::findPasswordInKWallet() { Q_D(KoStore); /* About KWallet access * * The choice has been made to postfix every entry in a kwallet concerning passwords for opendocument files with /opendocument * This choice has been made since, at the time of this writing, the author could not find any reference to standardized * naming schemes for entries in the wallet. Since collision of passwords in entries should be avoided and is at least possible, * considering remote files might be both protected by a secured web-area (konqueror makes an entry) and a password (we make an * entry), it seems a good thing to make sure it won't happen. */ if (!m_filename.isNull() && !KWallet::Wallet::folderDoesNotExist(KWallet::Wallet::LocalWallet(), KWallet::Wallet::PasswordFolder()) && !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::LocalWallet(), KWallet::Wallet::PasswordFolder(), m_filename + "/opendocument")) { KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), d->window ? d->window->winId() : 0); if (wallet) { if (wallet->setFolder(KWallet::Wallet::PasswordFolder())) { QString pass; wallet->readPassword(m_filename + "/opendocument", pass); m_password = QCA::SecureArray(pass.toUtf8()); } delete wallet; } } } void KoEncryptedStore::savePasswordInKWallet() { Q_D(KoStore); KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), d->window ? d->window->winId() : 0); if (wallet) { if (!wallet->hasFolder(KWallet::Wallet::PasswordFolder())) { wallet->createFolder(KWallet::Wallet::PasswordFolder()); } if (wallet->setFolder(KWallet::Wallet::PasswordFolder())) { if (wallet->hasEntry(m_filename + "/opendocument")) { wallet->removeEntry(m_filename + "/opendocument"); } wallet->writePassword(m_filename + "/opendocument", m_password.toByteArray().data()); } delete wallet; } } QCA::SecureArray KoEncryptedStore::decryptFile(QCA::SecureArray & encryptedFile, KoEncryptedStore_EncryptionData & encData, QCA::SecureArray & password) { QCA::SecureArray keyhash = QCA::Hash("sha1").hash(password); QCA::SymmetricKey key = QCA::PBKDF2("sha1").makeKey(keyhash, QCA::InitializationVector(encData.salt), 16, encData.iterationCount); QCA::Cipher decrypter("blowfish", QCA::Cipher::CFB, QCA::Cipher::DefaultPadding, QCA::Decode, key, QCA::InitializationVector(encData.initVector)); QCA::SecureArray result = decrypter.update(encryptedFile); result += decrypter.final(); return result; } bool KoEncryptedStore::setPassword(const QString& password) { if (m_bPasswordUsed || password.isEmpty()) { return false; } m_password = QCA::SecureArray(password.toUtf8()); return true; } QString KoEncryptedStore::password() { if (m_password.isEmpty()) { return QString(); } return QString(m_password.toByteArray()); } bool KoEncryptedStore::openWrite(const QString& name) { Q_D(KoStore); if (bad()) return false; if (isToBeEncrypted(name)) { // Encrypted files will be compressed by this class and should be stored in the zip as not compressed m_pZip->setCompression(KZip::NoCompression); } else { m_pZip->setCompression(KZip::DeflateCompression); } d->stream = new QBuffer(); (static_cast< QBuffer* >(d->stream))->open(QIODevice::WriteOnly); if (name == MANIFEST_FILE) return true; return m_pZip->prepareWriting(name, "", "", 0); } bool KoEncryptedStore::closeWrite() { Q_D(KoStore); bool passWasAsked = false; if (d->fileName == MANIFEST_FILE) { m_manifestBuffer = static_cast(d->stream)->buffer(); return true; } // Find a password // Do not accept empty passwords for compatibility with OOo if (m_password.isEmpty()) { findPasswordInKWallet(); } while (m_password.isEmpty()) { KNewPasswordDialog dlg(d->window); dlg.setPrompt(i18n("Please enter the password to encrypt the document with.")); if (! dlg.exec()) { // Without the first password, prevent asking again by deadsimply refusing to continue functioning // TODO: This feels rather hackish. There should be a better way to do this. delete m_pZip; m_pZip = 0; d->good = false; return false; } m_password = QCA::SecureArray(dlg.password().toUtf8()); passWasAsked = true; } // Ask the user to save the password if (passWasAsked && KMessageBox::questionYesNo(d->window, i18n("Do you want to save the password?")) == KMessageBox::Yes) { savePasswordInKWallet(); } QByteArray resultData; if (d->fileName == THUMBNAIL_FILE) { // TODO: Replace with a generic 'encrypted'-thumbnail resultData = static_cast(d->stream)->buffer(); } else if (!isToBeEncrypted(d->fileName)) { resultData = static_cast(d->stream)->buffer(); } else { m_bPasswordUsed = true; // Build all cryptographic data QCA::SecureArray passwordHash = QCA::Hash("sha1").hash(m_password); QCA::Random random; KoEncryptedStore_EncryptionData encData; encData.initVector = random.randomArray(8); encData.salt = random.randomArray(16); encData.iterationCount = 1024; QCA::SymmetricKey key = QCA::PBKDF2("sha1").makeKey(passwordHash, QCA::InitializationVector(encData.salt), 16, encData.iterationCount); QCA::Cipher encrypter("blowfish", QCA::Cipher::CFB, QCA::Cipher::DefaultPadding, QCA::Encode, key, QCA::InitializationVector(encData.initVector)); // Get the written data QByteArray data = static_cast(d->stream)->buffer(); encData.filesize = data.size(); // Compress the data QBuffer compressedData; QIODevice *compressDevice = KFilterDev::device(&compressedData, "application/x-gzip", false); if (!compressDevice) { return false; } static_cast(compressDevice)->setSkipHeaders(); if (!compressDevice->open(QIODevice::WriteOnly)) { delete compressDevice; return false; } if (compressDevice->write(data) != data.size()) { delete compressDevice; return false; } compressDevice->close(); delete compressDevice; encData.checksum = QCA::Hash("sha1").hash(QCA::SecureArray(compressedData.buffer())); encData.checksumShort = false; // Encrypt the data QCA::SecureArray result = encrypter.update(QCA::SecureArray(compressedData.buffer())); result += encrypter.final(); resultData = result.toByteArray(); m_encryptionData.insert(d->fileName, encData); } if (!m_pZip->writeData(resultData.data(), resultData.size())) { m_pZip->finishWriting(resultData.size()); return false; } return m_pZip->finishWriting(resultData.size()); } bool KoEncryptedStore::enterRelativeDirectory(const QString& dirName) { Q_D(KoStore); if (d->mode == Read) { if (!m_currentDir) { m_currentDir = m_pZip->directory(); // initialize } const KArchiveEntry *entry = m_currentDir->entry(dirName); if (entry && entry->isDirectory()) { m_currentDir = dynamic_cast(entry); return m_currentDir != 0; } return false; } else { // Write, no checking here return true; } } bool KoEncryptedStore::enterAbsoluteDirectory(const QString& path) { if (path.isEmpty()) { m_currentDir = 0; return true; } m_currentDir = dynamic_cast(m_pZip->directory()->entry(path)); return m_currentDir != 0; } bool KoEncryptedStore::fileExists(const QString& absPath) const { const KArchiveEntry *entry = m_pZip->directory()->entry(absPath); return (entry && entry->isFile()) || (absPath == MANIFEST_FILE && !m_manifestBuffer.isNull()); } #endif // QCA2 diff --git a/libs/odf/KoProperties.cpp b/libs/odf/KoProperties.cpp index c4792df9ca1..18717460574 100644 --- a/libs/odf/KoProperties.cpp +++ b/libs/odf/KoProperties.cpp @@ -1,203 +1,203 @@ /* * Copyright (c) 2006 Boudewijn Rempt * Copyright (C) 2006-2008 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 "KoProperties.h" #include #include class KoProperties::Private { public: QMap properties; }; KoProperties::KoProperties() : d(new Private()) { } KoProperties::KoProperties(const KoProperties & rhs) : d(new Private()) { d->properties = rhs.d->properties; } KoProperties::~KoProperties() { delete d; } QMapIterator KoProperties::propertyIterator() const { return QMapIterator(d->properties); } bool KoProperties::isEmpty() const { return d->properties.isEmpty(); } void KoProperties::load(const QDomElement &root) { d->properties.clear(); QDomElement e = root; QDomNode n = e.firstChild(); while (!n.isNull()) { // We don't nest elements. QDomElement e = n.toElement(); if (!e.isNull()) { if (e.tagName() == "property") { const QString name = e.attribute("name"); const QString type = e.attribute("type"); const QString value = e.text(); - QDataStream in(value.toAscii()); + QDataStream in(value.toLatin1()); QVariant v; in >> v; d->properties[name] = v; } } n = n.nextSibling(); } } bool KoProperties::load(const QString & s) { QDomDocument doc; if (!doc.setContent(s)) return false; load(doc.documentElement()); return true; } void KoProperties::save(QDomElement &root) const { QDomDocument doc = root.ownerDocument(); QMap::Iterator it; for (it = d->properties.begin(); it != d->properties.end(); ++it) { QDomElement e = doc.createElement("property"); e.setAttribute("name", QString(it.key().toLatin1())); QVariant v = it.value(); e.setAttribute("type", v.typeName()); QByteArray bytes; QDataStream out(&bytes, QIODevice::WriteOnly); out << v; - QDomText text = doc.createCDATASection(QString::fromAscii(bytes, bytes.size())); + QDomText text = doc.createCDATASection(QString::fromLatin1(bytes, bytes.size())); e.appendChild(text); root.appendChild(e); } } QString KoProperties::store(const QString &s) const { QDomDocument doc = QDomDocument(s); QDomElement root = doc.createElement(s); doc.appendChild(root); save(root); return doc.toString(); } void KoProperties::setProperty(const QString & name, const QVariant & value) { // If there's an existing value for this name already, replace it. d->properties.insert(name, value); } bool KoProperties::property(const QString & name, QVariant & value) const { QMap::const_iterator it = d->properties.constFind(name); if (it == d->properties.constEnd()) { return false; } else { value = *it; return true; } } QVariant KoProperties::property(const QString & name) const { return d->properties.value(name, QVariant()); } int KoProperties::intProperty(const QString & name, int def) const { const QVariant v = property(name); if (v.isValid()) return v.toInt(); else return def; } qreal KoProperties::doubleProperty(const QString & name, qreal def) const { const QVariant v = property(name); if (v.isValid()) return v.toDouble(); else return def; } bool KoProperties::boolProperty(const QString & name, bool def) const { const QVariant v = property(name); if (v.isValid()) return v.toBool(); else return def; } QString KoProperties::stringProperty(const QString & name, const QString & def) const { const QVariant v = property(name); if (v.isValid()) return v.toString(); else return def; } bool KoProperties::contains(const QString & key) const { return d->properties.contains(key); } QVariant KoProperties::value(const QString & key) const { return d->properties.value(key); } bool KoProperties::operator==(const KoProperties &other) const { if (d->properties.count() != other.d->properties.count()) return false; foreach(const QString & key, d->properties.keys()) { if (other.d->properties.value(key) != d->properties.value(key)) return false; } return true; } diff --git a/libs/odf/tests/storedroptest.cpp b/libs/odf/tests/storedroptest.cpp index 1f48135c1d5..68d51cbc254 100644 --- a/libs/odf/tests/storedroptest.cpp +++ b/libs/odf/tests/storedroptest.cpp @@ -1,175 +1,175 @@ /* This file is part of the KDE project * Copyright (C) 2004 David Faure * * 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 #include #include #include #include #include #include #include #include #include #include class StoreDropTest : public QTextBrowser { public: StoreDropTest(QWidget* parent); protected: virtual void contentsDragEnterEvent(QDragEnterEvent * e); virtual void contentsDragMoveEvent(QDragMoveEvent * e); virtual void contentsDropEvent(QDropEvent * e); virtual void keyPressEvent(QKeyEvent * e); virtual void paste(); private: bool processMimeSource(QMimeSource* ev); void showZipContents(QByteArray data, const char* mimeType, bool oasis); QString loadTextFile(KoStore* store, const QString& fileName); }; int main(int argc, char** argv) { //KApplication::disableAutoDcopRegistration(); KCmdLineArgs::init(argc, argv, "storedroptest", 0, KLocalizedString(), 0, KLocalizedString()); KApplication app; StoreDropTest* window = new StoreDropTest(0); window->resize(500, 500); window->show(); QObject::connect(qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit())); return app.exec(); } StoreDropTest::StoreDropTest(QWidget* parent) : QTextBrowser(parent) { setText("KoStore drop/paste test\nDrop or paste a selection from a Calligra application into this widget to see the ZIP contents"); setAcceptDrops(true); } void StoreDropTest::contentsDragEnterEvent(QDragEnterEvent * ev) { ev->acceptProposedAction(); } void StoreDropTest::contentsDragMoveEvent(QDragMoveEvent * ev) { ev->acceptProposedAction(); } void StoreDropTest::keyPressEvent(QKeyEvent * e) { if (((e->modifiers() & Qt::ShiftModifier) && e->key() == Qt::Key_Insert) || ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_V)) paste(); //else // QTextBrowser::keyPressEvent( e ); } void StoreDropTest::paste() { qDebug("paste"); const QMimeData* m = QApplication::clipboard()->mimeData(); if (!m) return; const QString acceptMimeType("application/vnd.oasis.opendocument."); QStringList formats = m->formats(); foreach(QString fmt, formats) { bool oasis = fmt.startsWith(acceptMimeType); if (oasis || fmt == "application/x-kpresenter") { QByteArray data = m->data(fmt); - showZipContents(data, fmt.toAscii(), oasis); + showZipContents(data, fmt.toLatin1(), oasis); return; } } setText("No acceptable format found. All I got was:\n" + formats.join("\n")); } void StoreDropTest::contentsDropEvent(QDropEvent *ev) { if (processMimeSource(ev)) ev->acceptProposedAction(); else ev->ignore(); } bool StoreDropTest::processMimeSource(QMimeSource* ev) { const QString acceptMimeType("application/vnd.oasis.opendocument."); const char* fmt; QStringList formats; for (int i = 0; (fmt = ev->format(i)); i++) { formats += fmt; bool oasis = QString(fmt).startsWith(acceptMimeType); if (oasis || QString(fmt) == "application/x-kpresenter") { QByteArray data = ev->encodedData(fmt); showZipContents(data, fmt, oasis); return true; } } setText("No acceptable format found. All I got was:\n" + formats.join("\n")); return false; } void StoreDropTest::showZipContents(QByteArray data, const char* mimeType, bool oasis) { if (data.isEmpty()) { setText("No data!"); return; } QBuffer buffer(&data); KoStore * store = KoStore::createStore(&buffer, KoStore::Read); if (store->bad()) { setText("Invalid ZIP!"); delete store; return; } store->disallowNameExpansion(); QString txt = QString("Valid ZIP file found for format ") + mimeType + "\n"; if (oasis) { txt += loadTextFile(store, "content.xml"); txt += loadTextFile(store, "styles.xml"); txt += loadTextFile(store, "settings.xml"); txt += loadTextFile(store, "META-INF/manifest.xml"); } else { txt += loadTextFile(store, "maindoc.xml"); } setText(txt); delete store; } QString StoreDropTest::loadTextFile(KoStore* store, const QString& fileName) { if (!store->open(fileName)) return QString("%1 not found\n").arg(fileName); QByteArray data = store->device()->readAll(); store->close(); QString txt = QString("Found %1: \n").arg(fileName); txt += QString::fromUtf8(data.data(), data.size()); txt += "\n"; return txt; } diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index 4c6c9241b3e..b6271e12320 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,214 +1,214 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoColorSet.h" #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) { // Implemented in KoResource class m_columns = 0; // Set the default value that the GIMP uses... } KoColorSet::KoColorSet() : KoResource("") { m_columns = 0; // Set the default value that the GIMP uses... } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : QObject(0) , KoResource("") { setFilename(rhs.filename()); m_ownData = false; m_name = rhs.m_name; m_comment = rhs.m_comment; m_columns = rhs.m_columns; m_colors = rhs.m_colors; setValid(true); } KoColorSet::~KoColorSet() { } bool KoColorSet::load() { QFile file(filename()); file.open(QIODevice::ReadOnly); m_data = file.readAll(); file.close(); return init(); } bool KoColorSet::save() { QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } QTextStream stream(&file); // Header: Magic\nName: \nColumns: // In any case, we don't use Columns... stream << "GIMP Palette\nName: " << name() << "\nColumns: " << m_columns << "\n#\n"; for (int i = 0; i < m_colors.size(); i++) { const KoColorSetEntry& entry = m_colors.at(i); QColor c = entry.color.toQColor(); stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; if (entry.name.isEmpty()) stream << "Untitled\n"; else stream << entry.name << "\n"; } file.close(); return true; } qint32 KoColorSet::nColors() { return m_colors.count(); } bool KoColorSet::init() { m_colors.clear(); // just in case this is a reload (eg by KoEditColorSetDialog), QString s = QString::fromUtf8(m_data.data(), m_data.count()); if (s.isEmpty() || s.isNull() || s.length() < 50) { kWarning(30009) << "Illegal Gimp palette file: " << filename(); return false; } if (s.startsWith("RIFF") || s.startsWith("PAL data")) { } else if (s.startsWith("GIMP Palette")) { // XXX: No checks for wrong input yet! quint32 index = 0; QStringList lines = s.split('\n', QString::SkipEmptyParts); if (lines.size() < 3) { return false; } QString entry, channel, columns; QStringList c; qint32 r, g, b; QColor color; KoColorSetEntry e; // Read name if (!lines[1].startsWith("Name: ") || !lines[0].startsWith("GIMP")) { kWarning(30009) << "Illegal Gimp palette file: " << filename(); return false; } - setName(i18n(lines[1].mid(strlen("Name: ")).trimmed().toAscii())); + setName(i18n(lines[1].mid(strlen("Name: ")).trimmed().toLatin1())); index = 2; // Read columns if (lines[index].startsWith("Columns: ")) { columns = lines[index].mid(strlen("Columns: ")).trimmed(); m_columns = columns.toInt(); index = 3; } for (qint32 i = index; i < lines.size(); i++) { if (lines[i].startsWith('#')) { m_comment += lines[i].mid(1).trimmed() + ' '; } else if (!lines[i].isEmpty()) { QStringList a = lines[i].replace(QChar('\t'), " ").split(' ', QString::SkipEmptyParts); if (a.count() < 3) { break; } r = a[0].toInt(); a.pop_front(); g = a[0].toInt(); a.pop_front(); b = a[0].toInt(); a.pop_front(); if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { break; } e.color = KoColor(KoColorSpaceRegistry::instance()->rgb8()); e.color.fromQColor(QColor(r, g, b)); QString name = a.join(" "); e.name = name.isEmpty() ? i18n("Untitled") : name; add(e); } } setValid(true); return true; } else if (s.length() == 768) { kWarning(30009) << "Photoshop format palette file. Not implemented yet"; } return false; } void KoColorSet::add(const KoColorSetEntry & c) { m_colors.push_back(c); } void KoColorSet::remove(const KoColorSetEntry & c) { QVector::iterator it = m_colors.begin(); QVector::iterator end = m_colors.end(); while (it != end) { if ((*it) == c) { m_colors.erase(it); return; } ++it; } } KoColorSetEntry KoColorSet::getColor(quint32 index) { return m_colors[index]; } diff --git a/libs/pigment/resources/KoPattern.cpp b/libs/pigment/resources/KoPattern.cpp index f3575205c10..8efabfe6bd1 100644 --- a/libs/pigment/resources/KoPattern.cpp +++ b/libs/pigment/resources/KoPattern.cpp @@ -1,307 +1,307 @@ /* This file is part of the KDE project Copyright (c) 2000 Matthias Elter Copyright (c) 2004 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoPattern.h" #include #include #include #include #include #include #include namespace { struct GimpPatternHeader { quint32 header_size; /* header_size = sizeof (PatternHeader) + brush name */ quint32 version; /* pattern file version # */ quint32 width; /* width of pattern */ quint32 height; /* height of pattern */ quint32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/ quint32 magic_number; /* GIMP brush magic number */ }; // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT' quint32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0)); } KoPattern::KoPattern(const QString& file) : KoResource(file) { } KoPattern::~KoPattern() { } bool KoPattern::load() { QString fileExtension; int index = filename().lastIndexOf('.'); if (index != -1) fileExtension = filename().mid(index).toLower(); bool result; if (fileExtension == ".pat") { QFile file(filename()); file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); file.close(); result = init(data); } else { result = m_image.load(filename()); setValid(result); } return result; } bool KoPattern::save() { QFile file(filename()); file.open(QIODevice::WriteOnly | QIODevice::Truncate); // Header: header_size (24+name length),version,width,height,colordepth of brush,magic,name // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA // magic = "GPAT", as a single uint32, the docs are wrong here! // name is UTF-8 (\0-terminated! The docs say nothing about this!) // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!) // We only save RGBA at the moment // Version is 1 for now... GimpPatternHeader ph; QByteArray utf8Name = name().toUtf8(); char const* name = utf8Name.data(); int nameLength = qstrlen(name); ph.header_size = htonl(sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0 ph.version = htonl(1); ph.width = htonl(width()); ph.height = htonl(height()); ph.bytes = htonl(4); ph.magic_number = htonl(GimpPatternMagic); QByteArray bytes = QByteArray::fromRawData(reinterpret_cast(&ph), sizeof(GimpPatternHeader)); int wrote = file.write(bytes); bytes.clear(); if (wrote == -1) return false; wrote = file.write(name, nameLength + 1); // Trailing 0 apparantly! if (wrote == -1) return false; int k = 0; bytes.resize(width() * height() * 4); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { // RGBA only QRgb pixel = m_image.pixel(x, y); bytes[k++] = static_cast(qRed(pixel)); bytes[k++] = static_cast(qGreen(pixel)); bytes[k++] = static_cast(qBlue(pixel)); bytes[k++] = static_cast(qAlpha(pixel)); } } wrote = file.write(bytes); if (wrote == -1) return false; file.close(); return true; } QImage KoPattern::image() const { return m_image; } bool KoPattern::init(QByteArray& bytes) { int dataSize = bytes.size(); const char* data = bytes.constData(); // load Gimp patterns GimpPatternHeader bh; qint32 k; char* name; if ((int)sizeof(GimpPatternHeader) > dataSize) { return false; } memcpy(&bh, data, sizeof(GimpPatternHeader)); bh.header_size = ntohl(bh.header_size); bh.version = ntohl(bh.version); bh.width = ntohl(bh.width); bh.height = ntohl(bh.height); bh.bytes = ntohl(bh.bytes); bh.magic_number = ntohl(bh.magic_number); if ((int)bh.header_size > dataSize || bh.header_size == 0) { return false; } int size = bh.header_size - sizeof(GimpPatternHeader); name = new char[size]; memcpy(name, data + sizeof(GimpPatternHeader), size); if (name[size - 1]) { delete[] name; return false; } // size -1 so we don't add the end 0 to the QString... - setName(QString::fromAscii(name, size -1)); + setName(QString::fromLatin1(name, size -1)); delete[] name; if (bh.width == 0 || bh.height == 0) { return false; } QImage::Format imageFormat; if (bh.bytes == 1 || bh.bytes == 3) { imageFormat = QImage::Format_RGB32; } else { imageFormat = QImage::Format_ARGB32; } m_image = QImage(bh.width, bh.height, imageFormat); if (m_image.isNull()) { return false; } k = bh.header_size; if (bh.bytes == 1) { // Grayscale qint32 val; for (quint32 y = 0; y < bh.height; y++) { QRgb* pixels = reinterpret_cast( m_image.scanLine(y) ); for (quint32 x = 0; x < bh.width; x++, k++) { if (k > dataSize) { kWarning(30009) << "failed in gray"; return false; } val = data[k]; pixels[x] = qRgb(val, val, val); } } } else if (bh.bytes == 2) { // Grayscale + A qint32 val; qint32 alpha; for (quint32 y = 0; y < bh.height; y++) { QRgb* pixels = reinterpret_cast( m_image.scanLine(y) ); for (quint32 x = 0; x < bh.width; x++, k++) { if (k + 2 > dataSize) { kWarning(30009) << "failed in grayA"; return false; } val = data[k]; alpha = data[k++]; pixels[x] = qRgba(val, val, val, alpha); } } } else if (bh.bytes == 3) { // RGB without alpha for (quint32 y = 0; y < bh.height; y++) { QRgb* pixels = reinterpret_cast( m_image.scanLine(y) ); for (quint32 x = 0; x < bh.width; x++) { if (k + 3 > dataSize) { kWarning(30009) << "failed in RGB"; return false; } pixels[x] = qRgb(data[k], data[k + 1], data[k + 2]); k += 3; } } } else if (bh.bytes == 4) { // Has alpha for (quint32 y = 0; y < bh.height; y++) { QRgb* pixels = reinterpret_cast( m_image.scanLine(y) ); for (quint32 x = 0; x < bh.width; x++) { if (k + 4 > dataSize) { kWarning(30009) << "failed in RGBA"; return false; } pixels[x] = qRgba(data[k], data[k + 1], data[k + 2], data[k + 3]); k += 4; } } } else { return false; } if (m_image.isNull()) { return false; } setValid(true); return true; } qint32 KoPattern::width() const { return m_image.width(); } qint32 KoPattern::height() const { return m_image.height(); } void KoPattern::setImage(const QImage& image) { m_image = image; m_image.detach(); setValid(true); } KoPattern& KoPattern::operator=(const KoPattern & pattern) { setFilename(pattern.filename()); setImage(pattern.image()); setValid(true); return *this; } QString KoPattern::defaultFileExtension() const { return QString(".pat"); } diff --git a/libs/widgets/KoResourceServer.h b/libs/widgets/KoResourceServer.h index c3ac731f273..3c2f404a2da 100644 --- a/libs/widgets/KoResourceServer.h +++ b/libs/widgets/KoResourceServer.h @@ -1,565 +1,565 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (c) 2007 Jan Hambrecht Copyright (C) 2011 Srikanth Tiyyagura This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCESERVER_H #define KORESOURCESERVER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoResource.h" #include "KoResourceServerObserver.h" #include "KoResourceTagging.h" #include "kowidgets_export.h" #include class KoResource; /** * KoResourceServerBase is the base class of all resource servers */ class KOWIDGETS_EXPORT KoResourceServerBase { public: /** * Constructs a KoResourceServerBase * @param resource type, has to be the same as used by KStandardDirs * @param extensions the file extensions separate by ':', e.g. "*.kgr:*.svg:*.ggr" */ KoResourceServerBase(const QString& type, const QString& extensions) : m_type(type) , m_extensions(extensions) , m_cancelled(false) { } virtual ~KoResourceServerBase() {} virtual void loadResources(QStringList filenames) = 0; virtual QStringList blackListedFiles() = 0; QString type() { return m_type; } /** * File extensions for resources of the server * @returns the file extensions separated by ':', e.g. "*.kgr:*.svg:*.ggr" */ QString extensions() { return m_extensions; } void cancel() { m_cancelled = true; } private: QString m_type; QString m_extensions; protected: bool m_cancelled; QMutex m_loadLock; }; /** * KoResourceServer manages the resources of one type. It stores, loads and saves the resources. * To keep track of changes the server can be observed with a KoResourceServerObserver */ template class KoResourceServer : public KoResourceServerBase { public: KoResourceServer(const QString& type, const QString& extensions, bool deleteResource = true) : KoResourceServerBase(type, extensions) , m_deleteResource(deleteResource) { m_blackListFile = KStandardDirs::locateLocal("data", "krita/" + type + ".blacklist"); m_blackListFileNames = readBlackListFile(); m_tagObject = new KoResourceTagging(extensions); } virtual ~KoResourceServer() { qDeleteAll(m_resources); delete m_tagObject; } /** * Loads a set of resources and adds them to the resource server. * If a filename appears twice the resource will only be added once. Resources that can't * be loaded or and invalid aren't added to the server. * @param filenames list of filenames to be loaded */ void loadResources(QStringList filenames) { kDebug(30009) << "loading resources for type " << type(); QStringList uniqueFiles; while (!filenames.empty() && !m_cancelled) { QString front = filenames.first(); filenames.pop_front(); QString fname = QFileInfo(front).fileName(); // XXX: Don't load resources with the same filename. Actually, we should look inside // the resource to find out whether they are really the same, but for now this // will prevent the same brush etc. showing up twice. if (uniqueFiles.empty() || uniqueFiles.indexOf(fname) == -1) { m_loadLock.lock(); uniqueFiles.append(fname); QList resources = createResources(front); foreach(T* resource, resources) { Q_CHECK_PTR(resource); if (resource->load() && resource->valid()) { m_resourcesByFilename[resource->shortFilename()] = resource; if ( resource->name().isEmpty() ) { resource->setName( fname ); } if (m_resourcesByName.contains(resource->name())) { resource->setName(resource->name() + "(" + resource->shortFilename() + ")"); } m_resourcesByName[resource->name()] = resource; notifyResourceAdded(resource); } else { delete resource; } } m_loadLock.unlock(); } } m_resources = sortedResources(); kDebug(30009) << "done loading resources for type " << type(); } /// Adds an already loaded resource to the server bool addResource(T* resource, bool save = true, bool infront = false) { if (!resource->valid()) { kWarning(30009) << "Tried to add an invalid resource!"; return false; } QFileInfo fileInfo(resource->filename()); if (fileInfo.exists()) { QString filename = fileInfo.path() + "/" + fileInfo.baseName() + "XXXXXX" + "." + fileInfo.suffix(); kDebug() << "fileName is " << filename; QTemporaryFile file(filename); if (file.open()) { kDebug() << "now " << file.fileName(); resource->setFilename(file.fileName()); } } if( save && ! resource->save()) { kWarning(30009) << "Could not save resource!"; return false; } Q_ASSERT( !resource->filename().isEmpty() || !resource->name().isEmpty() ); if ( resource->filename().isEmpty() ) { resource->setFilename( resource->name() ); } else if ( resource->name().isEmpty() ) { resource->setName( resource->filename() ); } m_resourcesByFilename[resource->shortFilename()] = resource; m_resourcesByName[resource->name()] = resource; if (infront) { m_resources.insert(0, resource); } else { m_resources.append(resource); } notifyResourceAdded(resource); return true; } /// Remove a resource from Resource Server but not from a file bool removeResourceFromServer(T* resource){ if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) { return false; } m_resourcesByName.remove(resource->name()); m_resourcesByFilename.remove(resource->shortFilename()); m_resources.removeAt(m_resources.indexOf(resource)); notifyRemovingResource(resource); if (m_deleteResource) { delete resource; } return true; } /// Remove a resource from resourceserver and hard disk bool removeResource(T* resource) { if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) { return false; } m_resourcesByName.remove(resource->name()); m_resourcesByFilename.remove(resource->shortFilename()); m_resources.removeAt(m_resources.indexOf(resource)); notifyRemovingResource(resource); m_blackListFileNames.append(resource->filename()); writeBlackListFile(); if (m_deleteResource && resource) { delete resource; } return true; } QList resources() { m_loadLock.lock(); QList resourceList = m_resources; foreach(T* r, m_resourceBlackList) { resourceList.removeOne(r); } m_loadLock.unlock(); return resourceList; } /// Returns path where to save user defined and imported resources to virtual QString saveLocation() { - return KGlobal::mainComponent().dirs()->saveLocation(type().toAscii()); + return KGlobal::mainComponent().dirs()->saveLocation(type().toLatin1()); } /** * Creates a new resource from a given file and adds them to the resource server * The base implementation does only load one resource per file, override to implement collections * @param filename file name of the resource file to be imported * @param fileCreation decides whether to create the file in the saveLocation() directory */ virtual void importResourceFile(const QString & filename , bool fileCreation=true) { QFileInfo fi( filename ); if( fi.exists() == false ) return; T* resource = createResource( filename ); resource->load(); if(!resource->valid()){ kWarning(30009) << "Import failed! Resource is not valid"; delete resource; return; } if(fileCreation) { Q_ASSERT(!resource->defaultFileExtension().isEmpty()); Q_ASSERT(!saveLocation().isEmpty()); QString newFilename = saveLocation() + fi.baseName() + resource->defaultFileExtension(); QFileInfo fileInfo(newFilename); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(saveLocation() + fi.baseName() + QString("%1").arg(i) + resource->defaultFileExtension()); i++; } resource->setFilename(fileInfo.filePath()); } if(!addResource(resource)) { delete resource; } } /// Removes the resource file from the resource server virtual void removeResourceFile(const QString & filename) { QFileInfo fi(filename); T* resource = getResourceByFilename(fi.fileName()); if (!resource) { kWarning(30009) << "Resource file do not exist "; return; } if (!removeResourceFromServer(resource)) return; } /** * Addes an observer to the server * @param observer the observer to be added * @param notifyLoadedResources determines if the observer should be notified about the already loaded resources */ void addObserver(KoResourceServerObserver* observer, bool notifyLoadedResources = true) { m_loadLock.lock(); if(observer && !m_observers.contains(observer)) { m_observers.append(observer); if(notifyLoadedResources) { foreach(T* resource, m_resourcesByFilename) { observer->resourceAdded(resource); } } } m_loadLock.unlock(); } /** * Removes an observer from the server * @param observer the observer to be removed */ void removeObserver(KoResourceServerObserver* observer) { int index = m_observers.indexOf( observer ); if( index < 0 ) return; m_observers.removeAt( index ); } T* getResourceByFilename( const QString& filename ) { if ( !m_resourcesByFilename.contains( filename ) ) { return 0; } return m_resourcesByFilename[filename]; } T* getResourceByName( const QString& name ) { if ( !m_resourcesByName.contains( name ) ) { return 0; } return m_resourcesByName[name]; } /** * Call after changing the content of a resource; * Notifies the connected views. */ void updateResource( T* resource ) { notifyResourceChanged(resource); } QStringList blackListedFiles() { return m_blackListFileNames; } void removeBlackListedFiles() { QStringList remainingFiles; // Files that can't be removed e.g. no rights will stay blacklisted foreach(QString filename, m_blackListFileNames) { QFile file( filename ); if( ! file.remove() ) { remainingFiles.append(filename); } } m_blackListFileNames = remainingFiles; writeBlackListFile(); } /// the below functions helps to access tagObject functions QStringList getAssignedTagsList( KoResource* resource ) { return m_tagObject->getAssignedTagsList(resource); } QStringList getTagNamesList() { return m_tagObject->getTagNamesList(); } void addTag( KoResource* resource,const QString& tag) { m_tagObject->addTag(resource,tag); } void delTag( KoResource* resource,const QString& tag) { m_tagObject->delTag(resource,tag); } QStringList searchTag(const QString& lineEditText) { return m_tagObject->searchTag(lineEditText); } #ifdef NEPOMUK void updateNepomukXML(bool nepomukOn) { KoResourceTagging* tagObject = new KoResourceTagging(extensions()); tagObject->setNepomukBool(nepomukOn); tagObject->updateNepomukXML(nepomukOn); delete tagObject; m_tagObject->setNepomukBool(nepomukOn); } #endif protected: /** * Create one or more resources from a single file. By default one resource is created. * Overide to create more resources from the file. * @param filename the filename of the resource or resource collection */ virtual QList createResources( const QString & filename ) { QList createdResources; createdResources.append(createResource(filename)); return createdResources; } virtual T* createResource( const QString & filename ) { return new T(filename); } /// Return the currently stored resources in alphabetical order, overwrite for customized sorting virtual QList sortedResources() { QMap sortedNames; foreach(QString name, m_resourcesByName.keys()) { sortedNames.insert(name.toLower(), m_resourcesByName[name]); } return sortedNames.values(); } void notifyResourceAdded(T* resource) { foreach(KoResourceServerObserver* observer, m_observers) { observer->resourceAdded(resource); } } void notifyRemovingResource(T* resource) { foreach(KoResourceServerObserver* observer, m_observers) { observer->removingResource(resource); } } void notifyResourceChanged(T* resource) { foreach(KoResourceServerObserver* observer, m_observers) { observer->resourceChanged(resource); } } /// Reads the xml file and returns the filenames as a list QStringList readBlackListFile() { QStringList filenameList; QFile f(m_blackListFile); if (!f.open(QIODevice::ReadOnly)) { return filenameList; } QDomDocument doc; if (!doc.setContent(&f)) { kWarning() << "The file could not be parsed."; return filenameList; } QDomElement root = doc.documentElement(); if (root.tagName() != "resourceFilesList") { kWarning() << "The file doesn't seem to be of interest."; return filenameList; } QDomElement file = root.firstChildElement("file"); while (!file.isNull()) { QDomNode n = file.firstChild(); QDomElement e = n.toElement(); if (e.tagName() == "name") { filenameList.append((e.text()).replace(QString("~"),QDir::homePath())); } file = file.nextSiblingElement("file"); } return filenameList; } /// write the blacklist file entries to an xml file void writeBlackListFile() { QFile f(m_blackListFile); if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { kWarning() << "Cannot write meta information to '" << m_blackListFile << "'." << endl; return; } QDomDocument doc; QDomElement root; QDomDocument docTemp("m_blackListFile"); doc = docTemp; doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"")); root = doc.createElement("resourceFilesList"); doc.appendChild(root); foreach(QString filename, m_blackListFileNames) { QDomElement fileEl = doc.createElement("file"); QDomElement nameEl = doc.createElement("name"); QDomText nameText = doc.createTextNode(filename.replace(QDir::homePath(),QString("~"))); nameEl.appendChild(nameText); fileEl.appendChild(nameEl); root.appendChild(fileEl); } QTextStream metastream(&f); metastream << doc.toByteArray(); f.close(); } private: QHash m_resourcesByName; QHash m_resourcesByFilename; QList m_resourceBlackList; QList m_resources; ///< list of resources in order of addition QList*> m_observers; bool m_deleteResource; QString m_blackListFile; QStringList m_blackListFileNames; KoResourceTagging* m_tagObject; }; #endif // KORESOURCESERVER_H diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp index 2d927dc9200..d293f9bb57f 100644 --- a/libs/widgets/KoResourceServerProvider.cpp +++ b/libs/widgets/KoResourceServerProvider.cpp @@ -1,231 +1,231 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (C) 2011 Srikanth Tiyyagura This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoResourceServerProvider.h" #include #include #include #include #include #include #include "KoSegmentGradient.h" #include "KoStopGradient.h" #include "KoColorSpaceRegistry.h" class GradientResourceServer : public KoResourceServer { public: GradientResourceServer(const QString& type, const QString& extensions) : KoResourceServer(type, extensions) , m_foregroundToTransparent(0) , m_foregroundToBackground(0) { insertSpecialGradients(); } void insertSpecialGradients() { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); QList stops; KoStopGradient* gradient = new KoStopGradient(""); gradient->setType(QGradient::LinearGradient); gradient->setName("Foreground to Transparent"); stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), cs)); gradient->setStops(stops); gradient->setValid(true); addResource(gradient, false, true); m_foregroundToTransparent = gradient; gradient = new KoStopGradient(""); gradient->setType(QGradient::LinearGradient); gradient->setName("Foreground to Background"); stops.clear(); stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(Qt::white, cs)); gradient->setStops(stops); gradient->setValid(true); addResource(gradient, false, true); m_foregroundToBackground = gradient; } private: virtual KoAbstractGradient* createResource( const QString & filename ) { QString fileExtension; int index = filename.lastIndexOf('.'); if (index != -1) fileExtension = filename.mid(index).toLower(); KoAbstractGradient* grad = 0; if(fileExtension == ".svg" || fileExtension == ".kgr") grad = new KoStopGradient(filename); else if(fileExtension == ".ggr" ) grad = new KoSegmentGradient(filename); return grad; } virtual QList< KoAbstractGradient* > sortedResources() { QList< KoAbstractGradient* > resources = KoResourceServer::sortedResources(); QList< KoAbstractGradient* > sorted; if (m_foregroundToTransparent && resources.contains(m_foregroundToTransparent)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToTransparent))); } if (m_foregroundToBackground && resources.contains(m_foregroundToBackground)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToBackground))); } return sorted + resources; } KoAbstractGradient* m_foregroundToTransparent; KoAbstractGradient* m_foregroundToBackground; }; KoResourceLoaderThread::KoResourceLoaderThread(KoResourceServerBase * server) : QThread() , m_server(server) { m_fileNames = getFileNames(m_server->extensions()); QStringList fileNames = m_server->blackListedFiles(); if (!fileNames.isEmpty()) { foreach (QString s, fileNames) { if (m_fileNames.contains(s)) { m_fileNames.removeAll(s); } } } } KoResourceLoaderThread::~KoResourceLoaderThread() { barrier(); } void KoResourceLoaderThread::run() { m_server->loadResources(m_fileNames); } void KoResourceLoaderThread::barrier() { if(isRunning()) { wait(); } } QStringList KoResourceLoaderThread::getFileNames( const QString & extensions) { QStringList extensionList = extensions.split(':'); QStringList fileNames; foreach (const QString &extension, extensionList) { - fileNames += KGlobal::mainComponent().dirs()->findAllResources(m_server->type().toAscii(), extension, KStandardDirs::Recursive | KStandardDirs::NoDuplicates); + fileNames += KGlobal::mainComponent().dirs()->findAllResources(m_server->type().toLatin1(), extension, KStandardDirs::Recursive | KStandardDirs::NoDuplicates); } return fileNames; } struct KoResourceServerProvider::Private { KoResourceServer* m_patternServer; KoResourceServer* m_gradientServer; KoResourceServer* m_paletteServer; KoResourceLoaderThread *paletteThread; KoResourceLoaderThread *gradientThread; KoResourceLoaderThread *patternThread; }; KoResourceServerProvider::KoResourceServerProvider() : d(new Private) { KGlobal::mainComponent().dirs()->addResourceType("ko_patterns", "data", "krita/patterns/"); KGlobal::mainComponent().dirs()->addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); KGlobal::mainComponent().dirs()->addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); KGlobal::mainComponent().dirs()->addResourceType("ko_gradients", "data", "krita/gradients/"); KGlobal::mainComponent().dirs()->addResourceType("ko_gradients", "data", "karbon/gradients/"); KGlobal::mainComponent().dirs()->addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); KGlobal::mainComponent().dirs()->addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); KGlobal::mainComponent().dirs()->addResourceType("ko_palettes", "data", "krita/palettes/"); KGlobal::mainComponent().dirs()->addResourceType("ko_palettes", "data", "karbon/palettes/"); KGlobal::mainComponent().dirs()->addResourceDir("ko_palettes", "/usr/share/create/swatches"); KGlobal::mainComponent().dirs()->addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); d->m_patternServer = new KoResourceServer("ko_patterns", "*.pat:*.jpg:*.gif:*.png:*.tif:*.xpm:*.bmp" ); d->patternThread = new KoResourceLoaderThread(d->m_patternServer); d->patternThread->start(); d->m_gradientServer = new GradientResourceServer("ko_gradients", "*.kgr:*.svg:*.ggr"); d->gradientThread = new KoResourceLoaderThread(d->m_gradientServer); d->gradientThread->start(); d->m_paletteServer = new KoResourceServer("ko_palettes", "*.gpl:*.pal:*.act"); d->paletteThread = new KoResourceLoaderThread(d->m_paletteServer); d->paletteThread->start(); } KoResourceServerProvider::~KoResourceServerProvider() { delete d->patternThread; delete d->gradientThread; delete d->paletteThread; delete d->m_patternServer; delete d->m_gradientServer; delete d->m_paletteServer; delete d; } KoResourceServerProvider* KoResourceServerProvider::instance() { K_GLOBAL_STATIC(KoResourceServerProvider, s_instance); return s_instance; } KoResourceServer* KoResourceServerProvider::patternServer() { d->patternThread->barrier(); return d->m_patternServer; } KoResourceServer* KoResourceServerProvider::gradientServer() { d->patternThread->barrier(); return d->m_gradientServer; } KoResourceServer* KoResourceServerProvider::paletteServer() { d->patternThread->barrier(); return d->m_paletteServer; } diff --git a/plan/libs/models/kptitemmodelbase.cpp b/plan/libs/models/kptitemmodelbase.cpp index faf61e699ab..32f4a139607 100644 --- a/plan/libs/models/kptitemmodelbase.cpp +++ b/plan/libs/models/kptitemmodelbase.cpp @@ -1,738 +1,738 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2007, 2012 Dag Andersen 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 "kptitemmodelbase.h" #include "kptproject.h" #include "kptschedule.h" #include "kptdurationspinbox.h" #include "kptresourcemodel.h" #include "kptresourceallocationmodel.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------------------------- bool ItemDelegate::eventFilter(QObject *object, QEvent *event) { QWidget *editor = ::qobject_cast(object); if (!editor) { return false; } m_lastHint = Delegate::NoHint; if (event->type() == QEvent::KeyPress) { QKeyEvent *e = static_cast(event); if ( e->modifiers() & Qt::AltModifier && e->modifiers() & Qt::ControlModifier ) { switch ( e->key() ) { case Qt::Key_Left: m_lastHint = Delegate::EditLeftItem; emit commitData(editor); emit closeEditor(editor, QAbstractItemDelegate::NoHint ); return true; case Qt::Key_Right: m_lastHint = Delegate::EditRightItem; emit commitData(editor); emit closeEditor( editor, QAbstractItemDelegate::NoHint ); return true; case Qt::Key_Down: m_lastHint = Delegate::EditDownItem; emit commitData(editor); emit closeEditor(editor, QAbstractItemDelegate::NoHint ); return true; case Qt::Key_Up: m_lastHint = Delegate::EditUpItem; emit commitData(editor); emit closeEditor(editor, QAbstractItemDelegate::NoHint ); return true; default: break; } } } return QStyledItemDelegate::eventFilter( object, event ); } QSize ItemDelegate::sizeHint( const QStyleOptionViewItem & option, const QModelIndex & index ) const { // 18 is a bit arbitrary, it gives (most?) editors a usable size QSize s = QStyledItemDelegate::sizeHint( option, index ); return QSize( s.width(), qMax( s.height(), 18 ) ); } //---------------------- CheckStateItemDelegate::CheckStateItemDelegate( QObject *parent ) : ItemDelegate( parent ) { } bool CheckStateItemDelegate::editorEvent( QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index ) { Q_ASSERT(event); Q_ASSERT(model); kDebug(planDbg()); Qt::ItemFlags flags = model->flags(index); if ( ! ( option.state & QStyle::State_Enabled ) || ! ( flags & Qt::ItemIsEnabled ) ) { return false; } // make sure that we have a check state QVariant value = index.data( Qt::EditRole ); if ( ! value.isValid() ) { return false; } QStyle *style = QApplication::style(); // make sure that we have the right event type if ( ( event->type() == QEvent::MouseButtonRelease ) || ( event->type() == QEvent::MouseButtonDblClick ) || ( event->type() == QEvent::MouseButtonPress ) ) { QStyleOptionViewItemV4 viewOpt( option ); initStyleOption( &viewOpt, index ); QRect checkRect = style->subElementRect( QStyle::SE_ItemViewItemDecoration, &viewOpt, 0 ); QMouseEvent *me = static_cast( event ); if ( me->button() != Qt::LeftButton || ! checkRect.contains( me->pos() ) ) { return false; } if ( ( event->type() == QEvent::MouseButtonPress ) || ( event->type() == QEvent::MouseButtonDblClick ) ) { return true; } } else if ( event->type() == QEvent::KeyPress ) { if (static_cast(event)->key() != Qt::Key_Space && static_cast(event)->key() != Qt::Key_Select) { return false; } } else { return false; } Qt::CheckState state = ( static_cast( value.toInt() ) == Qt::Checked ? Qt::Unchecked : Qt::Checked ); return model->setData(index, state, Qt::CheckStateRole); } //---------------------- DateTimeCalendarDelegate::DateTimeCalendarDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *DateTimeCalendarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QDateTimeEdit *editor = new QDateTimeEdit(parent); editor->setCalendarPopup( true ); editor->installEventFilter(const_cast(this)); return editor; } void DateTimeCalendarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QDateTime value = index.model()->data(index, Qt::EditRole).toDateTime(); QDateTimeEdit *e = static_cast(editor); e->setDateTime( value ); } void DateTimeCalendarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QDateTimeEdit *e = static_cast(editor); model->setData( index, e->dateTime(), Qt::EditRole ); } void DateTimeCalendarDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { kDebug(planDbg())<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() 50); editor->setGeometry(r); } //----------------------------- ProgressBarDelegate::ProgressBarDelegate( QObject *parent ) : ItemDelegate( parent ) { } ProgressBarDelegate::~ProgressBarDelegate() { } void ProgressBarDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const { QStyle *style; QStyleOptionViewItemV4 opt = option; initStyleOption( &opt, index ); style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter ); if ( !( opt.state & QStyle::State_Editing ) ) { bool ok = false; (void) index.data().toInt(&ok); if ( ok ) { QStyleOptionProgressBar pbOption; pbOption.QStyleOption::operator=( option ); initStyleOptionProgressBar( &pbOption, index ); style->drawControl( QStyle::CE_ProgressBar, &pbOption, painter ); // Draw focus, copied from qt if (opt.state & QStyle::State_HasFocus) { painter->save(); QStyleOptionFocusRect o; o.QStyleOption::operator=( opt ); o.rect = style->subElementRect( QStyle::SE_ItemViewItemFocusRect, &opt, opt.widget ); o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; QPalette::ColorGroup cg = ( opt.state & QStyle::State_Enabled ) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = opt.palette.color( cg, ( opt.state & QStyle::State_Selected ) ? QPalette::Highlight : QPalette::Window ); style->drawPrimitive( QStyle::PE_FrameFocusRect, &o, painter, opt.widget ); //kDebug(planDbg())<<"Focus"<restore(); } } else { EnumDelegate del; del.paint( painter, option, index ); } } } QSize ProgressBarDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const { QStyleOptionViewItemV4 opt = option; // initStyleOption( &opt, index ); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); QStyleOptionProgressBar pbOption; pbOption.QStyleOption::operator=( option ); initStyleOptionProgressBar( &pbOption, index ); return style->sizeFromContents( QStyle::CT_ProgressBar, &pbOption, QSize(), opt.widget ); } void ProgressBarDelegate::initStyleOptionProgressBar( QStyleOptionProgressBar *option, const QModelIndex &index ) const { option->rect.adjust( 0, 1, 0, -1 ); option->minimum = 0; int max = index.data( Role::Maximum ).toInt(); option->maximum = max > option->minimum ? max : option->minimum + 100; option->progress = index.data().toInt(); - option->text = QString::number( ( option->progress * 100 ) / ( option->maximum - option->minimum ) ) + QChar::fromAscii( '%' ); + option->text = QString::number( ( option->progress * 100 ) / ( option->maximum - option->minimum ) ) + QLatin1Char( '%' ); option->textAlignment = Qt::AlignCenter; option->textVisible = true; } QWidget *ProgressBarDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const { Slider *slider = new Slider( parent ); slider->setRange( 0, 100 ); slider->setOrientation( Qt::Horizontal ); //kDebug(planDbg())<minimumSizeHint()<minimumSize(); return slider; } void ProgressBarDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const { QSlider *slider = static_cast( editor ); slider->setValue( index.data( Qt::EditRole ).toInt() ); } void ProgressBarDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const { QSlider *slider = static_cast( editor ); model->setData( index, slider->value() ); } void ProgressBarDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & ) const { editor->setGeometry( option.rect ); //kDebug(planDbg())<minimumSizeHint()<minimumSize()<geometry()<size(); } Slider::Slider( QWidget *parent ) : QSlider( parent ) { connect( this, SIGNAL(valueChanged(int)), this, SLOT(updateTip(int)) ); } void Slider::updateTip( int value ) { QPoint p; p.setY( height() / 2 ); p.setX( style()->sliderPositionFromValue ( minimum(), maximum(), value, width() ) ); - QString text = QString::fromAscii( "%1%" ).arg( value ); + QString text = QString::number( value ) + QLatin1Char( '%' ); QToolTip::showText( mapToGlobal( p ), text, this ); } //-------------------------------------- // Hmmm, a bit hacky, but this makes it possible to use index specific editors... SelectorDelegate::SelectorDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *SelectorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index ) const { switch ( index.model()->data( index, Role::EditorType ).toInt() ) { case Delegate::EnumEditor: { QComboBox *editor = new KComboBox(parent); editor->installEventFilter(const_cast(this)); return editor; } case Delegate::TimeEditor: { QTimeEdit *editor = new QTimeEdit(parent); editor->installEventFilter(const_cast(this)); return editor; } } return 0; // FIXME: What to do? } void SelectorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { switch ( index.model()->data( index, Role::EditorType ).toInt() ) { case Delegate::EnumEditor: { QStringList lst = index.model()->data( index, Role::EnumList ).toStringList(); int value = index.model()->data(index, Role::EnumListValue).toInt(); QComboBox *box = static_cast(editor); box->addItems( lst ); box->setCurrentIndex( value ); return; } case Delegate::TimeEditor: QTime value = index.model()->data(index, Qt::EditRole).toTime(); QTimeEdit *e = static_cast(editor); e->setMinimumTime( index.model()->data( index, Role::Minimum ).toTime() ); e->setMaximumTime( index.model()->data( index, Role::Maximum ).toTime() ); e->setTime( value ); return; } } void SelectorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { switch ( index.model()->data( index, Role::EditorType ).toInt() ) { case Delegate::EnumEditor: { QComboBox *box = static_cast(editor); int value = box->currentIndex(); model->setData( index, value, Qt::EditRole ); return; } case Delegate::TimeEditor: { QTimeEdit *e = static_cast(editor); model->setData( index, e->time(), Qt::EditRole ); return; } } } void SelectorDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } EnumDelegate::EnumDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QComboBox *editor = new KComboBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void EnumDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QStringList lst = index.model()->data( index, Role::EnumList ).toStringList(); int value = index.model()->data(index, Role::EnumListValue).toInt(); QComboBox *box = static_cast(editor); box->addItems( lst ); box->setCurrentIndex( value ); } void EnumDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *box = static_cast(editor); int value = box->currentIndex(); model->setData( index, value, Qt::EditRole ); } void EnumDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { kDebug(planDbg())<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() 50); editor->setGeometry(r); } //--------------------------- RequieredResourceDelegate::RequieredResourceDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *RequieredResourceDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const { if ( index.data( Qt::CheckStateRole ).toInt() == Qt::Unchecked ) { return 0; } TreeComboBox *editor = new TreeComboBox(parent); editor->installEventFilter(const_cast(this)); ResourceItemSFModel *m = new ResourceItemSFModel( editor ); editor->setModel( m ); return editor; } void RequieredResourceDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { TreeComboBox *box = static_cast(editor); ResourceItemSFModel *pm = static_cast( box->model() ); ResourceItemModel *rm = qobject_cast( pm->sourceModel() ); Q_ASSERT( rm ); const ResourceAllocationItemModel *model = qobject_cast( index.model() ); Q_ASSERT( model ); rm->setProject( model->project() ); pm->addFilteredResource( model->resource( index ) ); QItemSelectionModel *sm = box->view()->selectionModel(); sm->clearSelection(); foreach ( const Resource *r, model->required( index ) ) { QModelIndex i = pm->mapFromSource( rm->index( r ) ); sm->select( i, QItemSelectionModel::Select | QItemSelectionModel::Rows ); } box->setCurrentIndexes( sm->selectedRows() ); box->view()->expandAll(); } void RequieredResourceDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { TreeComboBox *box = static_cast(editor); QAbstractProxyModel *pm = static_cast( box->model() ); ResourceItemModel *rm = qobject_cast( pm->sourceModel() ); QList lst; foreach ( const QModelIndex &i, box->currentIndexes() ) { lst << rm->resource( pm->mapToSource( i ) ); } ResourceAllocationItemModel *mdl = qobject_cast( model ); Q_ASSERT( mdl ); mdl->setRequired( index, lst ); } void RequieredResourceDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { kDebug(planDbg())<sizeHint(); QRect r = option.rect; r.setWidth( qMax( 100, r.width() ) ); editor->setGeometry(r); } //------------------------------- DurationSpinBoxDelegate::DurationSpinBoxDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *DurationSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { DurationSpinBox *editor = new DurationSpinBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void DurationSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { DurationSpinBox *dsb = static_cast(editor); // dsb->setScales( index.model()->data( index, Role::DurationScales ) ); dsb->setMinimumUnit( (Duration::Unit)(index.data( Role::Minimum ).toInt()) ); dsb->setMaximumUnit( (Duration::Unit)(index.data( Role::Maximum ).toInt()) ); dsb->setUnit( (Duration::Unit)( index.model()->data( index, Role::DurationUnit ).toInt() ) ); dsb->setValue( index.model()->data( index, Qt::EditRole ).toDouble() ); } void DurationSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { DurationSpinBox *dsb = static_cast(editor); QVariantList lst; lst << QVariant( dsb->value() ) << QVariant( (int)( dsb->unit() ) ); model->setData( index, QVariant( lst ), Qt::EditRole ); } void DurationSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { kDebug(planDbg())<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() + 50); editor->setGeometry(r); } //--------------------------- SpinBoxDelegate::SpinBoxDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QSpinBox *editor = new QSpinBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { int value = index.model()->data(index, Qt::EditRole).toInt(); int min = index.model()->data(index, Role::Minimum).toInt(); int max = index.model()->data(index, Role::Maximum).toInt(); QSpinBox *box = static_cast(editor); box->setRange( min, max ); box->setValue( value ); } void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSpinBox *box = static_cast(editor); model->setData( index, box->value(), Qt::EditRole ); } void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { kDebug(planDbg())<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() + 50); editor->setGeometry(r); } //--------------------------- DoubleSpinBoxDelegate::DoubleSpinBoxDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *DoubleSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QDoubleSpinBox *editor = new QDoubleSpinBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void DoubleSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { double value = index.model()->data(index, Qt::EditRole).toDouble(); double min = 0.0;//index.model()->data(index, Role::Minimum).toInt(); double max = 24.0;//index.model()->data(index, Role::Maximum).toInt(); QDoubleSpinBox *box = static_cast(editor); box->setDecimals( 1 ); box->setRange( min, max ); box->setValue( value ); box->selectAll(); } void DoubleSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QDoubleSpinBox *box = static_cast(editor); model->setData( index, box->value(), Qt::EditRole ); } void DoubleSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } //--------------------------- MoneyDelegate::MoneyDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *MoneyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { KLineEdit *editor = new KLineEdit(parent); //TODO: validator editor->installEventFilter(const_cast(this)); return editor; } void MoneyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QString value = index.model()->data(index, Qt::EditRole).toString(); KLineEdit *e = static_cast(editor); e->setText( value ); } void MoneyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { KLineEdit *e = static_cast(editor); model->setData( index, e->text(), Qt::EditRole ); } void MoneyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } //--------------------------- TimeDelegate::TimeDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *TimeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QTimeEdit *editor = new QTimeEdit(parent); editor->installEventFilter(const_cast(this)); return editor; } void TimeDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QTime value = index.model()->data(index, Qt::EditRole).toTime(); QTimeEdit *e = static_cast(editor); e->setMinimumTime( index.model()->data( index, Role::Minimum ).toTime() ); e->setMaximumTime( index.model()->data( index, Role::Maximum ).toTime() ); e->setTime( value ); } void TimeDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QTimeEdit *e = static_cast(editor); model->setData( index, e->time(), Qt::EditRole ); } void TimeDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } //-------------------------- ItemModelBase::ItemModelBase( QObject *parent ) : QAbstractItemModel( parent ), m_project(0), m_manager( 0 ), m_readWrite( false )//part->isReadWrite() ) { } ItemModelBase::~ItemModelBase() { } void ItemModelBase::setProject( Project *project ) { m_project = project; } void ItemModelBase::setScheduleManager( ScheduleManager *sm ) { m_manager = sm; } void ItemModelBase::slotLayoutChanged() { kDebug(planDbg()); emit layoutChanged(); } void ItemModelBase::slotLayoutToBeChanged() { kDebug(planDbg()); emit layoutAboutToBeChanged(); } bool ItemModelBase::dropAllowed( const QModelIndex &index, int, const QMimeData *data ) { if ( flags( index ) & Qt::ItemIsDropEnabled ) { foreach ( const QString &s, data->formats() ) { if ( mimeTypes().contains( s ) ) { return true; } } } return false; } QVariant ItemModelBase::data( const QModelIndex &index, int role ) const { if ( index.isValid() && role == Role::ColumnTag ) { return columnMap().key( index.column() ); } return QVariant(); } QVariant ItemModelBase::headerData( int section, Qt::Orientation orientation, int role ) const { Q_UNUSED(orientation); if ( role == Role::ColumnTag ) { return columnMap().key( section ); } return QVariant(); } bool ItemModelBase::setData( const QModelIndex &index, const QVariant &value, int role ) { Q_UNUSED(index); if ( role == Role::ReadWrite ) { setReadWrite( value.toBool() ); return true; } return false; } } //namespace KPlato #include "kptitemmodelbase.moc" diff --git a/plan/plugins/schedulers/rcps/KPlatoRCPSScheduler.cpp b/plan/plugins/schedulers/rcps/KPlatoRCPSScheduler.cpp index f4472b438f0..c19f4ae6783 100644 --- a/plan/plugins/schedulers/rcps/KPlatoRCPSScheduler.cpp +++ b/plan/plugins/schedulers/rcps/KPlatoRCPSScheduler.cpp @@ -1,1390 +1,1390 @@ /* This file is part of the KDE project * Copyright (C) 2009, 2010, 2012 Dag Andersen * * 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 "KPlatoRCPSScheduler.h" #include "kptproject.h" #include "kptschedule.h" #include "kptresource.h" #include "kpttask.h" #include "kptrelation.h" #include "kptdebug.h" #include #include #include #include #include #include #include #define GENERATION_MIN_LIMIT 5000 #define PROGRESS_CALLBACK_FREQUENCY 100 #define PROGRESS_MAX_VALUE 120000 #define PROGRESS_INIT_VALUE 12000 #define PROGRESS_INIT_STEP 2000 /* low weight == late, high weight == early */ #define WEIGHT_ASAP 50 #define WEIGHT_ALAP 1 #define WEIGHT_CONSTRAINT 1000 #define WEIGHT_FINISH 1000 #define GROUP_TARGETTIME 1 #define GROUP_CONSTRAINT 2 class ProgressInfo { public: explicit ProgressInfo() : init( true ), base( 0 ), progress( 0 ) { fitness.group = 0; fitness.weight = 0; } bool init; int base; int progress; struct rcps_fitness fitness; }; KPlatoRCPSScheduler::KPlatoRCPSScheduler( Project *project, ScheduleManager *sm, ulong granularity, QObject *parent ) : SchedulerThread( project, sm, parent ), result( -1 ), m_schedule( 0 ), m_recalculate( false ), m_usePert( false ), m_backward( false ), m_problem( 0 ), m_timeunit( granularity / 1000 ), m_offsetFromTime_t( 0 ), m_progressinfo( new ProgressInfo() ) { connect(this, SIGNAL(sigCalculationStarted( Project*, ScheduleManager*)), project, SIGNAL(sigCalculationStarted( Project*, ScheduleManager*))); emit sigCalculationStarted( project, sm ); connect( this, SIGNAL( sigCalculationFinished( Project*, ScheduleManager* ) ), project, SIGNAL( sigCalculationFinished( Project*, ScheduleManager* ) ) ); } KPlatoRCPSScheduler::~KPlatoRCPSScheduler() { delete m_progressinfo; qDeleteAll( m_duration_info_list ); qDeleteAll( m_weight_info_list ); rcps_problem_free( m_problem ); } KLocale *KPlatoRCPSScheduler::locale() const { return KGlobal::locale(); } int KPlatoRCPSScheduler::progress_callback( int generations, struct rcps_fitness fitness, void *arg ) { if ( arg == 0 ) { return -1; } KPlatoRCPSScheduler *self = static_cast( arg ); //kDebug(planDbg())<<"KPlatoRCPSScheduler::progress_callback"<progress( generations, fitness ); } int KPlatoRCPSScheduler::progress( int generations, struct rcps_fitness fitness ) { if ( m_haltScheduling ) { kDebug(planDbg())<<"KPlatoRCPSScheduler::progress:"<<"halt"; return -1; } if ( m_stopScheduling ) { m_schedule->logWarning( i18n( "Scheduling halted after %1 generations", generations ), 1 ); kDebug(planDbg())<<"KPlatoRCPSScheduler::progress:"<<"stop"; return -1; } // std::cout << "Progress after: " << generations << " generations\n"; if ( m_progressinfo->init ) { if ( generations == 0 ) { m_progressinfo->progress += PROGRESS_INIT_STEP; } else { m_progressinfo->progress = PROGRESS_INIT_VALUE; m_progressinfo->init = false; // std::cout << "Population generated: "<< generations << "\n"; } } else { m_progressinfo->progress = PROGRESS_INIT_VALUE + generations; } // detect change in fitness if ( rcps_fitness_cmp( &m_progressinfo->fitness, &fitness ) != 0 ) { // std::cout << "Fitness changed in generation: " << generations << " group=["<fitness.group<<"->"<fitness.weight<<"->"<fitness = fitness; m_progressinfo->base = generations; } m_manager->setProgress( m_progressinfo->progress ); setProgress( m_progressinfo->progress ); // stop if fitness does not change in GENERATION_MIN_LIMIT generations /* int result = ( generations >= m_progressinfo->base + GENERATION_MIN_LIMIT ? 1 : 0 ); if ( result ) { //kDebug(planDbg())<<"KPlatoRCPSScheduler::progress, stop after"<progress; m_schedule->logDebug( QString( "Acceptable solution found after %1 generations" ).arg( generations ), 1 ); std::cout << "Acceptable solution found after " << generations << " generations\n"; }*/ return 0; } int KPlatoRCPSScheduler::duration_callback( int direction, int time, int nominal_duration, void *arg ) { //kDebug(planDbg())<<"kplato_duration:"<( arg ); return info->self->duration( direction, time, nominal_duration, info ); } int KPlatoRCPSScheduler::duration( int direction, int time, int nominal_duration, KPlatoRCPSScheduler::duration_info *info ) { if ( m_haltScheduling || m_manager == 0 ) { return nominal_duration; } ++(info->calls); if ( info->cache.contains( QPair( time, direction ) ) ) { return info->cache[ QPair( time, direction ) ]; } if ( m_manager->recalculate() && info->task->completion().isFinished() ) { return 0; } int dur = 0; if ( info->task->constraint() == Node::FixedInterval ) { // duration may depend on daylight saving so we need to calculate // NOTE: dur may not be correct if time != info->task->constraintStartTime, let's see what happends... dur = ( info->task->constraintEndTime() - info->task->constraintStartTime() ).seconds() / m_timeunit; info->task->schedule()->logDebug( QString( "Fixed interval: Time=%1, duration=%2 ( %3, %4 )" ).arg( time ).arg( dur ).arg( fromRcpsTime( time ).toString() ).arg( Duration( (qint64)(dur) * m_timeunit * 1000 ).toDouble( Duration::Unit_h ) ) ); } else if ( info->estimatetype == Estimate::Type_Effort ) { if ( info->requests.isEmpty() ) { dur = info->estimate.seconds() / m_timeunit; } else { dur = info->task->requests().duration( info->requests, fromRcpsTime( time ), info->estimate, 0, /*no schedule*/ m_backward ? ! direction : direction ).seconds() / m_timeunit; //kDebug(planDbg())<task->name()<< QString( "duration_callback effort: backward=%5, direction=%6 (direction=%7); Time=%1, duration=%2 ( %3, %4 )" ).arg( time ).arg( dur ).arg( fromRcpsTime( time ).toString() ).arg( Duration( (qint64)(dur) * m_timeunit * 1000 ).toDouble( Duration::Unit_h ) ).arg( m_backward ).arg( direction ).arg( m_backward ? !direction : direction ); } } else { dur = info->task->length( fromRcpsTime( time ), info->estimate, 0, /*no schedule*/ m_backward ? ! direction : direction ).seconds() / m_timeunit; } info->cache[ QPair( time, direction ) ] = dur; info->task->schedule()->logDebug( QString( "duration_callback: Time=%1, duration=%2 ( %3, %4 )" ).arg( time ).arg( dur ).arg( fromRcpsTime( time ).toString() ).arg( Duration( (qint64)(dur) * m_timeunit * 1000 ).toDouble( Duration::Unit_h ) ) ); return dur; } int KPlatoRCPSScheduler::weight_callback( int time, int duration, struct rcps_fitness *nominal_weight, void* weight_arg, void* fitness_arg ) { //kDebug(planDbg())<<"kplato_weight:"<weight *= time; return 0; } KPlatoRCPSScheduler::weight_info *winfo = static_cast( weight_arg ); KPlatoRCPSScheduler::fitness_info *finfo = static_cast( fitness_arg ); return winfo->self->weight( time, duration, nominal_weight, winfo, finfo ); } void *KPlatoRCPSScheduler::fitness_callback_init( void *arg ) { Q_ASSERT( arg ); KPlatoRCPSScheduler::fitness_info *info = static_cast( arg ); Q_ASSERT( info ); fitness_info *finfo = new fitness_info; finfo->self = info->self; // kDebug(planDbg())<self; return finfo; } int KPlatoRCPSScheduler::fitness_callback_result( struct rcps_fitness *fit, void *arg ) { KPlatoRCPSScheduler::fitness_info *info = static_cast( arg ); info->self->fitness( fit, info ); delete info; return 0; } int KPlatoRCPSScheduler::fitness( struct rcps_fitness *fit, KPlatoRCPSScheduler::fitness_info *info ) { /* std::cout << ">-------------------------------------------\n"; std::cout << "Sequence: "; foreach ( Task *t, info->jobs ) { std::cout << (t ? t->name().toLocal8Bit().data() : "End") << ", "; } std::cout << "\n"; kDebug(planDbg())<map;*/ QMultiMap >::const_iterator it = info->map.constFind( GROUP_CONSTRAINT ); if ( it != info->map.constEnd() ) { // constraint fit->group = GROUP_CONSTRAINT; for ( ; it.key() == GROUP_CONSTRAINT && it != info->map.constEnd(); ++it ) { fit->weight += it.value().first; QString s = it.value().second ? it.value().second->name() : "End node"; // std::cout << s.toLocal8Bit().data() << ": group=" << it.key() << " weight=" << it.value().first << "\n"; // m_schedule->logDebug( QString( "%3: %1 %2" ).arg( it.key() ).arg( it.value().first ).arg( it.value().second->name() ) ); } // std::cout << "Result: group= " << fit->group << " weight=" << fit->weight << "\n--------------------------\n"; return 0; } it = info->map.constFind( GROUP_TARGETTIME ); if ( it != info->map.constEnd() ) { // missed target time fit->group = GROUP_TARGETTIME; for ( ; it.key() == GROUP_TARGETTIME && it != info->map.constEnd(); ++it ) { fit->weight += it.value().first; QString s = it.value().second ? it.value().second->name() : "End node"; // std::cout << s.toLocal8Bit().data() << ": group=" << it.key() << " weight=" << it.value().first << "\n"; // m_schedule->logDebug( QString( "%3: %1 %2" ).arg( it.key() ).arg( it.value().first ).arg( it.value().second->name() ) ); } // std::cout << "Result: group= " << fit->group << " weight=" << fit->weight << "\n--------------------------\n"; return 0; } fit->group = 0; for ( it = info->map.constBegin(); it != info->map.constEnd(); ++it ) { fit->weight += it.value().first; QString s = it.value().second ? it.value().second->name() : "End node"; // std::cout << s.toLocal8Bit().data() << ": group=" << it.key() << " weight=" << it.value().first << "\n"; // m_schedule->logDebug( QString( "%3: %1 %2" ).arg( it.key() ).arg( it.value().first ).arg( it.value().second->name() ) ); } // std::cout << "Result: group= " << fit->group << " weight=" << fit->weight << "\n--------------------------\n"; return 0; } int KPlatoRCPSScheduler::weight( int time, int duration, struct rcps_fitness *nominal_weight, KPlatoRCPSScheduler::weight_info* info, KPlatoRCPSScheduler::fitness_info* finfo ) { if ( m_haltScheduling || m_manager == 0 ) { return 0; } if ( m_manager->recalculate() && info->task->completion().isFinished() ) { return 0; } struct rcps_fitness &f = *nominal_weight; f.group = 0; f.weight = time; if ( info->isEndJob ) { if ( info->finish == 0 ) { info->finish = time; -/* const char *s = QString( "First : %1 %2 %3 End job" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).toAscii(); +/* const char *s = QString( "First : %1 %2 %3 End job" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).toLatin1(); std::cout<finish / ( time > 0 ? time : 1 ); if ( time > info->targettime ) { w = w + ( WEIGHT_CONSTRAINT * ( time - info->targettime ) ); }*/ if ( time > info->targettime ) { f.group = GROUP_TARGETTIME; f.weight = time - info->targettime; } -/* const char *s = QString( "End job: %1 %2 %3 End job target: %4" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).arg( info->targettime ).toAscii(); +/* const char *s = QString( "End job: %1 %2 %3 End job target: %4" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).arg( info->targettime ).toLatin1(); std::cout<task->constraint() ) { case Node::FinishNotLater: if ( info->targettime > time ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * ( info->targettime - time ); } break; case Node::MustFinishOn: if ( info->targettime != time ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * abs( info->targettime - time ); } break; case Node::StartNotEarlier: if ( info->targettime < time ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * ( time - info->targettime ); } break; case Node::MustStartOn: case Node::FixedInterval: if ( info->targettime != time ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * abs( info->targettime - time ); } break; default: break; } -/* const char *s = QString( "Backwrd: %1 %2 %3 %4 (target: %5)" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).arg( info->task->name() ).arg( info->targettime ).toAscii(); +/* const char *s = QString( "Backwrd: %1 %2 %3 %4 (target: %5)" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).arg( info->task->name() ).arg( info->targettime ).toLatin1(); std::cout<task->constraint() ) { case Node::StartNotEarlier: if ( time < info->targettime ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * ( info->targettime - time ); } break; case Node::MustStartOn: case Node::FixedInterval: if ( info->targettime != time ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * ( abs( info->targettime - time ) ); } break; case Node::FinishNotLater: // std::cout << "FNL " << info->task->name().toLocal8Bit().data() << ": end="< info->targettime ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * ( time - info->targettime ); } // std::cout << info->task->name().toLocal8Bit().data() << ": group=" << f.group << " weight=" << f.weight << "\n"; break; case Node::MustFinishOn: // std::cout << "MSO " << info->task->name().toLocal8Bit().data() << ": end="<targettime != time + duration ) { f.group = GROUP_CONSTRAINT; f.weight = WEIGHT_CONSTRAINT * abs( info->targettime - time ); } // std::cout << info->task->name().toLocal8Bit().data() << ": group=" << f.group << " weight=" << f.weight << "\n"; break; default: break; } -/* const char *s = QString( "Forward: %1 %2 %3 %4 (target: %5)" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).arg( info->task->name() ).arg( info->targettime ).toAscii(); +/* const char *s = QString( "Forward: %1 %2 %3 %4 (target: %5)" ).arg( time, 10 ).arg( duration, 10 ).arg( w, 10 ).arg( info->task->name() ).arg( info->targettime ).toLatin1(); std::cout<task ? info->task->name() : "End node"; if ( finfo ) { finfo->map.insert( f.group, QPair( f.weight, info->task ) ); finfo->jobs << info->task; // kDebug(planDbg())<map; }// else kDebug(planDbg())< m_projectMutex.lock(); m_managerMutex.lock(); m_project = new Project(); loadProject( m_project, m_pdoc ); m_project->setName( "Schedule: " + m_project->name() ); //Debug m_project->stopcalculation = false; m_manager = m_project->scheduleManager( m_mainmanagerId ); Q_CHECK_PTR( m_manager ); Q_ASSERT( m_manager->expected() ); Q_ASSERT( m_manager != m_mainmanager ); Q_ASSERT( m_manager->scheduleId() == m_mainmanager->scheduleId() ); Q_ASSERT( m_manager->expected() != m_mainmanager->expected() ); m_manager->setName( "Schedule: " + m_manager->name() ); //Debug m_schedule = m_manager->expected(); connect(m_manager, SIGNAL(sigLogAdded(Schedule::Log)), this, SLOT(slotAddLog(Schedule::Log))); m_project->initiateCalculation( *m_schedule ); m_project->initiateCalculationLists( *m_schedule ); m_problem = rcps_problem_new(); rcps_problem_setfitness_mode( m_problem, FITNESS_WEIGHT ); m_usePert = m_manager->usePert(); m_recalculate = m_manager->recalculate(); if ( m_recalculate ) { m_starttime = m_manager->recalculateFrom(); m_backward = false; } else { m_backward = m_manager->schedulingDirection(); m_starttime = m_backward ? m_project->constraintEndTime() : m_project->constraintStartTime(); } m_targettime = m_backward ? m_project->constraintStartTime() : m_project->constraintEndTime(); m_project->setCurrentSchedule( m_manager->expected()->id() ); m_schedule->setPhaseName( 0, i18n( "Init" ) ); if ( ! m_backward && locale() ) { m_schedule->logDebug( QString( "Schedule project using RCPS Scheduler, starting at %1, granularity %2 sec" ).arg( QDateTime::currentDateTime().toString() ).arg( m_timeunit ), 0 ); if ( m_recalculate ) { m_schedule->logInfo( i18n( "Re-calculate project from start time: %1", locale()->formatDateTime( m_starttime ) ), 0 ); } else { m_schedule->logInfo( i18n( "Schedule project from start time: %1", locale()->formatDateTime( m_starttime ) ), 0 ); } } if ( m_backward && locale() ) { m_schedule->logDebug( QString( "Schedule project backward using RCPS Scheduler, starting at %1, granularity %2 sec" ).arg( locale()->formatDateTime( QDateTime::currentDateTime() ) ).arg( m_timeunit ), 0 ); m_schedule->logInfo( i18n( "Schedule project from end time: %1", locale()->formatDateTime( m_starttime ) ), 0 ); } m_managerMutex.unlock(); m_projectMutex.unlock(); } // <--- mutex m_progressinfo->progress += PROGRESS_INIT_STEP / 5; setProgress( m_progressinfo->progress ); result = kplatoToRCPS(); if ( result != 0 ) { if ( locale() ) { m_schedule->logError( i18n( "Failed to build a valid RCPS project" ) ); } setProgress( PROGRESS_MAX_VALUE ); return; } m_schedule->setPhaseName( 1, i18n( "Schedule" ) ); setMaxProgress( PROGRESS_MAX_VALUE ); solve(); if ( m_haltScheduling ) { deleteLater(); return; } if ( result != 0 ) { m_schedule->logError( i18n( "Invalid scheduling solution. Result: %1", result ), 1 ); } kplatoFromRCPS(); setProgress( PROGRESS_MAX_VALUE ); } int KPlatoRCPSScheduler::check() { return rcps_check( m_problem ); } void KPlatoRCPSScheduler::solve() { kDebug(planDbg())<<"KPlatoRCPSScheduler::solve()"; struct rcps_solver *s = rcps_solver_new(); rcps_solver_set_progress_callback(s, PROGRESS_CALLBACK_FREQUENCY, this, &KPlatoRCPSScheduler::progress_callback); rcps_solver_set_duration_callback(s, &KPlatoRCPSScheduler::duration_callback ); rcps_problem_set_weight_callback( m_problem, &KPlatoRCPSScheduler::weight_callback ); fitness_init_arg.self = this; rcps_problem_set_fitness_callback( m_problem, &KPlatoRCPSScheduler::fitness_callback_init, &fitness_init_arg, &KPlatoRCPSScheduler::fitness_callback_result ); Q_ASSERT( check() == 0 ); rcps_solver_setparam( s, SOLVER_PARAM_POPSIZE, 1000 ); rcps_solver_solve( s, m_problem ); result = rcps_solver_getwarnings( s ); rcps_solver_free( s ); } int KPlatoRCPSScheduler::kplatoToRCPS() { addResources(); addTasks(); addDependencies(); addRequests(); setWeights(); setConstraints(); int r = check(); return r; } void KPlatoRCPSScheduler::taskFromRCPSForward( struct rcps_job *job, Task *task, QMap > &resourcemap ) { if ( m_haltScheduling || m_manager == 0 ) { return; } Schedule *cs = task->currentSchedule(); Q_ASSERT( cs ); struct rcps_mode *mode = rcps_mode_get(job, rcps_job_getmode_res(job)); /* get the duration of this mode */ KPlatoRCPSScheduler::duration_info *info = static_cast( rcps_mode_get_cbarg(mode) ); qint64 dur = 0; qint64 st = rcps_job_getstart_res(job); if ( info == 0 ) { dur = rcps_mode_getduration(mode); } else { cs->logDebug( QString( "Task '%1' estimate: %2" ).arg( task->name() ).arg( task->estimate()->value( Estimate::Use_Expected, false ).toString() ), 1 ); cs->logDebug( QString( "Task '%1' duration called %2 times, cached values: %3" ).arg( rcps_job_getname(job) ).arg( info->calls ).arg( info->cache.count() ) ); dur = duration_callback( 0, st, rcps_mode_getduration(mode), info ); for ( QMap, int>::ConstIterator it = info->cache.constBegin(); it != info->cache.constEnd(); ++it ) { cs->logDebug( QString( "Task '%1' start: %2, duration: %3 (%4, %5 hours)" ).arg( rcps_job_getname(job) ).arg( it.key().first ).arg( it.value() ).arg( fromRcpsTime( it.key().first ).toString() ).arg( (double)(it.value())/60.0 ), 1 ); } } DateTime start = m_starttime.addSecs(st * m_timeunit); DateTime end = start + Duration( dur * m_timeunit * 1000 ); if ( locale() ) { cs->logDebug( QString( "Task '%1' start=%2, duration=%3: %4 - %5" ).arg( rcps_job_getname(job) ).arg( st ).arg( dur ).arg( locale()->formatDateTime( start ) ).arg( locale()->formatDateTime( end ) ), 1 ); } task->setStartTime( start ); task->setEndTime( end ); for ( int reqs = 0; reqs < rcps_request_count(mode); ++reqs ) { struct rcps_request *req = rcps_request_get(mode, reqs); struct rcps_alternative *alt = rcps_alternative_get(req, rcps_request_getalternative_res(req)); int amount = rcps_alternative_getamount(alt); struct rcps_resource *res = rcps_alternative_getresource(alt); cs->logDebug( QString( "Job %1: resource %2 is %3 available" ).arg( rcps_job_getname(job) ).arg( rcps_resource_getname(res) ).arg( amount ), 1 ); // do actual appoinments etc ResourceRequest *r = m_requestmap.value( req ); if ( r == 0 ) { cs->logWarning( i18n( "No resource request is registered" ), 1 ); continue; } resourcemap[ task ] << r; cs->logDebug( QString( "Make appointments to resource %1" ).arg( r->resource()->name() ), 1 ); r->makeAppointment( cs, amount ); } if ( m_recalculate ) { if ( task->completion().isFinished() ) { task->copySchedule(); if ( locale() && m_manager ) { cs->logDebug( QString( "Task is completed, copied schedule: %2 to %3" ).arg( task->name() ).arg( locale()->formatDateTime( task->startTime() ) ).arg( locale()->formatDateTime( task->endTime() ) ), 1 ); } } else if ( task->completion().isStarted() ) { task->copyAppointments( DateTime(), start ); if ( locale() && m_manager ) { cs->logDebug( QString( "Task is %4% completed, copied appointments from %2 to %3" ).arg( task->name() ).arg( locale()->formatDateTime( task->startTime() ) ).arg( locale()->formatDateTime( start ) ).arg( task->completion().percentFinished() ), 1 ); } } } cs->setScheduled( true ); if ( task->estimate()->type() == Estimate::Type_Effort ) { if ( task->appointmentStartTime().isValid() ) { task->setStartTime( task->appointmentStartTime() ); } if ( task->appointmentEndTime().isValid() ) { task->setEndTime( task->appointmentEndTime() ); } if ( info && info->requests.isEmpty() ) { cs->setResourceError( true ); cs->logError( i18n( "No resource has been allocated" ), 1 ); } } else if ( task->estimate()->calendar() ) { DateTime t = task->estimate()->calendar()->firstAvailableAfter( task->startTime(), task->endTime() ); if ( t.isValid() ) { task->setStartTime( t ); } t = task->estimate()->calendar()->firstAvailableBefore( task->endTime(), task->startTime() ); if ( t.isValid() ) { task->setEndTime( t ); } } //else Fixed duration task->setDuration( task->endTime() - task->startTime() ); if ( locale() ) { cs->logInfo( i18n( "Scheduled task to start at %1 and finish at %2", locale()->formatDateTime( task->startTime() ), locale()->formatDateTime( task->endTime() ) ), 1 ); } } void KPlatoRCPSScheduler::kplatoFromRCPS() { if ( m_backward ) { kplatoFromRCPSBackward(); } else { kplatoFromRCPSForward(); } } void KPlatoRCPSScheduler::kplatoFromRCPSForward() { //kDebug(planDbg())<<"KPlatoRCPSScheduler::kplatoFromRCPSForward:"; MainSchedule *cs = static_cast( m_project->currentSchedule() ); QMap > resourcemap; int count = rcps_job_count(m_problem); int step = ( PROGRESS_MAX_VALUE - m_progressinfo->progress ) / count; DateTime projectstart = m_starttime.addSecs( rcps_job_getstart_res(m_jobstart) * m_timeunit ); for ( int i = 0; i < count; ++i ) { m_progressinfo->progress += step; m_manager->setProgress( m_progressinfo->progress ); setProgress( m_progressinfo->progress ); struct rcps_job *job = rcps_job_get( m_problem, i ); Task *task = m_taskmap.value( job ); if ( task == 0 ) { continue; //might be dummy task for lag, ... } taskFromRCPSForward( job, task, resourcemap ); if ( projectstart > task->startTime() ) { projectstart = task->startTime(); } } qint64 st = rcps_job_getstart_res(m_jobstart) * m_timeunit; DateTime start = m_starttime.addSecs( st ); qint64 et = rcps_job_getstart_res(m_jobend) * m_timeunit; DateTime end = m_starttime.addSecs( et ); m_project->setStartTime( projectstart ); m_project->setEndTime( end ); adjustSummaryTasks( m_schedule->summaryTasks() ); calculatePertValues( resourcemap ); cs->logInfo( i18n( "Project scheduled to start at %1 and finish at %2", locale()->formatDateTime( projectstart ), locale()->formatDateTime( end ) ), 1 ); if ( m_manager ) { if ( locale() ) cs->logDebug( QString( "Project scheduling finished at %1" ).arg( QDateTime::currentDateTime().toString() ), 1 ); m_project->finishCalculation( *m_manager ); m_manager->scheduleChanged( cs ); } } void KPlatoRCPSScheduler::taskFromRCPSBackward( struct rcps_job *job, Task *task, QMap > &resourcemap ) { if ( m_haltScheduling || m_manager == 0 ) { return; } Schedule *cs = task->currentSchedule(); Q_ASSERT( cs ); struct rcps_mode *mode = rcps_mode_get( job, rcps_job_getmode_res( job ) ); /* get the duration of this mode */ KPlatoRCPSScheduler::duration_info *info = static_cast( rcps_mode_get_cbarg( mode ) ); qint64 dur = 0; qint64 st = rcps_job_getstart_res( job ); if ( info == 0 ) { dur = rcps_mode_getduration( mode ); } else { cs->logDebug( QString( "Task '%1' estimate: %2" ).arg( task->name() ).arg( task->estimate()->value( Estimate::Use_Expected, false ).toString() ), 1 ); cs->logDebug( QString( "Task '%1' duration called %2 times, cached values: %3" ).arg( rcps_job_getname( job ) ).arg( info->calls ).arg( info->cache.count() ) ); dur = duration_callback( 0, st, rcps_mode_getduration( mode ), info ); for ( QMap, int>::ConstIterator it = info->cache.constBegin(); it != info->cache.constEnd(); ++it ) { cs->logDebug( QString( "Task '%1' start: %2, duration: %3 (%4, %5 hours)" ).arg( rcps_job_getname(job) ).arg( it.key().first ).arg( it.value() ).arg( fromRcpsTime( it.key().first ).toString() ).arg( (double)(it.value())/60.0 ), 1 ); } } DateTime end = fromRcpsTime( st ); DateTime start = fromRcpsTime( st + dur ); if ( locale() ) { cs->logDebug( QString( "Task '%1' start=%2, duration=%3: %4 - %5" ).arg( rcps_job_getname(job) ).arg( st ).arg( dur ).arg( locale()->formatDateTime( start ) ).arg( locale()->formatDateTime( end ) ), 1 ); } task->setStartTime( start ); task->setEndTime( end ); for ( int reqs = 0; reqs < rcps_request_count( mode ); ++reqs ) { struct rcps_request *req = rcps_request_get( mode, reqs ); struct rcps_alternative *alt = rcps_alternative_get( req, rcps_request_getalternative_res( req ) ); int amount = rcps_alternative_getamount( alt ); struct rcps_resource *res = rcps_alternative_getresource( alt ); cs->logDebug( QString( "Job %1: resource %2 is %3 available" ).arg( rcps_job_getname( job ) ).arg( rcps_resource_getname( res ) ).arg( amount ), 1 ); // do actual appoinments etc ResourceRequest *r = m_requestmap.value( req ); if ( r == 0 ) { cs->logWarning( i18n( "No resource request is registered" ), 1 ); continue; } resourcemap[ task ] << r; cs->logDebug( QString( "Make appointments to resource %1" ).arg( r->resource()->name() ), 1 ); r->makeAppointment( cs, amount ); } if ( m_recalculate ) { if ( task->completion().isFinished() ) { task->copySchedule(); if ( locale() && m_manager ) { cs->logDebug( QString( "Task is completed, copied schedule: %2 to %3" ).arg( task->name() ).arg( locale()->formatDateTime( task->startTime() ) ).arg( locale()->formatDateTime( task->endTime() ) ), 1 ); } } else if ( task->completion().isStarted() ) { task->copyAppointments( DateTime(), start ); if ( locale() && m_manager ) { cs->logDebug( QString( "Task is %4% completed, copied appointments from %2 to %3" ).arg( task->name() ).arg( locale()->formatDateTime( task->startTime() ) ).arg( locale()->formatDateTime( start ) ).arg( task->completion().percentFinished() ), 1 ); } } } cs->setScheduled( true ); if ( task->estimate()->type() == Estimate::Type_Effort ) { if ( task->appointmentStartTime().isValid() ) { task->setStartTime( task->appointmentStartTime() ); } if ( task->appointmentEndTime().isValid() ) { task->setEndTime( task->appointmentEndTime() ); } if ( info && info->requests.isEmpty() ) { cs->setResourceError( true ); cs->logError( i18n( "No resource has been allocated" ), 1 ); } } else if ( task->estimate()->calendar() ) { DateTime t = task->estimate()->calendar()->firstAvailableAfter( task->startTime(), task->endTime() ); if ( t.isValid() ) { task->setStartTime( t ); } t = task->estimate()->calendar()->firstAvailableBefore( task->endTime(), task->startTime() ); if ( t.isValid() ) { task->setEndTime( t ); } } //else Fixed duration task->setDuration( task->endTime() - task->startTime() ); if ( locale() ) { cs->logInfo( i18n( "Scheduled task to start at %1 and finish at %2", locale()->formatDateTime( task->startTime() ), locale()->formatDateTime( task->endTime() ) ), 1 ); } } void KPlatoRCPSScheduler::kplatoFromRCPSBackward() { //kDebug(planDbg())<<"KPlatoRCPSScheduler::kplatoFromRCPSBackward:"; MainSchedule *cs = static_cast( m_project->currentSchedule() ); QMap > resourcemap; int count = rcps_job_count( m_problem ); int step = ( PROGRESS_MAX_VALUE - m_progressinfo->progress ) / count; DateTime projectstart = fromRcpsTime( rcps_job_getstart_res( m_jobend ) ); for ( int i = 0; i < count; ++i ) { m_progressinfo->progress += step; m_manager->setProgress( m_progressinfo->progress ); setProgress( m_progressinfo->progress ); struct rcps_job *job = rcps_job_get( m_problem, i ); Task *task = m_taskmap.value( job ); if ( task == 0 ) { continue; //might be dummy task for lag, ... } taskFromRCPSBackward( job, task, resourcemap ); if ( projectstart > task->startTime() ) { projectstart = task->startTime(); } } DateTime end = fromRcpsTime( rcps_job_getstart_res( m_jobstart ) ); m_project->setStartTime( projectstart ); m_project->setEndTime( end ); cs->logInfo( i18n( "Project scheduled to start at %1 and finish at %2", locale()->formatDateTime( projectstart ), locale()->formatDateTime( end ) ), 1 ); if ( projectstart < m_project->constraintStartTime() ) { cs->setConstraintError( true ); cs->logError( i18n( "Must start project early in order to finish in time: %1", locale()->formatDateTime( m_project->constraintStartTime() ) ), 1 ); } adjustSummaryTasks( m_schedule->summaryTasks() ); calculatePertValues( resourcemap ); if ( m_manager ) { if ( locale() ) cs->logDebug( QString( "Project scheduling finished at %1" ).arg( QDateTime::currentDateTime().toString() ), 1 ); m_project->finishCalculation( *m_manager ); m_manager->scheduleChanged( cs ); } } void KPlatoRCPSScheduler::adjustSummaryTasks( const QList &nodes ) { foreach ( Node *n, nodes ) { adjustSummaryTasks( n->childNodeIterator() ); if ( n->parentNode()->type() == Node::Type_Summarytask ) { DateTime pt = n->parentNode()->startTime(); DateTime nt = n->startTime(); if ( ! pt.isValid() || pt > nt ) { n->parentNode()->setStartTime( nt ); } pt = n->parentNode()->endTime(); nt = n->endTime(); if ( ! pt.isValid() || pt < nt ) { n->parentNode()->setEndTime( nt ); } } } } void KPlatoRCPSScheduler::calculatePertValues( const QMap > &map ) { if ( m_manager ) { m_schedule->setPhaseName( 2, i18nc( "Project Evaluation and Review Technique", "PERT" ) ); } foreach ( Node *n, m_project->allNodes() ) { if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) { continue; } Task *t = static_cast( n ); if ( n->isStartNode() ) { (void)calculateLateStuff( map, static_cast( n ) ); } if ( n->isEndNode() ) { (void)calculateEarlyStuff( map, static_cast( n ) ); } switch ( n->constraint() ) { case Node::StartNotEarlier: n->schedule()->setNegativeFloat( n->startTime() < n->constraintStartTime() ? n->startTime() - n->constraintStartTime() : Duration::zeroDuration ); break; case Node::MustStartOn: case Node::FixedInterval: n->schedule()->setNegativeFloat( n->startTime() > n->constraintStartTime() ? n->startTime() - n->constraintStartTime() : n->constraintStartTime() - n->startTime() ); break; case Node::FinishNotLater: n->schedule()->setNegativeFloat( n->endTime() > n->constraintEndTime() ? n->endTime() - n->constraintEndTime() : Duration::zeroDuration ); break; case Node::MustFinishOn: n->schedule()->setNegativeFloat( n->endTime() > n->constraintEndTime() ? n->endTime() - n->constraintEndTime() : n->constraintEndTime() - n->endTime() ); break; default: break; } if ( t->negativeFloat() != 0 ) { n->schedule()->setConstraintError( true ); n->schedule()->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", n->constraintToString( true ), locale()->formatDuration( t->negativeFloat().milliseconds() ) ) ); } } } Duration KPlatoRCPSScheduler::calculateLateStuff( const QMap > &map, Task *task ) { Schedule *cs = task->currentSchedule(); Duration pf = m_project->endTime() - m_project->startTime(); QList lst = task->dependChildNodes() + task->childProxyRelations(); if ( lst.isEmpty() ) { // end node DateTime end = task->endTime(); if ( task->estimate()->type() == Estimate::Type_Effort ) { foreach ( ResourceRequest *r, map.value( static_cast( task ) ) ) { DateTime x = r->resource()->availableBefore( m_project->endTime(), task->endTime(), 0 ); cs->logDebug( QString( "Resource '%1' available before %2: %3" ).arg( r->resource()->name() ).arg( m_project->endTime().toString() ).arg( x.toString() ), 2 ); if ( x.isValid() && x > end ) { end = x; } } } else if ( task->estimate()->calendar() ) { end = task->estimate()->calendar()->firstAvailableBefore( m_project->endTime(), task->endTime() ); cs->logDebug( QString( "Calendar work time before %1: %2" ).arg( m_project->endTime().toString() ).arg( end.toString() ), 2 ); } // TODO must calculate backwards to get late *start* of task pf = end.isValid() ? end - task->endTime() : m_project->endTime() - task->endTime(); task->setFreeFloat( pf ); } else { Duration prev = pf; Duration x = pf; foreach ( Relation *r, lst ) { prev = qMin( prev, calculateLateStuff( map, static_cast( r->child() ) ) ); DateTime end = task->endTime(); if ( task->estimate()->type() == Estimate::Type_Effort ) { foreach ( ResourceRequest *req, map.value( static_cast( task ) ) ) { DateTime y = req->resource()->availableBefore( r->child()->startTime(), task->endTime(), 0 ); cs->logDebug( QString( "Resource '%1' available before %2: %3" ).arg( req->resource()->name() ).arg( r->child()->startTime().toString() ).arg( y.toString() ), 2 ); if ( y.isValid() && y > end ) { end = y; } } } else if ( task->estimate()->calendar() ) { end = task->estimate()->calendar()->firstAvailableBefore( r->child()->startTime(), task->endTime() ); cs->logDebug( QString( "Calendar work time before %1: %2" ).arg( r->child()->startTime().toString() ).arg( end.toString() ), 2 ); } x = qMin( x, end.isValid() ? end - task->endTime() : r->child()->startTime() - task->endTime() ); } task->setFreeFloat( x ); // TODO must calculate backwards to get late *start* of task pf = prev + x; } task->setPositiveFloat( pf ); task->setLateFinish( task->endTime() + pf ); task->setLateStart( task->lateFinish() - ( task->endTime() - task->startTime() ) ); if ( locale() ) { cs->logDebug( QString( "Late start %1, late finish %2, positive float %3" ).arg( locale()->formatDateTime( task->lateStart() ) ).arg( locale()->formatDateTime( task->lateFinish() ) ).arg( pf.toString() ), 2 ); } return pf; } Duration KPlatoRCPSScheduler::calculateEarlyStuff( const QMap > &map, Task *task ) { Schedule *cs = task->currentSchedule(); Duration tot = m_project->endTime() - m_project->startTime(); QList lst = task->dependParentNodes() + task->parentProxyRelations(); if ( lst.isEmpty() ) { // start node DateTime earlystart = task->startTime(); if ( task->estimate()->type() == Estimate::Type_Effort ) { foreach ( ResourceRequest *r, map.value( static_cast( task ) ) ) { DateTime x = r->resource()->availableAfter( m_project->startTime(), task->startTime(), 0 ); cs->logDebug( QString( "Resource '%1' available after %2 (%4): %3" ).arg( r->resource()->name() ).arg( m_project->startTime().toString() ).arg( x.toString() ).arg( task->startTime().toString() ), 2 ); if ( x.isValid() && x < earlystart ) { earlystart = x; } } } else if ( task->estimate()->calendar() ) { earlystart = task->estimate()->calendar()->firstAvailableAfter( m_project->startTime(), task->startTime() ); cs->logDebug( QString( "Calendar work time after %1: %2" ).arg( m_project->startTime().toString() ).arg( earlystart.toString() ), 2 ); } // TODO must calculate forward to get early *????* of task tot = earlystart.isValid() ? task->startTime() - earlystart : task->startTime() - m_project->startTime(); } else { Duration prev = tot; Duration x = tot; foreach ( Relation *r, lst ) { prev = qMin( prev, calculateEarlyStuff( map, static_cast( r->parent() ) ) ); DateTime earlystart = task->startTime(); if ( task->estimate()->type() == Estimate::Type_Effort ) { foreach ( ResourceRequest *req, map.value( static_cast( task ) ) ) { DateTime y = req->resource()->availableAfter( r->parent()->endTime(), task->startTime(), 0 ); cs->logDebug( QString( "Resource '%1' available after %2: %3" ).arg( req->resource()->name() ).arg( r->parent()->endTime().toString() ).arg( y.toString() ), 2 ); if ( y.isValid() && y < earlystart ) { earlystart = y; } } } else if ( task->estimate()->calendar() ) { earlystart = task->estimate()->calendar()->firstAvailableAfter( r->parent()->endTime(), task->startTime() ); cs->logDebug( QString( "Calendar work time after %1: %2" ).arg( r->parent()->endTime().toString() ).arg( earlystart.toString() ), 2 ); } x = qMin( x, earlystart.isValid() ? task->startTime() - earlystart : task->startTime() - r->parent()->startTime() ); } // TODO must calculate backwards to get late *start* of task tot = prev + x; } task->setEarlyStart( task->startTime() - tot ); task->setEarlyFinish( task->earlyStart() + ( task->endTime() - task->startTime() ) ); if ( locale() ) { cs->logDebug( QString( "Early start %1, early finish %2" ).arg( locale()->formatDateTime( task->earlyStart() ) ).arg( locale()->formatDateTime( task->earlyFinish() ) ), 2 ); } return tot; } struct rcps_resource *KPlatoRCPSScheduler::addResource( KPlato::Resource *r) { if ( m_resourcemap.values().contains( r ) ) { kWarning()<name()<<"already exist"; return 0; } struct rcps_resource *res = rcps_resource_new(); rcps_resource_setname( res, r->name().toLocal8Bit().data() ); rcps_resource_setavail( res, r->units() ); rcps_resource_add( m_problem, res ); m_resourcemap[res] = r; return res; } void KPlatoRCPSScheduler::addResources() { kDebug(planDbg()); QList list = m_project->resourceList(); for (int i = 0; i < list.count(); ++i) { addResource( list.at(i) ); } } int KPlatoRCPSScheduler::toRcpsTime( const DateTime &time ) const { return ( m_backward ? time.secsTo( m_starttime ) : m_starttime.secsTo( time ) ) / m_timeunit; } DateTime KPlatoRCPSScheduler::fromRcpsTime( int time ) const { return m_starttime.addSecs( ( m_backward ? -time : time ) * m_timeunit ); } struct rcps_job *KPlatoRCPSScheduler::addTask( KPlato::Task *task ) { struct rcps_job *job = rcps_job_new(); rcps_job_setname( job, task->name().toLocal8Bit().data() ); rcps_job_add( m_problem, job ); m_taskmap[job] = task; return job; } void KPlatoRCPSScheduler::addTasks() { kDebug(planDbg()); // Add a start job m_jobstart = rcps_job_new(); rcps_job_setname( m_jobstart, "RCPS start job" ); rcps_job_add( m_problem, m_jobstart ); struct rcps_mode *mode = rcps_mode_new(); rcps_mode_add( m_jobstart, mode ); QList list = m_project->allNodes(); for (int i = 0; i < list.count(); ++i) { Node *n = list.at(i); switch ( n->type() ) { case Node::Type_Summarytask: m_schedule->insertSummaryTask( n ); break; case Node::Type_Task: case Node::Type_Milestone: addTask( static_cast( n ) ); break; default: break; } } // Add an end job m_jobend = rcps_job_new(); rcps_job_setname( m_jobend, "RCPS end job" ); rcps_job_add( m_problem, m_jobend ); mode = rcps_mode_new(); rcps_mode_add( m_jobend, mode ); // add a weight callback argument struct KPlatoRCPSScheduler::weight_info *info = new KPlatoRCPSScheduler::weight_info; info->self = this; info->task = 0; info->targettime = toRcpsTime( m_targettime ); info->isEndJob = true; info->finish = 0; rcps_mode_set_weight_cbarg( mode, info ); m_weight_info_list[ m_jobend ] = info; for( int i = 0; i < rcps_job_count( m_problem ); ++i ) { kDebug(planDbg())<<"Task:"<dependParentNodes().isEmpty() && task->parentProxyRelations().isEmpty() ) { rcps_job_successor_add( m_jobstart, job, SUCCESSOR_FINISH_START ); } if ( task->dependChildNodes().isEmpty() && task->childProxyRelations().isEmpty() ) { rcps_job_successor_add( job, m_jobend, SUCCESSOR_FINISH_START ); } foreach ( Relation *r, task->dependChildNodes() ) { Node *n = r->child(); if ( n == 0 || n->type() == Node::Type_Summarytask ) { continue; } int type = SUCCESSOR_FINISH_START; switch ( r->type() ) { case Relation::FinishStart: type = SUCCESSOR_FINISH_START; break; case Relation::FinishFinish: type = SUCCESSOR_FINISH_FINISH; break; case Relation::StartStart: type = SUCCESSOR_START_START; break; } if ( r->lag() == Duration::zeroDuration ) { rcps_job_successor_add( job, m_taskmap.key( static_cast( n ) ), type ); } else { // Add a dummy job to represent the lag struct rcps_job *dummy = addJob( r->lag().toString(), r->lag().seconds() / m_timeunit ); rcps_job_successor_add( job, dummy, type ); int t = type == SUCCESSOR_FINISH_FINISH ? type : SUCCESSOR_FINISH_START; rcps_job_successor_add( dummy, m_taskmap.key( static_cast( n ) ), t ); } } foreach ( Relation *r, task->childProxyRelations() ) { Node *n = r->child(); if ( n == 0 || n->type() == Node::Type_Summarytask ) { continue; } int type = SUCCESSOR_FINISH_START; switch ( r->type() ) { case Relation::FinishStart: type = SUCCESSOR_FINISH_START; break; case Relation::FinishFinish: type = SUCCESSOR_FINISH_FINISH; break; case Relation::StartStart: type = SUCCESSOR_START_START; break; } if ( r->lag() == Duration::zeroDuration ) { rcps_job_successor_add( job, m_taskmap.key( static_cast( n ) ), type ); } else { // Add a dummy job to represent the lag struct rcps_job *dummy = addJob( r->lag().toString(), r->lag().seconds() / m_timeunit ); rcps_job_successor_add( job, dummy, type ); int t = type == SUCCESSOR_FINISH_FINISH ? type : SUCCESSOR_FINISH_START; rcps_job_successor_add( dummy, m_taskmap.key( static_cast( n ) ), t ); } } } void KPlatoRCPSScheduler::addDependenciesBackward( struct rcps_job *job, KPlato::Task *task ) { if ( task->dependParentNodes().isEmpty() && task->parentProxyRelations().isEmpty() ) { rcps_job_successor_add( job, m_jobend, SUCCESSOR_FINISH_START ); //kDebug(planDbg())<"<dependChildNodes().isEmpty() && task->childProxyRelations().isEmpty() ) { rcps_job_successor_add( m_jobstart, job, SUCCESSOR_FINISH_START ); //kDebug(planDbg())<"<dependParentNodes() ) { Node *n = r->parent(); if ( n == 0 || n->type() == Node::Type_Summarytask ) { continue; } int type = SUCCESSOR_FINISH_START; switch ( r->type() ) { case Relation::FinishStart: type = SUCCESSOR_FINISH_START; break; case Relation::FinishFinish: type = SUCCESSOR_FINISH_FINISH; break; case Relation::StartStart: type = SUCCESSOR_START_START; break; } if ( r->lag() == Duration::zeroDuration ) { rcps_job_successor_add( job, m_taskmap.key( static_cast( n ) ), type ); //kDebug(planDbg())<"<( n ) ) )<lag().toString(), r->lag().seconds() / m_timeunit ); rcps_job_successor_add( job, dummy, type ); kDebug(planDbg())<"<<"dummy lag"<( n ) ), t ); //kDebug(planDbg())<<"dummy lag"<<"->"<( n ) ) )<parentProxyRelations() ) { Node *n = r->parent(); if ( n == 0 || n->type() == Node::Type_Summarytask ) { continue; } int type = SUCCESSOR_FINISH_START; switch ( r->type() ) { case Relation::FinishStart: type = SUCCESSOR_FINISH_START; break; case Relation::FinishFinish: type = SUCCESSOR_FINISH_FINISH; break; case Relation::StartStart: type = SUCCESSOR_START_START; break; } if ( r->lag() == Duration::zeroDuration ) { rcps_job_successor_add( job, m_taskmap.key( static_cast( n ) ), type ); //kDebug(planDbg())<"<( n ) ) )<lag().toString(), r->lag().seconds() / m_timeunit ); rcps_job_successor_add( job, dummy, type ); kDebug(planDbg())<"<<"dummy lag"<( n ) ), t ); //kDebug(planDbg())<<"dummy lag"<<"->"<( n ) ) )< ::const_iterator it = m_taskmap.constBegin(); for ( ; it != m_taskmap.constEnd(); ++it ) { if ( m_backward ) { addDependenciesBackward( it.key(), it.value() ); } else { addDependenciesForward( it.key(), it.value() ); } } } void KPlatoRCPSScheduler::addRequests() { kDebug(planDbg()); QMap ::const_iterator it = m_taskmap.constBegin(); for ( ; it != m_taskmap.constEnd(); ++it ) { addRequest( it.key(), it.value() ); } } void KPlatoRCPSScheduler::addRequest( rcps_job *job, Task *task ) { kDebug(planDbg()); struct rcps_mode *mode = rcps_mode_new(); rcps_mode_add( job, mode ); // add a weight callback argument struct KPlatoRCPSScheduler::weight_info *wi = new KPlatoRCPSScheduler::weight_info; wi->self = this; wi->task = task; wi->targettime = 0; wi->isEndJob = false; wi->finish = 0; rcps_mode_set_weight_cbarg( mode, wi ); m_weight_info_list[ job ] = wi; if ( task->constraint() != Node::FixedInterval ) { if ( task->type() == Node::Type_Milestone || task->estimate() == 0 || ( m_recalculate && task->completion().isFinished() ) ) { rcps_mode_setduration(mode, 0); return; } if ( task->estimate()->type() == Estimate::Type_Duration && task->estimate()->calendar() == 0 ) { // Fixed duration, no duration callback needed rcps_mode_setduration(mode, task->estimate()->value( Estimate::Use_Expected, m_usePert ).seconds() / m_timeunit ); return; } } /* set the argument for the duration callback */ struct KPlatoRCPSScheduler::duration_info *info = new KPlatoRCPSScheduler::duration_info; info->self = this; info->calls = 0; info->task = task; if ( m_recalculate && task->completion().isStarted() ) { info->estimate = task->completion().remainingEffort(); } else { info->estimate = task->estimate()->value( Estimate::Use_Expected, m_usePert ); } info->requests = task->requests().resourceRequests(); // returns team members (not team resource itself) info->estimatetype = task->estimate()->type(); rcps_mode_set_cbarg( mode, info ); m_duration_info_list[ job ] = info; foreach ( ResourceRequest *rr, info->requests ) { Resource *r = rr->resource(); if ( r->type() == Resource::Type_Team ) { kWarning()<<"There should not be any request to a team resource:"<name(); continue; } struct rcps_request *req = rcps_request_new(); rcps_request_add( mode, req ); m_requestmap[ req ] = rr; struct rcps_alternative *alt = rcps_alternative_new(); rcps_alternative_setresource( alt, m_resourcemap.key( r ) ); rcps_alternative_setamount( alt, (double)rr->units() * 100 / r->units() ); rcps_alternative_add( req, alt ); } } void KPlatoRCPSScheduler::setConstraints() { for ( QMap::iterator it = m_taskmap.begin(); it != m_taskmap.end(); ++it ) { Task *task = it.value(); struct rcps_job *job = it.key(); struct weight_info *wi = m_weight_info_list.value( job ); struct duration_info *di = m_duration_info_list.value( job ); switch ( task->constraint() ) { case Node::MustStartOn: case Node::StartNotEarlier: wi->targettime = toRcpsTime( task->constraintStartTime() ); if ( m_backward ) { int d = 0; if ( di ) { // as m_backward == true, DURATION_BACKWARD in rcps means forward in plan d = duration( DURATION_BACKWARD, wi->targettime, 0, di ); } wi->targettime -= d; } rcps_job_setearliest_start( job, wi->targettime ); task->currentSchedule()->logDebug( QString( "%2 %3 %4: %5 (rcps=%6)") .arg( task->constraintToString() ) .arg( m_backward?"backward":"forward") .arg( task->constraintStartTime().toString() ) .arg( fromRcpsTime( wi->targettime ).toString() ) .arg( wi->targettime ) ); break; case Node::MustFinishOn: wi->targettime = toRcpsTime( task->constraintEndTime() ); if ( ! m_backward ) { int d = 0; if ( di ) { d = duration( DURATION_BACKWARD, wi->targettime, 0, di ); } rcps_job_setearliest_start( job, wi->targettime - d ); } break; case Node::FinishNotLater: wi->targettime = toRcpsTime( task->constraintEndTime() ); if ( m_backward ) { rcps_job_setearliest_start( job, wi->targettime ); } break; case Node::FixedInterval: wi->targettime = m_backward ? toRcpsTime( task->constraintEndTime() ) : toRcpsTime( task->constraintStartTime() ); rcps_job_setearliest_start( job, wi->targettime ); break; default: break; } } } void KPlatoRCPSScheduler::setWeights() { for ( QMap::iterator it = m_taskmap.begin(); it != m_taskmap.end(); ++it ) { Task *task = it.value(); struct rcps_job *job = it.key(); if ( m_backward ) { switch ( task->constraint() ) { case Node::ASAP: rcps_job_setweight( job, WEIGHT_ALAP ); break; case Node::ALAP: rcps_job_setweight( job, WEIGHT_ASAP ); break; case Node::StartNotEarlier: rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::MustStartOn: rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::FinishNotLater: rcps_job_setearliest_start( job, toRcpsTime( task->constraintEndTime() ) ); rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::MustFinishOn: rcps_job_setearliest_start( job, toRcpsTime( task->constraintEndTime() ) ); rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::FixedInterval: rcps_job_setearliest_start( job, toRcpsTime( task->constraintEndTime() ) ); rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; default: rcps_job_setweight( job, WEIGHT_ASAP ); break; } } else { switch ( task->constraint() ) { case Node::ASAP: rcps_job_setweight( job, WEIGHT_ASAP ); break; case Node::ALAP: rcps_job_setweight( job, WEIGHT_ALAP ); break; case Node::StartNotEarlier: rcps_job_setearliest_start( job, toRcpsTime( task->constraintStartTime() ) ); rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::MustStartOn: rcps_job_setearliest_start( job, toRcpsTime( task->constraintStartTime() ) ); rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::FinishNotLater: rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::MustFinishOn: rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; case Node::FixedInterval: rcps_job_setearliest_start( job, toRcpsTime( task->constraintStartTime() ) ); rcps_job_setweight( job, WEIGHT_CONSTRAINT ); break; default: rcps_job_setweight( job, WEIGHT_ASAP ); break; } } } } #include "KPlatoRCPSScheduler.moc" diff --git a/plugins/chartshape/CellRegion.cpp b/plugins/chartshape/CellRegion.cpp index 0cf2b667bfe..c6361be0f81 100644 --- a/plugins/chartshape/CellRegion.cpp +++ b/plugins/chartshape/CellRegion.cpp @@ -1,797 +1,797 @@ /* This file is part of the KDE project Copyright 2008 Johannes Simon Copyright 2008 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 "CellRegion.h" // C #include // Qt #include #include #include #include #include // KDE #include // KChart #include "TableSource.h" using std::pow; using namespace KChart; /************************RegionParser*******************************/ class Parser { public: Parser(const QString & input) : m_input(input) , m_pos(m_input.constEnd()) { m_delimiter.append(QChar::fromLatin1('.')); m_delimiter.append(QChar::fromLatin1(':')); m_delimiter.append(QChar::fromLatin1(';')); m_delimiter.append(QChar::fromLatin1(' ')); } bool parse(); QList< QRect > getResult() const { return m_result; } QString tableName() const { return m_tableName; } private: struct Token { enum TokenType{ Dot = 0, DoubleDot = 1, Space = 2, Spacer = 3, Identifier = 4, End }; Token(TokenType type, const QString & identifier): m_type(type), m_identifier(identifier){} Token(): m_type(End) {} TokenType m_type; QString m_identifier; }; inline Token parseToken(); inline void eatWhitespaces(); inline bool parseRegionList(); inline bool parseRegion(); inline bool parseName(); inline bool parseName2(); inline bool parsePoint(); inline bool parseRegion2(); inline void setTableName(const QString &name); private: const QString m_input; QString::ConstIterator m_pos; QList< QRect > m_result; Token m_currentToken; QRect m_currentRect; QPoint m_currentPoint; QString m_tableName; int m_index; QVector< QChar > m_delimiter; }; void Parser::setTableName(const QString &name) { QString strippedName = name; if (name.startsWith(QChar::fromLatin1('$'))) strippedName.remove(0, 1); if (m_tableName.isEmpty()) m_tableName = strippedName; else if (strippedName != m_tableName) kDebug() << "More than one sheet referenced, this is currently not supported"; } bool Parser::parse() { //qDebug() << "Input " << m_input; m_pos = m_input.constBegin(); m_index = 0; m_currentToken = parseToken(); return parseRegionList(); } Parser::Token Parser::parseToken() { Token::TokenType type = Token::End; if (m_pos != m_input.constEnd()) { switch(m_delimiter.indexOf(*m_pos)) { case(0): type = Token::Dot; break; case(1): type = Token::DoubleDot; break; case(2): case(3): type = Token::Space; break; default: type = Token::Identifier; } } bool dollarPrefix = false; if (m_index >= m_input.size()) type = Token::End; else if (*m_pos == QChar::fromLatin1('$')) { ++m_pos; ++m_index; } QString identifier; if (m_pos != m_input.constEnd() && *m_pos == QChar::fromLatin1('\'')) { ++m_pos; ++m_index; int startPos = m_index; for (; m_pos != m_input.constEnd() && *m_pos != QChar::fromLatin1('\''); ++m_pos, ++m_index) ; if (type == Token::Identifier) identifier = m_input.mid(startPos, m_index - startPos); if (m_pos != m_input.constEnd()) { ++m_pos; ++m_index; } } else { int startPos = m_index; for (; m_pos != m_input.constEnd() && !m_delimiter.contains(*m_pos); ++m_pos, ++m_index) ; if (m_pos != m_input.constEnd() && startPos == m_index) { ++m_index; ++m_pos; } if (type == Token::Identifier) identifier = m_input.mid(startPos, m_index - startPos); } return Token(type, identifier); } void Parser::eatWhitespaces() { for (; m_pos != m_input.constEnd() && *m_pos == QChar::fromLatin1(' '); ++m_pos, ++m_index) ; } bool Parser::parseRegionList() { bool res = true; for (; m_currentToken.m_type != Token::End; m_currentToken = parseToken()) { if (m_currentToken.m_type != Token::Space) { if (m_currentToken.m_type == Token::Identifier) res = parseRegion(); else res = false; } } return res; } bool Parser::parseRegion() { //qDebug() << "parseRegion"; bool res = true; res &= parseRegion2(); m_currentToken = parseToken(); //qDebug() << "CurrentToken " << m_currentToken.m_identifier << m_currentToken.m_type; if (m_currentToken.m_type == Token::DoubleDot) { const QPoint topLeft = m_currentPoint; m_currentToken = parseToken(); res &= parseRegion2(); //m_currentToken = parseToken(); m_result.append(QRect(topLeft, m_currentPoint)); //qDebug() << "DoubleDot"; } else { m_result.append(QRect(m_currentPoint, m_currentPoint)); //qDebug() << "NODoubleDot"; } if (m_currentToken.m_type == Token::Space) res &= parseRegionList(); else if (m_currentToken.m_type == Token::End) return res; else res = false; return res; } bool Parser::parseRegion2() { //qDebug() << "ParseRegion2"; bool res = true; if (m_currentToken.m_type != Token::Identifier && m_currentToken.m_type != Token::Dot) res = false; const QString firstIdentifier = m_currentToken.m_type != Token::Dot ? m_currentToken.m_identifier : tableName(); if (m_currentToken.m_type != Token::Dot) m_currentToken = parseToken(); if (m_currentToken.m_type == Token::Dot) { m_currentToken = parseToken(); if (m_currentToken.m_type == Token::Identifier) { QRegExp regEx(QString::fromLatin1("([$]*)([A-Z]+)([$]*)([0-9]+)")); regEx.exactMatch(m_currentToken.m_identifier); m_currentPoint = QPoint(CellRegion::rangeStringToInt(regEx.cap(2)), regEx.cap(4).toInt()); //qDebug() << "FUN" << regEx.cap(2) << " " << regEx.cap(4); setTableName(firstIdentifier); } else res = false; } else { QRegExp regEx(QString::fromLatin1("([$]*)([A-Z]+)([$]*)([0-9]+)")); regEx.exactMatch(firstIdentifier); //qDebug() << "FUN" << regEx.cap(2) << " " << regEx.cap(4); m_currentPoint = QPoint(CellRegion::rangeStringToInt(regEx.cap(2)), regEx.cap(4).toInt()); } //qDebug() << "TableName "<< m_tableName; //qDebug() << firstIdentifier; //qDebug() << "Point" << m_currentPoint; //qDebug() << m_currentToken.m_identifier; //qDebug() << m_currentToken.m_type; return res; } // bool Parser::parsePoint() // { // bool res = true; // int startIndex = m_index; // while(m_pos != m_input.end() && *m_pos != QChar::fromLatin1(':')) // { // ++m_pos; // ++m_index; // } // const QString currentString = m_input.mid(startIndex, m_index - startIndex); // qDebug() << "PointString" << currentString; // QRegExp regEx(QString::fromLatin1("[A-Z]+[0-9]+")); // regEx.indexIn(currentString); // m_currentPoint = QPoint(CellRegion::rangeStringToInt(regEx.cap(0)), regEx.cap(1).toInt()); // return res; // } /************************ENDRegionParser*******************************/ static QString columnName(uint column); //static int rangeCharToInt(char c); /** * Makes sure that quotes are added if name contains spaces or special * characters. May also be used to escape certain characters if needed. */ static QString formatTableName(QString name) { static const QList specialChars = QList() << ' ' << '\t' << '-' << '\''; bool containsSpecialChars = false; foreach(QChar c, specialChars) containsSpecialChars = containsSpecialChars || name.contains(c); if(containsSpecialChars) name.prepend('\'').append('\''); return name; } /** * Reverts any operation done by formatTableName(), so that ideally * unformatTableName(formatTableName(name)) == name */ static QString unformatTableName(QString name) { if (name.startsWith('\'') && name.endsWith('\'')) { name.remove(0, 1); name.remove(name.length() - 1, 1); } return name; } class CellRegion::Private { public: Private(); ~Private(); QString pointToString(const QPoint &point) const; // These are actually one-dimensional, but can have different // orientations (hor / vert). QVector rects; QRect boundingRect; // NOTE: Don't forget to extend operator=() if you add new members /// Table this region is in (name/model pair provided by TableSource) Table *table; }; CellRegion::Private::Private() { table = 0; } CellRegion::Private::~Private() { } // ================================================================ // Class CellRegion CellRegion::CellRegion() : d(new Private()) { } CellRegion::CellRegion(const CellRegion ®ion) : d(new Private()) { // Use operator=(); *this = region; } CellRegion::CellRegion(TableSource *source, const QString& regions) : d(new Private()) { // A dollar sign before a part of the address means that this part // is absolute. This is irrelevant for us, however, thus we can remove // all occurrences of '$', and handle relative and absolute addresses in // the same way. // See ODF specs $8.3.1 "Referencing Table Cells" Parser parser(regions); const bool success = parser.parse(); if (!success) kDebug() << "Parsing cell region failed"; d->rects = parser.getResult().toVector(); d->table = source->get(parser.tableName()); // QStringList regionsList = regions.split(" ", QString::SkipEmptyParts); // Q_FOREACH(const QString& region, regionsList) { // QString searchStr = QString(region).remove("$"); // QRegExp regEx; // // QStringList regionList = searchStr.split(";"); // Q_FOREACH(const QString ®ion, regionList) { // const bool isPoint = !region.contains(':'); // if (isPoint) // regEx = QRegExp("(|.*\\.)([A-Z]+)([0-9]+)"); // else // support range-notations like Sheet1.D2:Sheet1.F2 Sheet1.D2:F2 D2:F2 // regEx = QRegExp ("(|.*\\.)([A-Z]+)([0-9]+)\\:(|.*\\.)([A-Z]+)([0-9]+)"); // // // Check if region string is valid (e.g. not empty) // if (regEx.indexIn(region) >= 0) { // // It is possible for a cell-range-address as defined in ODF to contain // // refernces to cells of more than one sheet. This, however, we ignore // // here. We do not support more than one table in a cell region. // // Also we do not support regions spanned over different sheets. For us // // everything is either on no sheet or on the same sheet. // QString sheetName = regEx.cap(1); // if (sheetName.endsWith(".")) // sheetName = sheetName.left(sheetName.length() - 1); // // TODO: Support for multiple tables in one region // d->table = source->get(unformatTableName(sheetName)); // // QPoint topLeft(rangeStringToInt(regEx.cap(2)), regEx.cap(3).toInt()); // if (isPoint) { // d->rects.append(QRect(topLeft, QSize(1, 1))); // } else { // QPoint bottomRight(rangeStringToInt(regEx.cap(5)), regEx.cap(6).toInt()); // d->rects.append(QRect(topLeft, bottomRight)); // } // } // } // } } CellRegion::CellRegion(Table *table, const QPoint &point) : d(new Private()) { d->table = table; add(point); } CellRegion::CellRegion(Table *table, const QRect &rect) : d(new Private()) { d->table = table; add(rect); } CellRegion::CellRegion(Table *table, const QVector &rects) : d(new Private()) { d->table = table; foreach(const QRect& rect, rects) add(rect); } CellRegion::CellRegion(Table *table) : d(new Private()) { d->table = table; } CellRegion::~CellRegion() { delete d; } CellRegion& CellRegion::operator = (const CellRegion& region) { d->rects = region.d->rects; d->boundingRect = region.d->boundingRect; d->table = region.d->table; return *this; } bool CellRegion::operator == (const CellRegion &other) const { return d->rects == other.d->rects; } Table *CellRegion::table() const { return d->table; } QVector CellRegion::rects() const { return d->rects; } int CellRegion::rectCount() const { return d->rects.size(); } QString CellRegion::sheetName() const { return d->table->name(); } bool CellRegion::isValid() const { return d->rects.size() > 0 && d->table ; } QString CellRegion::Private::pointToString(const QPoint &point) const { QString result; result.append('$' + columnName(point.x())); result.append('$' + QString::number(point.y())); return result; } QString CellRegion::toString() const { if (!isValid()) return QString(); QString result; for (int i = 0; i < d->rects.count(); ++i) { const QRect range = d->rects[i]; // Top-left corner if (table()) result.append('$' + formatTableName(table()->name()) + '.'); result.append(d->pointToString(range.topLeft())); // If it is not a point, append rect's bottom-right corner if (range.topLeft() != range.bottomRight()) { result.append(':'); result.append(d->pointToString(range.bottomRight())); } // Separate ranges by a comma, except for the last one if (i < d->rects.count() - 1) result.append(';'); } return result; } bool CellRegion::contains(const QPoint &point, bool proper) const { foreach (const QRect &rect, d->rects) { if (rect.contains(point, proper)) return true; } return false; } bool CellRegion::contains(const QRect &rect, bool proper) const { foreach (const QRect &r, d->rects) { if (r.contains(rect, proper)) return true; } return false; } bool CellRegion::intersects(const CellRegion &other) const { // If both regions lie within only one table and these tables // are different, they trivially do not intersect. if (table() && other.table() && table() != other.table()) return false; foreach (const QRect &r, d->rects) { foreach(const QRect &_r, other.d->rects) { if (r.intersects(_r)) return true; } } return false; } CellRegion CellRegion::intersected(const QRect &rect) const { CellRegion intersections; foreach (const QRect &r, d->rects) { if (r.intersects(rect)) intersections.add(r.intersected(rect)); } return intersections; } Qt::Orientation CellRegion::orientation() const { foreach (const QRect &rect, d->rects) { if (rect.width() > 1) return Qt::Horizontal; if (rect.height() > 1) return Qt::Vertical; } // Default if region is only one cell return Qt::Vertical; } int CellRegion::cellCount() const { int count = 0; /*FIXME the following would be more correct cause it * would also cover multi-dimensional ranges (means * where rect.width()>1 *and* rect.height()>1). But * for that kchart needs lot of fixing (e.g. in * the CellRegion to proper handle multi-dimension * ranges too). * foreach (const QRect &rect, d->rects) count += (rect.width() * rect.height()); */ if (orientation() == Qt::Horizontal) { foreach (const QRect &rect, d->rects) count += rect.width(); } else { foreach(const QRect &rect, d->rects) count += rect.height(); } return count; } void CellRegion::add(const CellRegion &other) { add(other.rects()); } void CellRegion::add(const QPoint &point) { add(QRect(point, QSize(1, 1))); } void CellRegion::add(const QRect &rect) { // These checks are obsolete, a CellRegion can be used otherwise as well #if 0 if (!rect.isValid()) { qWarning() << "CellRegion::add() Attempt to add invalid rectangle"; qWarning() << "CellRegion::add():" << rect; return; } if (rect.width() > 1 && rect.height() > 1) { qWarning() << "CellRegion::add() Attempt to add rectangle with height AND width > 1"; qWarning() << "CellRegion::add():" << rect; return; } #endif d->rects.append(rect); d->boundingRect |= rect; } void CellRegion::add(const QVector &rects) { foreach (const QRect &rect, rects) add(rect); } QRect CellRegion::boundingRect() const { return d->boundingRect; } bool CellRegion::hasPointAtIndex(int index) const { return pointAtIndex(index) != QPoint(-1, -1); } QPoint CellRegion::pointAtIndex(int index) const { // sum of all previous rectangle indices int i = 0; foreach (const QRect &rect, d->rects) { // Rectangle is horizontal if (rect.width() > 1) { // Found it! // Index refers to point in current rectangle if (i + rect.width() > index) { // Local index of point in this rectangle int j = index - i; return QPoint(rect.x() + j, rect.y()); } // add number of indices in current rectangle to total index count i += rect.width(); } else { // Found it! // Index refers to point in current rectangle if (i + rect.height() > index) { // Local index of point in this rectangle int j = index - i; return QPoint(rect.x(), rect.y() + j); } // add number of indices in current rectangle to total index count i += rect.height(); } } // Invalid index! return QPoint(-1, -1); } int CellRegion::indexAtPoint(const QPoint &point) const { int indicesLeftToPoint = 0; bool found = false; foreach (const QRect &rect, d->rects) { if (!rect.contains(point)) { indicesLeftToPoint += rect.width() > 1 ? rect.width() : rect.height(); continue; } found = true; if (rect.width() > 1) indicesLeftToPoint += point.x() - rect.topLeft().x(); else indicesLeftToPoint += point.y() - rect.topLeft().y(); } return found ? indicesLeftToPoint : -1; } #if 0 // Unused? static int rangeCharToInt(char c) { return (c >= 'A' && c <= 'Z') ? (c - 'A' + 1) : -1; } static int rangeStringToInt(const QString &string) { int result = 0; const int size = string.size(); for (int i = 0; i < size; i++) { - //kDebug(350001) << "---" << float(rangeCharToInt(string[i].toAscii()) * pow(10.0, (size - i - 1))); - result += rangeCharToInt(string[i].toAscii()) * pow(10.0, (size - i - 1)); + //kDebug(350001) << "---" << float(rangeCharToInt(string[i].toLatin1()) * pow(10.0, (size - i - 1))); + result += rangeCharToInt(string[i].toLatin1()) * pow(10.0, (size - i - 1)); } //kDebug(350001) << "+++++ result=" << result; return result; } static QString rangeIntToString(int i) { QString tmp = QString::number(i); for (int j = 0; j < tmp.size(); j++) { - tmp[j] = 'A' + tmp[j].toAscii() - '1'; + tmp[j] = 'A' + tmp[j].toLatin1() - '1'; } //kDebug(350001) << "tmp=" << tmp; return tmp; } #endif int CellRegion::rangeCharToInt(char c) { return (c >= 'A' && c <= 'Z') ? (c - 'A' + 1) : -1; } int CellRegion::rangeStringToInt(const QString &string) { int result = 0; const int size = string.size(); for (int i = 0; i < size; i++) { - result += rangeCharToInt(string[i].toAscii()) * pow(10.0, (size - i - 1)); + result += rangeCharToInt(string[i].toLatin1()) * pow(10.0, (size - i - 1)); } return result; } QString CellRegion::rangeIntToString(int i) { QString tmp = QString::number(i); for (int j = 0; j < tmp.size(); j++) { - tmp[j] = 'A' + tmp[j].toAscii() - '1'; + tmp[j] = 'A' + tmp[j].toLatin1() - '1'; } return tmp; } // Return the symbolic name of any column. static QString columnName(uint column) { if (column < 1 || column > 32767) return QString("@@@"); QString str; unsigned digits = 1; unsigned offset = 0; column--; for (unsigned limit = 26; column >= limit + offset; limit *= 26, ++digits) offset += limit; for (unsigned col = column - offset; digits; --digits, col /= 26) str.prepend(QChar('A' + (col % 26))); return str; } diff --git a/plugins/chartshape/ChartTableModel.cpp b/plugins/chartshape/ChartTableModel.cpp index d478b515dc0..4e6529a3e56 100644 --- a/plugins/chartshape/ChartTableModel.cpp +++ b/plugins/chartshape/ChartTableModel.cpp @@ -1,191 +1,191 @@ /* This file is part of the KDE project Copyright 2008 Johannes Simon Copyright 2009 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 "ChartTableModel.h" // C #include // Qt #include #include // KDE #include // Calligra #include #include #include #include #include #include #include // KChart #include "CellRegion.h" #include "OdfLoadingHelper.h" namespace KChart { ChartTableModel::ChartTableModel(QObject *parent /* = 0 */) : QStandardItemModel(parent) { } ChartTableModel::~ChartTableModel() { } QHash > ChartTableModel::cellRegion() const { // FIXME: Unimplemented? return QHash >(); } bool ChartTableModel::setCellRegion(const QString& /*regionName*/) { #if 0 // FIXME: What does this code do? int result = 0; const int size = regionName.size(); for (int i = 0; i < size; i++) { - result += (CellRegion::rangeCharToInt(regionName[i].toAscii()) + result += (CellRegion::rangeCharToInt(regionName[i].toLatin1()) * std::pow(10.0, (size - i - 1))); } return result; #endif return true; } bool ChartTableModel::isCellRegionValid(const QString& regionName) const { Q_UNUSED(regionName); return true; } bool ChartTableModel::loadOdf(const KoXmlElement &tableElement, KoShapeLoadingContext &context) { Q_UNUSED(context); setRowCount(0); setColumnCount(0); //QDomDocument doc; //KoXml::asQDomElement(doc, tableElement); //QTextStream stream(stdout); //stream << doc.documentElement(); int row = 0; KoXmlElement n; forEachElement (n, tableElement) { if (n.namespaceURI() != KoXmlNS::table) continue; if (n.localName() == "table-columns" || n.localName() == "table-header-columns") { int column = 0; KoXmlElement _n; forEachElement (_n, n) { if (_n.namespaceURI() != KoXmlNS::table || _n.localName() != "table-column") continue; column += qMax(1, _n.attributeNS(KoXmlNS::table, "number-columns-repeated").toInt()); if (column > columnCount()) setColumnCount(column); } } else if (n.localName() == "table-rows" || n.localName() == "table-header-rows") { if (n.localName() == "table-header-rows") { if (row >= 1) { // There can only be one header-row and only at the very beginning. // So, ignore all following header-rows to be sure our internal // table doesn't start at the wrong offset or something like that. continue; } } KoXmlElement _n; forEachElement (_n, n) { if (_n.namespaceURI() != KoXmlNS::table || _n.localName() != "table-row") continue; // Add a row to the internal representation. setRowCount(row + 1); // Loop through all cells in a table row. int column = 0; KoXmlElement __n; forEachElement (__n, _n) { if (__n.namespaceURI() != KoXmlNS::table || __n.localName() != "table-cell") continue; // We have a cell so be sure our column-counter is increased right now so // any 'continue' coming now will leave with the correct value for the next // cell we deal with. ++column; // If this row is wider than any previous one, then add another column. if (column > columnCount()) setColumnCount(column); const QString valueType = __n.attributeNS(KoXmlNS::office, "value-type"); QString valueString = __n.attributeNS(KoXmlNS::office, "value"); const KoXmlElement valueElement = __n.namedItemNS(KoXmlNS::text, "p").toElement(); if ((valueElement.isNull() || !valueElement.isElement()) && valueString.isEmpty()) continue; // Read the actual value in the cell. QVariant value; if (valueString.isEmpty()) valueString = valueElement.text().trimmed(); if (valueType == "float") value = valueString.toDouble(); else if (valueType == "boolean") value = (bool)valueString.toInt(); else // if (valueType == "string") value = valueString; setData(index(row, column - 1), value); } // foreach table:table-cell ++row; } // foreach table:table-row } } return true; } bool ChartTableModel::saveOdf(KoXmlWriter &bodyWriter, KoGenStyles &mainStyles ) const { Q_UNUSED(bodyWriter); Q_UNUSED(mainStyles); // The save logic is in ChartShape::saveOdf return true; } } diff --git a/plugins/chartshape/tests/TestDataSet.cpp b/plugins/chartshape/tests/TestDataSet.cpp index 72c35b0ee8d..1a64ac742b5 100644 --- a/plugins/chartshape/tests/TestDataSet.cpp +++ b/plugins/chartshape/tests/TestDataSet.cpp @@ -1,195 +1,195 @@ /* This file is part of the KDE project Copyright 2008 Johannes Simon 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 "TestDataSet.h" // Qt #include #include #include #include #include #include // KChart #include "DataSet.h" #include "CellRegion.h" namespace QTest { template<> char *toString(const CellRegion ®ion) { - return qstrdup(region.toString().toAscii().data()); + return qstrdup(region.toString().toLatin1()); } } using namespace KChart; TestDataSet::TestDataSet() : m_source() , m_proxyModel(0, &m_source) , m_sourceModel1() , m_table1(0) { } void TestDataSet::initTestCase() { m_table1 = m_source.add("Table1", &m_sourceModel1); m_table2 = m_source.add("Table2", &m_sourceModel2); m_sourceModel1.setRowCount(4); m_sourceModel1.setColumnCount(5); // Vertical header data m_sourceModel1.setData(m_sourceModel1.index(1, 0), "Row 1"); m_sourceModel1.setData(m_sourceModel1.index(2, 0), "Row 2"); m_sourceModel1.setData(m_sourceModel1.index(3, 0), "Row 3"); // Horizontal header data m_sourceModel1.setData(m_sourceModel1.index(0, 1), "Column 1"); m_sourceModel1.setData(m_sourceModel1.index(0, 2), "Column 2"); m_sourceModel1.setData(m_sourceModel1.index(0, 3), "Column 3"); m_sourceModel1.setData(m_sourceModel1.index(0, 4), "Column 4"); // First row m_sourceModel1.setData(m_sourceModel1.index(1, 1), 7.2); m_sourceModel1.setData(m_sourceModel1.index(1, 2), 1.8); m_sourceModel1.setData(m_sourceModel1.index(1, 3), 9.4); m_sourceModel1.setData(m_sourceModel1.index(1, 4), 1.5); // Second row m_sourceModel1.setData(m_sourceModel1.index(2, 1), 8.4); m_sourceModel1.setData(m_sourceModel1.index(2, 2), 2.9); m_sourceModel1.setData(m_sourceModel1.index(2, 3), 3.7); m_sourceModel1.setData(m_sourceModel1.index(2, 4), 5.5); // Third row m_sourceModel1.setData(m_sourceModel1.index(3, 1), 2.9); m_sourceModel1.setData(m_sourceModel1.index(3, 2), 5.3); m_sourceModel1.setData(m_sourceModel1.index(3, 3), 6.4); m_sourceModel1.setData(m_sourceModel1.index(3, 4), 2.1); m_sourceModel2.setRowCount(4); m_sourceModel2.setColumnCount(5); // Vertical header data m_sourceModel2.setData(m_sourceModel2.index(1, 0), "Row 1"); m_sourceModel2.setData(m_sourceModel2.index(2, 0), "Row 2"); m_sourceModel2.setData(m_sourceModel2.index(3, 0), "Row 3"); // Horizontal header data m_sourceModel2.setData(m_sourceModel2.index(0, 1), "Column 1"); m_sourceModel2.setData(m_sourceModel2.index(0, 2), "Column 2"); m_sourceModel2.setData(m_sourceModel2.index(0, 3), "Column 3"); m_sourceModel2.setData(m_sourceModel2.index(0, 4), "Column 4"); // First row m_sourceModel2.setData(m_sourceModel2.index(1, 1), 1); m_sourceModel2.setData(m_sourceModel2.index(1, 2), 2); m_sourceModel2.setData(m_sourceModel2.index(1, 3), 3); m_sourceModel2.setData(m_sourceModel2.index(1, 4), 4); // Second row m_sourceModel2.setData(m_sourceModel2.index(2, 1), 5); m_sourceModel2.setData(m_sourceModel2.index(2, 2), 6); m_sourceModel2.setData(m_sourceModel2.index(2, 3), 7); m_sourceModel2.setData(m_sourceModel2.index(2, 4), 8); // Third row m_sourceModel2.setData(m_sourceModel2.index(3, 1), 9); m_sourceModel2.setData(m_sourceModel2.index(3, 2), 10); m_sourceModel2.setData(m_sourceModel2.index(3, 3), 11); m_sourceModel2.setData(m_sourceModel2.index(3, 4), 12); } void TestDataSet::testFooData() { DataSet dataSet(0); dataSet.setLabelDataRegion(CellRegion(m_table1, QPoint(1, 2))); dataSet.setCategoryDataRegion(CellRegion(m_table1, QRect(2, 1, 4, 1))); dataSet.setXDataRegion(CellRegion(m_table1, QRect(2, 2, 4, 1))); dataSet.setYDataRegion(CellRegion(m_table1, QRect(2, 3, 4, 1))); dataSet.setCustomDataRegion(CellRegion(m_table1, QRect(2, 4, 4, 1))); QCOMPARE(dataSet.size(), 4); QCOMPARE(dataSet.labelData(), QVariant("Row 1")); QCOMPARE(dataSet.categoryData(0), QVariant("Column 1")); QCOMPARE(dataSet.categoryData(1), QVariant("Column 2")); QCOMPARE(dataSet.categoryData(2), QVariant("Column 3")); QCOMPARE(dataSet.categoryData(3), QVariant("Column 4")); QCOMPARE(dataSet.xData(0), QVariant(7.2)); QCOMPARE(dataSet.xData(1), QVariant(1.8)); QCOMPARE(dataSet.xData(2), QVariant(9.4)); QCOMPARE(dataSet.xData(3), QVariant(1.5)); QCOMPARE(dataSet.yData(0), QVariant(8.4)); QCOMPARE(dataSet.yData(1), QVariant(2.9)); QCOMPARE(dataSet.yData(2), QVariant(3.7)); QCOMPARE(dataSet.yData(3), QVariant(5.5)); QCOMPARE(dataSet.customData(0), QVariant(2.9)); QCOMPARE(dataSet.customData(1), QVariant(5.3)); QCOMPARE(dataSet.customData(2), QVariant(6.4)); QCOMPARE(dataSet.customData(3), QVariant(2.1)); } void TestDataSet::testFooDataMultipleTables() { DataSet dataSet(0); dataSet.setLabelDataRegion(CellRegion(m_table1, QPoint(1, 2))); dataSet.setCategoryDataRegion(CellRegion(m_table1, QRect(2, 1, 4, 1))); dataSet.setXDataRegion(CellRegion(m_table2, QRect(2, 2, 4, 1))); dataSet.setYDataRegion(CellRegion(m_table1, QRect(2, 3, 4, 1))); dataSet.setCustomDataRegion(CellRegion(m_table2, QRect(2, 4, 4, 1))); QCOMPARE(dataSet.size(), 4); QCOMPARE(dataSet.labelData(), QVariant("Row 1")); QCOMPARE(dataSet.categoryData(0), QVariant("Column 1")); QCOMPARE(dataSet.categoryData(1), QVariant("Column 2")); QCOMPARE(dataSet.categoryData(2), QVariant("Column 3")); QCOMPARE(dataSet.categoryData(3), QVariant("Column 4")); QCOMPARE(dataSet.xData(0), QVariant(1)); QCOMPARE(dataSet.xData(1), QVariant(2)); QCOMPARE(dataSet.xData(2), QVariant(3)); QCOMPARE(dataSet.xData(3), QVariant(4)); QCOMPARE(dataSet.yData(0), QVariant(8.4)); QCOMPARE(dataSet.yData(1), QVariant(2.9)); QCOMPARE(dataSet.yData(2), QVariant(3.7)); QCOMPARE(dataSet.yData(3), QVariant(5.5)); QCOMPARE(dataSet.customData(0), QVariant(9)); QCOMPARE(dataSet.customData(1), QVariant(10)); QCOMPARE(dataSet.customData(2), QVariant(11)); QCOMPARE(dataSet.customData(3), QVariant(12)); } QTEST_MAIN(TestDataSet) diff --git a/plugins/chartshape/tests/TestProxyModel.cpp b/plugins/chartshape/tests/TestProxyModel.cpp index d6045d49339..74e82b648e1 100644 --- a/plugins/chartshape/tests/TestProxyModel.cpp +++ b/plugins/chartshape/tests/TestProxyModel.cpp @@ -1,603 +1,603 @@ /* This file is part of the KDE project Copyright 2008, 2010 Johannes Simon 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 "TestProxyModel.h" // Qt #include #include #include #include #include #include // KChart #include "DataSet.h" #include "CellRegion.h" namespace QTest { template<> char *toString(const CellRegion ®ion) { - return qstrdup(region.toString().toAscii().data()); + return qstrdup(region.toString().toLatin1()); } } using namespace KChart; TestProxyModel::TestProxyModel() : m_source() , m_proxyModel(0, &m_source) , m_sourceModel() , m_table(0) { } void TestProxyModel::init() { m_source.clear(); m_table = m_source.add("Table1", &m_sourceModel); m_sourceModel.setRowCount(4); m_sourceModel.setColumnCount(5); // Vertical header data m_sourceModel.setData(m_sourceModel.index(1, 0), "Row 1"); m_sourceModel.setData(m_sourceModel.index(2, 0), "Row 2"); m_sourceModel.setData(m_sourceModel.index(3, 0), "Row 3"); // Horizontal header data m_sourceModel.setData(m_sourceModel.index(0, 1), "Column 1"); m_sourceModel.setData(m_sourceModel.index(0, 2), "Column 2"); m_sourceModel.setData(m_sourceModel.index(0, 3), "Column 3"); m_sourceModel.setData(m_sourceModel.index(0, 4), "Column 4"); // First row m_sourceModel.setData(m_sourceModel.index(1, 1), 7.2); m_sourceModel.setData(m_sourceModel.index(1, 2), 1.8); m_sourceModel.setData(m_sourceModel.index(1, 3), 9.4); m_sourceModel.setData(m_sourceModel.index(1, 4), 1.5); // Second row m_sourceModel.setData(m_sourceModel.index(2, 1), 8.4); m_sourceModel.setData(m_sourceModel.index(2, 2), 2.9); m_sourceModel.setData(m_sourceModel.index(2, 3), 3.7); m_sourceModel.setData(m_sourceModel.index(2, 4), 5.5); // Third row m_sourceModel.setData(m_sourceModel.index(3, 1), 2.9); m_sourceModel.setData(m_sourceModel.index(3, 2), 5.3); m_sourceModel.setData(m_sourceModel.index(3, 3), 6.4); m_sourceModel.setData(m_sourceModel.index(3, 4), 2.1); QRect selection(QPoint(1, 1), QSize(m_sourceModel.columnCount(), m_sourceModel.rowCount())); m_proxyModel.reset(CellRegion(m_table, selection)); } void TestProxyModel::testWithoutLabels() { QList dataSets; // Horizontal data direction m_proxyModel.setDataDirection(Qt::Horizontal); m_proxyModel.setFirstColumnIsLabel(false); m_proxyModel.setFirstRowIsLabel(false); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 4); QCOMPARE(dataSets[0]->size(), 5); QCOMPARE(dataSets[1]->size(), 5); QCOMPARE(dataSets[2]->size(), 5); QCOMPARE(dataSets[3]->size(), 5); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 5, 1))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(1, 3, 5, 1))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(1, 4, 5, 1))); QCOMPARE(dataSets[3]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion()); // Vertical data direction m_proxyModel.setDataDirection(Qt::Vertical); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 5); QCOMPARE(dataSets[0]->size(), 4); QCOMPARE(dataSets[1]->size(), 4); QCOMPARE(dataSets[2]->size(), 4); QCOMPARE(dataSets[3]->size(), 4); QCOMPARE(dataSets[4]->size(), 4); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 4))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 4))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 4))); QCOMPARE(dataSets[3]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->yDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 4))); QCOMPARE(dataSets[4]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->categoryDataRegion(), CellRegion()); } void TestProxyModel::testFirstRowAsLabel() { QList dataSets; // Horizontal data direction m_proxyModel.setDataDirection(Qt::Horizontal); m_proxyModel.setFirstColumnIsLabel(false); // With first row as category data m_proxyModel.setFirstRowIsLabel(true); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 3); QCOMPARE(dataSets[0]->size(), 5); QCOMPARE(dataSets[1]->size(), 5); QCOMPARE(dataSets[2]->size(), 5); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 5, 1))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1))); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 3, 5, 1))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1))); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(1, 4, 5, 1))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1))); // Vertical data direction m_proxyModel.setDataDirection(Qt::Vertical); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 5); QCOMPARE(dataSets[0]->size(), 3); QCOMPARE(dataSets[1]->size(), 3); QCOMPARE(dataSets[2]->size(), 3); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 1))); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 1, 3))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 1))); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(3, 2, 1, 3))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 1))); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(4, 2, 1, 3))); QCOMPARE(dataSets[3]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 1))); QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->yDataRegion(), CellRegion(m_table, QRect(5, 2, 1, 3))); QCOMPARE(dataSets[4]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->labelDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 1))); QCOMPARE(dataSets[4]->categoryDataRegion(), CellRegion()); } void TestProxyModel::testFirstColumnAsLabel() { QList dataSets; // Horizontal data direction m_proxyModel.setDataDirection(Qt::Horizontal); m_proxyModel.setFirstRowIsLabel(false); // With first column as label data m_proxyModel.setFirstColumnIsLabel(true); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 4); QCOMPARE(dataSets[0]->size(), 4); QCOMPARE(dataSets[1]->size(), 4); QCOMPARE(dataSets[2]->size(), 4); QCOMPARE(dataSets[3]->size(), 4); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 1))); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 4, 1))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 1))); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(2, 3, 4, 1))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(1, 3, 1, 1))); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(2, 4, 4, 1))); QCOMPARE(dataSets[3]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion(m_table, QRect(1, 4, 1, 1))); QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion()); // Vertical data direction m_proxyModel.setDataDirection(Qt::Vertical); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 4); QCOMPARE(dataSets[0]->size(), 4); QCOMPARE(dataSets[1]->size(), 4); QCOMPARE(dataSets[2]->size(), 4); QCOMPARE(dataSets[3]->size(), 4); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 4))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4))); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 4))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4))); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 4))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4))); QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 4))); QCOMPARE(dataSets[3]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4))); } void TestProxyModel::testFirstRowAndColumnAsLabels() { QList dataSets; // Horizontal data direction m_proxyModel.setDataDirection(Qt::Horizontal); // With first row as category data m_proxyModel.setFirstRowIsLabel(true); // ...and first column as label data m_proxyModel.setFirstColumnIsLabel(true); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 3); QCOMPARE(dataSets[0]->size(), 4); QCOMPARE(dataSets[1]->size(), 4); QCOMPARE(dataSets[2]->size(), 4); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 4, 1))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 1))); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1))); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 3, 4, 1))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(1, 3, 1, 1))); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1))); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(2, 4, 4, 1))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(1, 4, 1, 1))); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1))); // Vertical data direction m_proxyModel.setDataDirection(Qt::Vertical); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 4); QCOMPARE(dataSets[0]->size(), 3); QCOMPARE(dataSets[1]->size(), 3); QCOMPARE(dataSets[2]->size(), 3); QCOMPARE(dataSets[3]->size(), 3); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 1, 3))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 1))); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3))); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(3, 2, 1, 3))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 1))); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3))); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(4, 2, 1, 3))); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 1))); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3))); QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(5, 2, 1, 3))); QCOMPARE(dataSets[3]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 1))); QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3))); } void TestProxyModel::testRegionOrder() { CellRegion selection(m_table); // Second row selection.add(QRect(1, 2, 4, 1)); // First row selection.add(QRect(1, 1, 4, 1)); m_proxyModel.setDataDirection(Qt::Horizontal); m_proxyModel.setFirstColumnIsLabel(false); m_proxyModel.setFirstRowIsLabel(false); m_proxyModel.reset(selection); QList dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 2); QCOMPARE(dataSets[0]->size(), 4); QCOMPARE(dataSets[1]->size(), 4); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 1, 4, 1))); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 4, 1))); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); } // Random test case with results from Open Office void TestProxyModel::testComplexRegions() { CellRegion selection(&m_source, "Table1.C2:D3;Table1.D9:F10;Table1.E4:F4;Table1.E6:E7"); m_proxyModel.setDataDirection(Qt::Horizontal); m_proxyModel.setFirstColumnIsLabel(false); m_proxyModel.setFirstRowIsLabel(false); m_proxyModel.reset(selection); QList dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 7); QCOMPARE(dataSets[0]->size(), 2); QCOMPARE(dataSets[1]->size(), 2); QCOMPARE(dataSets[2]->size(), 2); QCOMPARE(dataSets[3]->size(), 1); QCOMPARE(dataSets[4]->size(), 1); QCOMPARE(dataSets[5]->size(), 3); QCOMPARE(dataSets[6]->size(), 3); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(&m_source, "Table1.C2:D2")); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(&m_source, "Table1.C3:D3")); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(&m_source, "Table1.E4:F4")); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(&m_source, "Table1.E6")); QCOMPARE(dataSets[3]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->yDataRegion(), CellRegion(&m_source, "Table1.E7")); QCOMPARE(dataSets[4]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[4]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[5]->yDataRegion(), CellRegion(&m_source, "Table1.D9:F9")); QCOMPARE(dataSets[5]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[5]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[5]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[5]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[6]->yDataRegion(), CellRegion(&m_source, "Table1.D10:F10")); QCOMPARE(dataSets[6]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[6]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[6]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[6]->categoryDataRegion(), CellRegion()); m_proxyModel.setDataDirection(Qt::Vertical); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 4); QCOMPARE(dataSets[0]->size(), 2); QCOMPARE(dataSets[1]->size(), 4); QCOMPARE(dataSets[2]->size(), 5); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(&m_source, "Table1.C2:C3")); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(&m_source, "Table1.D2:D3;$Table1.D9:D10")); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(&m_source, "Table1.E4;Table1.E6:E7;Table1.E9:E10")); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion()); } void TestProxyModel::testTwoDimensions() { QList dataSets; // Horizontal data direction m_proxyModel.setDataDirection(Qt::Horizontal); m_proxyModel.setFirstColumnIsLabel(false); m_proxyModel.setFirstRowIsLabel(false); m_proxyModel.setDataDimensions(2); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 3); QCOMPARE(dataSets[0]->size(), 5); QCOMPARE(dataSets[1]->size(), 5); QCOMPARE(dataSets[2]->size(), 5); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1))); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 5, 1))); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1))); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 3, 5, 1))); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->xDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1))); QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(1, 4, 5, 1))); QCOMPARE(dataSets[2]->customDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion()); } void TestProxyModel::testThreeDimensions() { QList dataSets; // Horizontal data direction m_proxyModel.setDataDirection(Qt::Horizontal); m_proxyModel.setFirstColumnIsLabel(false); m_proxyModel.setFirstRowIsLabel(false); m_proxyModel.setDataDimensions(3); dataSets = m_proxyModel.dataSets(); QCOMPARE(dataSets.size(), 2); QCOMPARE(dataSets[0]->size(), 5); QCOMPARE(dataSets[1]->size(), 5); QCOMPARE(dataSets[0]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(&m_source, "Table1.$A$1:$E$1")); QCOMPARE(dataSets[0]->customDataRegion(), CellRegion(&m_source, "Table1.$A$2:$E$2")); QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->xDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(&m_source, "Table1.$A$3:$E$3")); QCOMPARE(dataSets[1]->customDataRegion(), CellRegion(&m_source, "Table1.$A$4:$E$4")); QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion()); QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion()); } QTEST_MAIN(TestProxyModel) diff --git a/plugins/chartshape/tests/odf/TestLoadingBase.cpp b/plugins/chartshape/tests/odf/TestLoadingBase.cpp index ede37aa0f5e..529c4460a6f 100644 --- a/plugins/chartshape/tests/odf/TestLoadingBase.cpp +++ b/plugins/chartshape/tests/odf/TestLoadingBase.cpp @@ -1,212 +1,212 @@ /* This file is part of the KDE project Copyright 2010 Johannes Simon Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). Contact: Suresh Chande suresh.chande@nokia.com 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 "TestLoadingBase.h" // Qt #include // KDE #include // Calligra #include #include #include // KChart #include "ChartShape.h" #include "ChartProxyModel.h" #include "ChartDocument.h" #include "PlotArea.h" #include "Legend.h" #include "DataSet.h" #include "TableSource.h" #include "Axis.h" // KD Chart #include #include using namespace KChart; TestLoadingBase::TestLoadingBase() : QObject() { // No message boxes please. ChartShape::setEnableUserInteraction(false); m_chart = new ChartShape(0); } void TestLoadingBase::initTestCase() { ChartDocument document(m_chart); QString srcdirname(KDESRCDIR); QVERIFY(!srcdirname.isEmpty()); QDir srcdir(srcdirname); QVERIFY(srcdir.exists()); bool hasDocDirInSrcDir = srcdir.cd("doc"); QVERIFY(hasDocDirInSrcDir); // Go back up, we only used the cd as a test. if (hasDocDirInSrcDir) srcdir.cd(".."); KoStore *store = KoStore::createStore(srcdir.absolutePath(), KoStore::Read); QVERIFY(store->enterDirectory("doc")); QString errorMsg; KoOdfReadStore odfReadStore(store); bool success = odfReadStore.loadAndParse(errorMsg); if (!success) qDebug() << "Error in odfReadStore.loadAndParse(): " << errorMsg; QVERIFY(success); QVERIFY(document.loadOdf(odfReadStore)); } void TestLoadingBase::testElementIsVisible(KoShape *element, bool shouldBeVisible) { QVERIFY(element); QCOMPARE(element->isVisible(), shouldBeVisible); } void TestLoadingBase::testLegendElements(QStringList labels) { QVERIFY(m_chart->legend()); QVERIFY(m_chart->legend()->kdLegend()); QCOMPARE(m_chart->legend()->kdLegend()->datasetCount(), (unsigned int)labels.count()); QList diagrams = m_chart->legend()->kdLegend()->diagrams(); foreach(KDChart::AbstractDiagram *diagram, diagrams) { QVERIFY(diagram); QStringList diagramLabels = diagram->datasetLabels(); foreach(QString diagramLabel, diagramLabels) { QVERIFY(!labels.isEmpty()); QCOMPARE(diagramLabel, labels.takeFirst()); } } QVERIFY(labels.isEmpty()); } void TestLoadingBase::testDataSetCellRegions(int dataSetNr, CellRegion yDataRegion, CellRegion labelDataRegion, CellRegion categoryDataRegion, CellRegion xDataRegion, CellRegion customDataRegion) { QVERIFY(m_chart->proxyModel()); QList dataSets = m_chart->proxyModel()->dataSets(); QVERIFY(dataSetNr >= 0); QVERIFY(dataSets.count() > dataSetNr); DataSet *dataSet = dataSets[dataSetNr]; QVERIFY(dataSet); int dataSetSize = 0; dataSetSize = qMax(dataSetSize, yDataRegion.cellCount()); dataSetSize = qMax(dataSetSize, categoryDataRegion.cellCount()); dataSetSize = qMax(dataSetSize, xDataRegion.cellCount()); dataSetSize = qMax(dataSetSize, customDataRegion.cellCount()); QCOMPARE(dataSet->size(), dataSetSize); QCOMPARE(dataSet->xDataRegion(), xDataRegion); QCOMPARE(dataSet->yDataRegion(), yDataRegion); QCOMPARE(dataSet->labelDataRegion(), labelDataRegion); QCOMPARE(dataSet->categoryDataRegion(), categoryDataRegion); QCOMPARE(dataSet->customDataRegion(), customDataRegion); } void TestLoadingBase::testHasOnlyInternalTable() { QVERIFY(m_chart->usesInternalModelOnly()); QVERIFY(internalTable()); } void TestLoadingBase::testInternalTableSize(int rowCount, int colCount) { QAbstractItemModel *model = m_chart->internalModel(); QVERIFY(model); QVERIFY(m_chart->tableSource()->get(model)); QCOMPARE(model->rowCount(), rowCount); QCOMPARE(model->columnCount(), colCount); } void TestLoadingBase::testTitleText(const QString &text) { QVERIFY(m_chart->title()); KoTextShapeDataBase *data = dynamic_cast(m_chart->title()->userData()); QVERIFY(data); QVERIFY(data->document()); QCOMPARE(data->document()->toPlainText(), text); } void TestLoadingBase::testSubTitleText(const QString &text) { QVERIFY(m_chart->subTitle()); KoTextShapeDataBase *data = dynamic_cast(m_chart->subTitle()->userData()); QVERIFY(data); QVERIFY(data->document()); QCOMPARE(data->document()->toPlainText(), text); } void TestLoadingBase::testFooterText(const QString &text) { QVERIFY(m_chart->footer()); KoTextShapeDataBase *data = dynamic_cast(m_chart->footer()->userData()); QVERIFY(data); QVERIFY(data->document()); QCOMPARE(data->document()->toPlainText(), text); } void TestLoadingBase::testAxisTitle(Axis *axis, const QString &text) { QVERIFY(axis); QVERIFY(axis->title()); KoTextShapeDataBase *data = dynamic_cast(axis->title()->userData()); QVERIFY(data); QVERIFY(data->document()); QCOMPARE(data->document()->toPlainText(), text); } Table *TestLoadingBase::internalTable() { QAbstractItemModel *internalModel = m_chart->internalModel(); if (!internalModel) return 0; return m_chart->tableSource()->get(internalModel); } TableSource *TestLoadingBase::tableSource() { return m_chart->tableSource(); } namespace QTest { template<> char *toString(const KChart::CellRegion ®ion) { - return qstrdup(region.toString().toAscii().data()); + return qstrdup(region.toString().toLatin1()); } } diff --git a/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp b/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp index ef8f5e6f689..ec6fe5aa32f 100644 --- a/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp +++ b/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp @@ -1,225 +1,225 @@ /* * Copyright (c) 2007-2008 Cyrille Berger * Copyright (c) 2011 Srikanth Tiyyagura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "IccColorSpaceEngine.h" #include "KoColorModelStandardIds.h" #include #include #include "LcmsColorSpace.h" #include "DebugPigment.h" // -- KoLcmsColorConversionTransformation -- class KoLcmsColorConversionTransformation : public KoColorConversionTransformation { public: KoLcmsColorConversionTransformation(const KoColorSpace* srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer* srcProfile, const KoColorSpace* dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer* dstProfile, Intent renderingIntent, ConversionFlags conversionFlags) : KoColorConversionTransformation(srcCs, dstCs, renderingIntent, conversionFlags) , m_transform(0) { Q_ASSERT(srcCs); Q_ASSERT(dstCs); Q_ASSERT(renderingIntent < 4); if (srcCs->colorDepthId() == Integer8BitsColorDepthID || srcCs->colorDepthId() == Integer16BitsColorDepthID) { if ((srcProfile->name().toLower().contains("linear") || dstProfile->name().toLower().contains("linear")) && !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization) ) { conversionFlags |= KoColorConversionTransformation::NoOptimization; } } m_transform = cmsCreateTransform(srcProfile->lcmsProfile(), srcColorSpaceType, dstProfile->lcmsProfile(), dstColorSpaceType, renderingIntent, conversionFlags); Q_ASSERT(m_transform); } ~KoLcmsColorConversionTransformation() { cmsDeleteTransform(m_transform); } public: virtual void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const { Q_ASSERT(m_transform); qint32 srcPixelSize = srcColorSpace()->pixelSize(); qint32 dstPixelSize = dstColorSpace()->pixelSize(); cmsDoTransform(m_transform, const_cast(src), dst, numPixels); // Lcms does nothing to the destination alpha channel so we must convert that manually. while (numPixels > 0) { qreal alpha = srcColorSpace()->opacityF(src); dstColorSpace()->setOpacity(dst, alpha, 1); src += srcPixelSize; dst += dstPixelSize; numPixels--; } } private: mutable cmsHTRANSFORM m_transform; }; struct IccColorSpaceEngine::Private { }; IccColorSpaceEngine::IccColorSpaceEngine() : KoColorSpaceEngine("icc", i18n("ICC Engine")), d(new Private) { } IccColorSpaceEngine::~IccColorSpaceEngine() { delete d; } void IccColorSpaceEngine::addProfile(const QString &filename) { KoColorSpaceRegistry* registry = KoColorSpaceRegistry::instance(); KoColorProfile *profile = new IccColorProfile(filename); Q_CHECK_PTR(profile); // this our own loading code; sometimes it fails because of an lcms error profile->load(); // and then lcms can read the profile from file itself without problems, // quite often, and we can initialize it if (!profile->valid()) { - cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toAscii(), "r"); + cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r"); profile = LcmsColorProfileContainer::createFromLcmsProfile(cmsp); } if (profile->valid()) { kDebug(31000) << "Valid profile : " << profile->fileName() << profile->name(); registry->addProfile(profile); } else { kDebug(31000) << "Invalid profile : " << profile->fileName() << profile->name(); delete profile; } } void IccColorSpaceEngine::removeProfile(const QString &filename) { KoColorSpaceRegistry* registry = KoColorSpaceRegistry::instance(); KoColorProfile *profile = new IccColorProfile(filename); Q_CHECK_PTR(profile); profile->load(); if (profile->valid() && registry->profileByName(profile->name())) { registry->removeProfile(profile); } } KoColorConversionTransformation* IccColorSpaceEngine::createColorTransformation(const KoColorSpace* srcColorSpace, const KoColorSpace* dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_ASSERT(srcColorSpace); Q_ASSERT(dstColorSpace); return new KoLcmsColorConversionTransformation( srcColorSpace, computeColorSpaceType(srcColorSpace), dynamic_cast(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace), dynamic_cast(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags); } quint32 IccColorSpaceEngine::computeColorSpaceType(const KoColorSpace* cs) const { Q_ASSERT(cs); if (const KoLcmsInfo *lcmsInfo = dynamic_cast(cs)) { return lcmsInfo->colorSpaceType(); } else { QString modelId = cs->colorModelId().id(); QString depthId = cs->colorDepthId().id(); // Compute the depth part of the type quint32 depthType; if (depthId == Integer8BitsColorDepthID.id()) { depthType = BYTES_SH(1); } else if (depthId == Integer16BitsColorDepthID.id()) { depthType = BYTES_SH(2); } else if (depthId == Float16BitsColorDepthID.id()) { depthType = BYTES_SH(2); } else if (depthId == Float32BitsColorDepthID.id()) { depthType = BYTES_SH(4); } else if (depthId == Float64BitsColorDepthID.id()) { depthType = BYTES_SH(0); } else { dbgPigmentCS << "Unknow bit depth"; return 0; } // Compute the model part of the type quint32 modelType = 0; if (modelId == RGBAColorModelID.id()) { if (depthId.startsWith("U")) { modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1)); } else if (depthId.startsWith("F")) { modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) ); } } else if (modelId == XYZAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_XYZ) | EXTRA_SH(1) | CHANNELS_SH(3)); } else if (modelId == LABAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_Lab) | EXTRA_SH(1) | CHANNELS_SH(3)); } else if (modelId == CMYKAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_CMYK) | EXTRA_SH(1) | CHANNELS_SH(4)); } else if (modelId == GrayAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_GRAY) | EXTRA_SH(1) | CHANNELS_SH(1)); } else if (modelId == GrayColorModelID.id()) { modelType = (COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1)); } else if (modelId == YCbCrAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_YCbCr) | EXTRA_SH(1) | CHANNELS_SH(3)); } else { warnPigment << "Cannot convert colorspace to lcms modeltype"; return 0; } return depthType | modelType; } } diff --git a/plugins/colorengines/lcms2/colorprofiles/LcmsColorProfileContainer.cpp b/plugins/colorengines/lcms2/colorprofiles/LcmsColorProfileContainer.cpp index 393db27c383..35f88abbb27 100644 --- a/plugins/colorengines/lcms2/colorprofiles/LcmsColorProfileContainer.cpp +++ b/plugins/colorengines/lcms2/colorprofiles/LcmsColorProfileContainer.cpp @@ -1,304 +1,304 @@ /* * This file is part of the KDE project * Copyright (c) 2000 Matthias Elter * 2001 John Califf * 2004 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * Copyright (c) 2007 Adrian Page * * 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 "LcmsColorProfileContainer.h" #include #include #include "DebugPigment.h" #include "KoChromaticities.h" class LcmsColorProfileContainer::Private { public: Private() : valid(false), suitableForOutput(false) { } cmsHPROFILE profile; cmsColorSpaceSignature colorSpaceSignature; cmsProfileClassSignature deviceClass; QString productDescription; QString manufacturer; QString name; IccColorProfile::Data * data; bool valid; bool suitableForOutput; }; LcmsColorProfileContainer::LcmsColorProfileContainer() : d(new Private()) { d->profile = 0; } LcmsColorProfileContainer::LcmsColorProfileContainer(IccColorProfile::Data * data) : d(new Private()) { d->data = data; d->profile = 0; init(); } QByteArray LcmsColorProfileContainer::lcmsProfileToByteArray(const cmsHPROFILE profile) { cmsUInt32Number bytesNeeded = 0; // Make a raw data image ready for saving cmsSaveProfileToMem(profile, 0, &bytesNeeded); // calc size QByteArray rawData; rawData.resize(bytesNeeded); if (rawData.size() >= (int)bytesNeeded) { cmsSaveProfileToMem(profile, rawData.data(), &bytesNeeded); // fill buffer } else { errorPigment << "Couldn't resize the profile buffer, system is probably running out of memory."; rawData.resize(0); } return rawData; } IccColorProfile* LcmsColorProfileContainer::createFromLcmsProfile(const cmsHPROFILE profile) { IccColorProfile* iccprofile = new IccColorProfile(lcmsProfileToByteArray(profile)); cmsCloseProfile(profile); return iccprofile; } #define lcmsToPigmentViceVersaStructureCopy(dst, src ) \ dst .x = src .x; \ dst .y = src .y; \ dst .Y = src .Y; QByteArray LcmsColorProfileContainer::createFromChromacities(const KoRGBChromaticities& _chromacities, qreal gamma, QString _profileName) { cmsCIExyYTRIPLE primaries; cmsCIExyY whitePoint; lcmsToPigmentViceVersaStructureCopy(primaries.Red, _chromacities.primaries.Red); lcmsToPigmentViceVersaStructureCopy(primaries.Green, _chromacities.primaries.Green); lcmsToPigmentViceVersaStructureCopy(primaries.Blue, _chromacities.primaries.Blue); lcmsToPigmentViceVersaStructureCopy(whitePoint, _chromacities.whitePoint); cmsToneCurve* gammaTable = cmsBuildGamma(0, gamma); const int numTransferFunctions = 3; cmsToneCurve* transferFunctions[numTransferFunctions]; for (int i = 0; i < numTransferFunctions; ++i) { transferFunctions[i] = gammaTable; } cmsHPROFILE profile = cmsCreateRGBProfile(&whitePoint, &primaries, transferFunctions); QString name = _profileName; if (name.isEmpty()) { name = QString("lcms virtual RGB profile - R(%1, %2) G(%3, %4) B(%5, %6) W(%7, %8) gamma %9") .arg(primaries.Red.x) .arg(primaries.Red.y) .arg(primaries.Green.x) .arg(primaries.Green.y) .arg(primaries.Blue.x) .arg(primaries.Blue.y) .arg(whitePoint.x) .arg(whitePoint.y) .arg(gamma); } // icSigProfileDescriptionTag is the compulsory tag and is the profile name // displayed by other applications. - cmsWriteTag(profile, cmsSigProfileDescriptionTag, name.toLatin1().data()); + cmsWriteTag(profile, cmsSigProfileDescriptionTag, name.toLatin1().constData()); - cmsWriteTag(profile, cmsSigDeviceModelDescTag, name.toLatin1().data()); + cmsWriteTag(profile, cmsSigDeviceModelDescTag, name.toLatin1().constData()); // Clear the default manufacturer's tag that is set to "(lcms internal)" QByteArray ba(""); cmsWriteTag(profile, cmsSigDeviceMfgDescTag, ba.data()); cmsFreeToneCurve(gammaTable); QByteArray profileArray = lcmsProfileToByteArray(profile); cmsCloseProfile(profile); return profileArray; } LcmsColorProfileContainer::~LcmsColorProfileContainer() { cmsCloseProfile(d->profile); delete d; } #define _BUFFER_SIZE_ 1000 bool LcmsColorProfileContainer::init() { if (d->profile) cmsCloseProfile(d->profile); d->profile = cmsOpenProfileFromMem((void*)d->data->rawData().constData(), d->data->rawData().size()); #ifndef NDEBUG if (d->data->rawData().size() == 4096) { warnPigment << "Profile has a size of 4096, which is suspicious and indicates a possible misuse of QIODevice::read(int), check your code."; } #endif if (d->profile) { wchar_t buffer[_BUFFER_SIZE_]; d->colorSpaceSignature = cmsGetColorSpace(d->profile); d->deviceClass = cmsGetDeviceClass(d->profile); cmsGetProfileInfo(d->profile, cmsInfoDescription, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->productDescription = QString::fromWCharArray(buffer); d->valid = true; cmsGetProfileInfo(d->profile, cmsInfoModel, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->name = QString::fromWCharArray(buffer); cmsGetProfileInfo(d->profile, cmsInfoManufacturer, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->manufacturer = QString::fromWCharArray(buffer); // Check if the profile can convert (something->this) d->suitableForOutput = cmsIsMatrixShaper(d->profile) || ( cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT) && cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT) ); return true; } return false; } cmsHPROFILE LcmsColorProfileContainer::lcmsProfile() const { #if 0 if (d->profile = 0) { QFile file(d->filename); file.open(QIODevice::ReadOnly); d->rawData = file.readAll(); d->profile = cmsOpenProfileFromMem((void*)d->rawData.constData(), (DWORD)d->rawData.size()); file.close(); } #endif return d->profile; } cmsColorSpaceSignature LcmsColorProfileContainer::colorSpaceSignature() const { return d->colorSpaceSignature; } cmsProfileClassSignature LcmsColorProfileContainer::deviceClass() const { return d->deviceClass; } QString LcmsColorProfileContainer::manufacturer() const { return d->manufacturer; } bool LcmsColorProfileContainer::valid() const { return d->valid; } bool LcmsColorProfileContainer::isSuitableForOutput() const { return d->suitableForOutput; } bool LcmsColorProfileContainer::isSuitableForPrinting() const { return deviceClass() == cmsSigOutputClass; } bool LcmsColorProfileContainer::isSuitableForDisplay() const { return deviceClass() == cmsSigDisplayClass; } QString LcmsColorProfileContainer::name() const { return d->name; } QString LcmsColorProfileContainer::info() const { return d->productDescription; } static KoCIExyY RGB2xyY(cmsHPROFILE RGBProfile, qreal red, qreal green, qreal blue) { cmsHPROFILE XYZProfile = cmsCreateXYZProfile(); const cmsUInt32Number inputFormat = TYPE_RGB_DBL; const cmsUInt32Number outputFormat = TYPE_XYZ_DBL; const cmsUInt32Number transformFlags = cmsFLAGS_LOWRESPRECALC; cmsHTRANSFORM transform = cmsCreateTransform(RGBProfile, inputFormat, XYZProfile, outputFormat, INTENT_ABSOLUTE_COLORIMETRIC, transformFlags); struct XYZPixel { qreal X; qreal Y; qreal Z; }; struct RGBPixel { qreal red; qreal green; qreal blue; }; XYZPixel xyzPixel; RGBPixel rgbPixel; rgbPixel.red = red; rgbPixel.green = green; rgbPixel.blue = blue; const unsigned int numPixelsToTransform = 1; cmsDoTransform(transform, &rgbPixel, &xyzPixel, numPixelsToTransform); cmsCIEXYZ xyzPixelXYZ; xyzPixelXYZ.X = xyzPixel.X; xyzPixelXYZ.Y = xyzPixel.Y; xyzPixelXYZ.Z = xyzPixel.Z; cmsCIExyY xyzPixelxyY; cmsXYZ2xyY(&xyzPixelxyY, &xyzPixelXYZ); cmsDeleteTransform(transform); cmsCloseProfile(XYZProfile); KoCIExyY res; lcmsToPigmentViceVersaStructureCopy(res, xyzPixelxyY); return res; } KoRGBChromaticities* LcmsColorProfileContainer::chromaticitiesFromProfile() const { if (cmsGetColorSpace(d->profile) != cmsSigRgbData) return 0; KoRGBChromaticities* chromaticities = new KoRGBChromaticities(); chromaticities->primaries.Red = RGB2xyY(d->profile, 1.0f, 0.0f, 0.0f); chromaticities->primaries.Green = RGB2xyY(d->profile, 0.0f, 1.0f, 0.0f); chromaticities->primaries.Blue = RGB2xyY(d->profile, 0.0f, 0.0f, 1.0f); chromaticities->whitePoint = RGB2xyY(d->profile, 1.0f, 1.0f, 1.0f); return chromaticities; } diff --git a/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp b/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp index a00afe87007..612da3100fa 100644 --- a/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp +++ b/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp @@ -1,97 +1,97 @@ #include "TestKoColorSpaceRegistry.h" #include #include "KoColorSpaceRegistry.h" #include "KoColorSpace.h" #include "RgbU8ColorSpace.h" #include "RgbU16ColorSpace.h" #include "LabColorSpace.h" void TestKoColorSpaceRegistry::testConstruction() { KoColorSpaceRegistry* instance = KoColorSpaceRegistry::instance(); Q_ASSERT(instance); } void TestKoColorSpaceRegistry::testRgbU8() { QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(RGBAColorModelID, Integer8BitsColorDepthID); const KoColorSpaceFactory *colorSpaceFactory = KoColorSpaceRegistry::instance()->colorSpaceFactory(colorSpaceId); QVERIFY(colorSpaceFactory != 0); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8(); QVERIFY(colorSpace != 0); const KoColorProfile *profile = colorSpace->profile(); QVERIFY(profile != 0); QCOMPARE(profile->name(), colorSpaceFactory->defaultProfile()); cmsHPROFILE lcmsProfile = cmsCreate_sRGBProfile(); QString testProfileName = "TestRGBU8ProfileName"; - cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().data()); - cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().data()); + cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().constData()); + cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().constData()); cmsWriteTag(lcmsProfile, cmsSigDeviceMfgDescTag, ""); } void TestKoColorSpaceRegistry::testRgbU16() { QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(RGBAColorModelID, Integer16BitsColorDepthID); const KoColorSpaceFactory *colorSpaceFactory = KoColorSpaceRegistry::instance()->colorSpaceFactory(colorSpaceId); QVERIFY(colorSpaceFactory != 0); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb16(); QVERIFY(colorSpace != 0); const KoColorProfile *profile = colorSpace->profile(); QVERIFY(profile != 0); QCOMPARE(profile->name(), colorSpaceFactory->defaultProfile()); cmsHPROFILE lcmsProfile = cmsCreate_sRGBProfile(); QString testProfileName = "TestRGBU16ProfileName"; - cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().data()); - cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().data()); + cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().constData()); + cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().constData()); cmsWriteTag(lcmsProfile, cmsSigDeviceMfgDescTag, ""); } void TestKoColorSpaceRegistry::testLab() { QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(LABAColorModelID, Integer16BitsColorDepthID); const KoColorSpaceFactory *colorSpaceFactory = KoColorSpaceRegistry::instance()->colorSpaceFactory(colorSpaceId); QVERIFY(colorSpaceFactory != 0); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->lab16(); QVERIFY(colorSpace != 0); const KoColorProfile *profile = colorSpace->profile(); QVERIFY(profile != 0); QCOMPARE(profile->name(), colorSpaceFactory->defaultProfile()); cmsCIExyY whitepoint; whitepoint.x = 0.33; whitepoint.y = 0.33; whitepoint.Y = 1.0; cmsHPROFILE lcmsProfile = cmsCreateLab2Profile(&whitepoint); QString testProfileName = "TestLabProfileName"; - cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().data()); - cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().data()); + cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().constData()); + cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().constData()); cmsWriteTag(lcmsProfile, cmsSigDeviceMfgDescTag, ""); } QTEST_KDEMAIN(TestKoColorSpaceRegistry, NoGUI) #include diff --git a/plugins/formulashape/Dictionary.cpp b/plugins/formulashape/Dictionary.cpp index 43713b6c121..24c8c64ecfe 100644 --- a/plugins/formulashape/Dictionary.cpp +++ b/plugins/formulashape/Dictionary.cpp @@ -1,4039 +1,4039 @@ // Created: Wed Sep 12 13:13:23 2007 // WARNING! All changes made in this file will be lost! /* This file is part of the KDE project Copyright (C) 2007 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 "Dictionary.h" Dictionary::Dictionary() { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; m_maxsize = "infinity"; m_minsize = "1"; m_fence = false; m_separator = false; m_stretchy = false; m_symmetric = true; m_largeop = false; m_movablelimits = false; m_accent = false; } QString Dictionary::lSpace() const { return m_lspace; } QString Dictionary::rSpace() const { return m_rspace; } bool Dictionary::stretchy() const { return m_stretchy; } QChar Dictionary::mapEntity( const QString& entity ) { if( entity.isEmpty() ) return QChar(); - switch( entity[0].toLower().toAscii() ) { + switch( entity[0].toLower().toLatin1() ) { case 'a': if( entity == "Aacute" ) return QChar( 0x000C1 ); if( entity == "aacute" ) return QChar( 0x000E1 ); if( entity == "Abreve" ) return QChar( 0x00102 ); if( entity == "abreve" ) return QChar( 0x00103 ); if( entity == "ac" ) return QChar( 0x0223E ); if( entity == "acd" ) return QChar( 0x0223F ); if( entity == "Acirc" ) return QChar( 0x000C2 ); if( entity == "acirc" ) return QChar( 0x000E2 ); if( entity == "acute" ) return QChar( 0x000B4 ); if( entity == "Acy" ) return QChar( 0x00410 ); if( entity == "acy" ) return QChar( 0x00430 ); if( entity == "AElig" ) return QChar( 0x000C6 ); if( entity == "aelig" ) return QChar( 0x000E6 ); if( entity == "af" ) return QChar( 0x02061 ); if( entity == "Afr" ) return QChar( 0x1D504 ); if( entity == "afr" ) return QChar( 0x1D51E ); if( entity == "Agrave" ) return QChar( 0x000C0 ); if( entity == "agrave" ) return QChar( 0x000E0 ); if( entity == "aleph" ) return QChar( 0x02135 ); if( entity == "alpha" ) return QChar( 0x003B1 ); if( entity == "Amacr" ) return QChar( 0x00100 ); if( entity == "amacr" ) return QChar( 0x00101 ); if( entity == "amalg" ) return QChar( 0x02A3F ); if( entity == "amp" ) return QChar( 0x00026 ); if( entity == "And" ) return QChar( 0x02A53 ); if( entity == "and" ) return QChar( 0x02227 ); if( entity == "andand" ) return QChar( 0x02A55 ); if( entity == "andd" ) return QChar( 0x02A5C ); if( entity == "andslope" ) return QChar( 0x02A58 ); if( entity == "andv" ) return QChar( 0x02A5A ); if( entity == "ang" ) return QChar( 0x02220 ); if( entity == "ange" ) return QChar( 0x029A4 ); if( entity == "angle" ) return QChar( 0x02220 ); if( entity == "angmsd" ) return QChar( 0x02221 ); if( entity == "angmsdaa" ) return QChar( 0x029A8 ); if( entity == "angmsdab" ) return QChar( 0x029A9 ); if( entity == "angmsdac" ) return QChar( 0x029AA ); if( entity == "angmsdad" ) return QChar( 0x029AB ); if( entity == "angmsdae" ) return QChar( 0x029AC ); if( entity == "angmsdaf" ) return QChar( 0x029AD ); if( entity == "angmsdag" ) return QChar( 0x029AE ); if( entity == "angmsdah" ) return QChar( 0x029AF ); if( entity == "angrt" ) return QChar( 0x0221F ); if( entity == "angrtvb" ) return QChar( 0x022BE ); if( entity == "angrtvbd" ) return QChar( 0x0299D ); if( entity == "angsph" ) return QChar( 0x02222 ); if( entity == "angst" ) return QChar( 0x0212B ); if( entity == "angzarr" ) return QChar( 0x0237C ); if( entity == "Aogon" ) return QChar( 0x00104 ); if( entity == "aogon" ) return QChar( 0x00105 ); if( entity == "Aopf" ) return QChar( 0x1D538 ); if( entity == "aopf" ) return QChar( 0x1D552 ); if( entity == "ap" ) return QChar( 0x02248 ); if( entity == "apacir" ) return QChar( 0x02A6F ); if( entity == "apE" ) return QChar( 0x02A70 ); if( entity == "ape" ) return QChar( 0x0224A ); if( entity == "apid" ) return QChar( 0x0224B ); if( entity == "apos" ) return QChar( 0x00027 ); if( entity == "ApplyFunction" ) return QChar( 0x02061 ); if( entity == "approx" ) return QChar( 0x02248 ); if( entity == "approxeq" ) return QChar( 0x0224A ); if( entity == "Aring" ) return QChar( 0x000C5 ); if( entity == "aring" ) return QChar( 0x000E5 ); if( entity == "Ascr" ) return QChar( 0x1D49C ); if( entity == "ascr" ) return QChar( 0x1D4B6 ); if( entity == "Assign" ) return QChar( 0x02254 ); if( entity == "ast" ) return QChar( 0x0002A ); if( entity == "asymp" ) return QChar( 0x02248 ); if( entity == "asympeq" ) return QChar( 0x0224D ); if( entity == "Atilde" ) return QChar( 0x000C3 ); if( entity == "atilde" ) return QChar( 0x000E3 ); if( entity == "Auml" ) return QChar( 0x000C4 ); if( entity == "auml" ) return QChar( 0x000E4 ); if( entity == "awconint" ) return QChar( 0x02233 ); if( entity == "awint" ) return QChar( 0x02A11 ); break; case 'b': if( entity == "backcong" ) return QChar( 0x0224C ); if( entity == "backepsilon" ) return QChar( 0x003F6 ); if( entity == "backprime" ) return QChar( 0x02035 ); if( entity == "backsim" ) return QChar( 0x0223D ); if( entity == "backsimeq" ) return QChar( 0x022CD ); if( entity == "Backslash" ) return QChar( 0x02216 ); if( entity == "Barv" ) return QChar( 0x02AE7 ); if( entity == "barvee" ) return QChar( 0x022BD ); if( entity == "Barwed" ) return QChar( 0x02306 ); if( entity == "barwed" ) return QChar( 0x02305 ); if( entity == "barwedge" ) return QChar( 0x02305 ); if( entity == "bbrk" ) return QChar( 0x023B5 ); if( entity == "bbrktbrk" ) return QChar( 0x023B6 ); if( entity == "bcong" ) return QChar( 0x0224C ); if( entity == "Bcy" ) return QChar( 0x00411 ); if( entity == "bcy" ) return QChar( 0x00431 ); if( entity == "becaus" ) return QChar( 0x02235 ); if( entity == "Because" ) return QChar( 0x02235 ); if( entity == "because" ) return QChar( 0x02235 ); if( entity == "bemptyv" ) return QChar( 0x029B0 ); if( entity == "bepsi" ) return QChar( 0x003F6 ); if( entity == "bernou" ) return QChar( 0x0212C ); if( entity == "Bernoullis" ) return QChar( 0x0212C ); if( entity == "beta" ) return QChar( 0x003B2 ); if( entity == "beth" ) return QChar( 0x02136 ); if( entity == "between" ) return QChar( 0x0226C ); if( entity == "Bfr" ) return QChar( 0x1D505 ); if( entity == "bfr" ) return QChar( 0x1D51F ); if( entity == "bigcap" ) return QChar( 0x022C2 ); if( entity == "bigcirc" ) return QChar( 0x025EF ); if( entity == "bigcup" ) return QChar( 0x022C3 ); if( entity == "bigodot" ) return QChar( 0x02A00 ); if( entity == "bigoplus" ) return QChar( 0x02A01 ); if( entity == "bigotimes" ) return QChar( 0x02A02 ); if( entity == "bigsqcup" ) return QChar( 0x02A06 ); if( entity == "bigstar" ) return QChar( 0x02605 ); if( entity == "bigtriangledown" ) return QChar( 0x025BD ); if( entity == "bigtriangleup" ) return QChar( 0x025B3 ); if( entity == "biguplus" ) return QChar( 0x02A04 ); if( entity == "bigvee" ) return QChar( 0x022C1 ); if( entity == "bigwedge" ) return QChar( 0x022C0 ); if( entity == "bkarow" ) return QChar( 0x0290D ); if( entity == "blacklozenge" ) return QChar( 0x029EB ); if( entity == "blacksquare" ) return QChar( 0x025AA ); if( entity == "blacktriangle" ) return QChar( 0x025B4 ); if( entity == "blacktriangledown" ) return QChar( 0x025BE ); if( entity == "blacktriangleleft" ) return QChar( 0x025C2 ); if( entity == "blacktriangleright" ) return QChar( 0x025B8 ); if( entity == "blank" ) return QChar( 0x02423 ); if( entity == "blk12" ) return QChar( 0x02592 ); if( entity == "blk14" ) return QChar( 0x02591 ); if( entity == "blk34" ) return QChar( 0x02593 ); if( entity == "block" ) return QChar( 0x02588 ); if( entity == "bNot" ) return QChar( 0x02AED ); if( entity == "bnot" ) return QChar( 0x02310 ); if( entity == "Bopf" ) return QChar( 0x1D539 ); if( entity == "bopf" ) return QChar( 0x1D553 ); if( entity == "bot" ) return QChar( 0x022A5 ); if( entity == "bottom" ) return QChar( 0x022A5 ); if( entity == "bowtie" ) return QChar( 0x022C8 ); if( entity == "boxbox" ) return QChar( 0x029C9 ); if( entity == "boxDL" ) return QChar( 0x02557 ); if( entity == "boxDl" ) return QChar( 0x02556 ); if( entity == "boxdL" ) return QChar( 0x02555 ); if( entity == "boxdl" ) return QChar( 0x02510 ); if( entity == "boxDR" ) return QChar( 0x02554 ); if( entity == "boxDr" ) return QChar( 0x02553 ); if( entity == "boxdR" ) return QChar( 0x02552 ); if( entity == "boxdr" ) return QChar( 0x0250C ); if( entity == "boxH" ) return QChar( 0x02550 ); if( entity == "boxh" ) return QChar( 0x02500 ); if( entity == "boxHD" ) return QChar( 0x02566 ); if( entity == "boxHd" ) return QChar( 0x02564 ); if( entity == "boxhD" ) return QChar( 0x02565 ); if( entity == "boxhd" ) return QChar( 0x0252C ); if( entity == "boxHU" ) return QChar( 0x02569 ); if( entity == "boxHu" ) return QChar( 0x02567 ); if( entity == "boxhU" ) return QChar( 0x02568 ); if( entity == "boxhu" ) return QChar( 0x02534 ); if( entity == "boxminus" ) return QChar( 0x0229F ); if( entity == "boxplus" ) return QChar( 0x0229E ); if( entity == "boxtimes" ) return QChar( 0x022A0 ); if( entity == "boxUL" ) return QChar( 0x0255D ); if( entity == "boxUl" ) return QChar( 0x0255C ); if( entity == "boxuL" ) return QChar( 0x0255B ); if( entity == "boxul" ) return QChar( 0x02518 ); if( entity == "boxUR" ) return QChar( 0x0255A ); if( entity == "boxUr" ) return QChar( 0x02559 ); if( entity == "boxuR" ) return QChar( 0x02558 ); if( entity == "boxur" ) return QChar( 0x02514 ); if( entity == "boxV" ) return QChar( 0x02551 ); if( entity == "boxv" ) return QChar( 0x02502 ); if( entity == "boxVH" ) return QChar( 0x0256C ); if( entity == "boxVh" ) return QChar( 0x0256B ); if( entity == "boxvH" ) return QChar( 0x0256A ); if( entity == "boxvh" ) return QChar( 0x0253C ); if( entity == "boxVL" ) return QChar( 0x02563 ); if( entity == "boxVl" ) return QChar( 0x02562 ); if( entity == "boxvL" ) return QChar( 0x02561 ); if( entity == "boxvl" ) return QChar( 0x02524 ); if( entity == "boxVR" ) return QChar( 0x02560 ); if( entity == "boxVr" ) return QChar( 0x0255F ); if( entity == "boxvR" ) return QChar( 0x0255E ); if( entity == "boxvr" ) return QChar( 0x0251C ); if( entity == "bprime" ) return QChar( 0x02035 ); if( entity == "Breve" ) return QChar( 0x002D8 ); if( entity == "breve" ) return QChar( 0x002D8 ); if( entity == "brvbar" ) return QChar( 0x000A6 ); if( entity == "Bscr" ) return QChar( 0x0212C ); if( entity == "bscr" ) return QChar( 0x1D4B7 ); if( entity == "bsemi" ) return QChar( 0x0204F ); if( entity == "bsim" ) return QChar( 0x0223D ); if( entity == "bsime" ) return QChar( 0x022CD ); if( entity == "bsol" ) return QChar( 0x0005C ); if( entity == "bsolb" ) return QChar( 0x029C5 ); if( entity == "bull" ) return QChar( 0x02022 ); if( entity == "bullet" ) return QChar( 0x02022 ); if( entity == "bump" ) return QChar( 0x0224E ); if( entity == "bumpE" ) return QChar( 0x02AAE ); if( entity == "bumpe" ) return QChar( 0x0224F ); if( entity == "Bumpeq" ) return QChar( 0x0224E ); if( entity == "bumpeq" ) return QChar( 0x0224F ); break; case 'c': if( entity == "Cacute" ) return QChar( 0x00106 ); if( entity == "cacute" ) return QChar( 0x00107 ); if( entity == "Cap" ) return QChar( 0x022D2 ); if( entity == "cap" ) return QChar( 0x02229 ); if( entity == "capand" ) return QChar( 0x02A44 ); if( entity == "capbrcup" ) return QChar( 0x02A49 ); if( entity == "capcap" ) return QChar( 0x02A4B ); if( entity == "capcup" ) return QChar( 0x02A47 ); if( entity == "capdot" ) return QChar( 0x02A40 ); if( entity == "CapitalDifferentialD" ) return QChar( 0x02145 ); if( entity == "caret" ) return QChar( 0x02041 ); if( entity == "caron" ) return QChar( 0x002C7 ); if( entity == "Cayleys" ) return QChar( 0x0212D ); if( entity == "ccaps" ) return QChar( 0x02A4D ); if( entity == "Ccaron" ) return QChar( 0x0010C ); if( entity == "ccaron" ) return QChar( 0x0010D ); if( entity == "Ccedil" ) return QChar( 0x000C7 ); if( entity == "ccedil" ) return QChar( 0x000E7 ); if( entity == "Ccirc" ) return QChar( 0x00108 ); if( entity == "ccirc" ) return QChar( 0x00109 ); if( entity == "Cconint" ) return QChar( 0x02230 ); if( entity == "ccups" ) return QChar( 0x02A4C ); if( entity == "ccupssm" ) return QChar( 0x02A50 ); if( entity == "Cdot" ) return QChar( 0x0010A ); if( entity == "cdot" ) return QChar( 0x0010B ); if( entity == "cedil" ) return QChar( 0x000B8 ); if( entity == "Cedilla" ) return QChar( 0x000B8 ); if( entity == "cemptyv" ) return QChar( 0x029B2 ); if( entity == "cent" ) return QChar( 0x000A2 ); if( entity == "CenterDot" ) return QChar( 0x000B7 ); if( entity == "centerdot" ) return QChar( 0x000B7 ); if( entity == "Cfr" ) return QChar( 0x0212D ); if( entity == "cfr" ) return QChar( 0x1D520 ); if( entity == "CHcy" ) return QChar( 0x00427 ); if( entity == "chcy" ) return QChar( 0x00447 ); if( entity == "check" ) return QChar( 0x02713 ); if( entity == "checkmark" ) return QChar( 0x02713 ); if( entity == "chi" ) return QChar( 0x003C7 ); if( entity == "cir" ) return QChar( 0x025CB ); if( entity == "circ" ) return QChar( 0x002C6 ); if( entity == "circeq" ) return QChar( 0x02257 ); if( entity == "circlearrowleft" ) return QChar( 0x021BA ); if( entity == "circlearrowright" ) return QChar( 0x021BB ); if( entity == "circledast" ) return QChar( 0x0229B ); if( entity == "circledcirc" ) return QChar( 0x0229A ); if( entity == "circleddash" ) return QChar( 0x0229D ); if( entity == "CircleDot" ) return QChar( 0x02299 ); if( entity == "circledR" ) return QChar( 0x000AE ); if( entity == "circledS" ) return QChar( 0x024C8 ); if( entity == "CircleMinus" ) return QChar( 0x02296 ); if( entity == "CirclePlus" ) return QChar( 0x02295 ); if( entity == "CircleTimes" ) return QChar( 0x02297 ); if( entity == "cirE" ) return QChar( 0x029C3 ); if( entity == "cire" ) return QChar( 0x02257 ); if( entity == "cirfnint" ) return QChar( 0x02A10 ); if( entity == "cirmid" ) return QChar( 0x02AEF ); if( entity == "cirscir" ) return QChar( 0x029C2 ); if( entity == "ClockwiseContourIntegral" ) return QChar( 0x02232 ); if( entity == "CloseCurlyDoubleQuote" ) return QChar( 0x0201D ); if( entity == "CloseCurlyQuote" ) return QChar( 0x02019 ); if( entity == "clubs" ) return QChar( 0x02663 ); if( entity == "clubsuit" ) return QChar( 0x02663 ); if( entity == "Colon" ) return QChar( 0x02237 ); if( entity == "colon" ) return QChar( 0x0003A ); if( entity == "Colone" ) return QChar( 0x02A74 ); if( entity == "colone" ) return QChar( 0x02254 ); if( entity == "coloneq" ) return QChar( 0x02254 ); if( entity == "comma" ) return QChar( 0x0002C ); if( entity == "commat" ) return QChar( 0x00040 ); if( entity == "comp" ) return QChar( 0x02201 ); if( entity == "compfn" ) return QChar( 0x02218 ); if( entity == "complement" ) return QChar( 0x02201 ); if( entity == "complexes" ) return QChar( 0x02102 ); if( entity == "cong" ) return QChar( 0x02245 ); if( entity == "congdot" ) return QChar( 0x02A6D ); if( entity == "Congruent" ) return QChar( 0x02261 ); if( entity == "Conint" ) return QChar( 0x0222F ); if( entity == "conint" ) return QChar( 0x0222E ); if( entity == "ContourIntegral" ) return QChar( 0x0222E ); if( entity == "Copf" ) return QChar( 0x02102 ); if( entity == "copf" ) return QChar( 0x1D554 ); if( entity == "coprod" ) return QChar( 0x02210 ); if( entity == "Coproduct" ) return QChar( 0x02210 ); if( entity == "copy" ) return QChar( 0x000A9 ); if( entity == "copysr" ) return QChar( 0x02117 ); if( entity == "CounterClockwiseContourIntegral" ) return QChar( 0x02233 ); if( entity == "Cross" ) return QChar( 0x02A2F ); if( entity == "cross" ) return QChar( 0x02717 ); if( entity == "Cscr" ) return QChar( 0x1D49E ); if( entity == "cscr" ) return QChar( 0x1D4B8 ); if( entity == "csub" ) return QChar( 0x02ACF ); if( entity == "csube" ) return QChar( 0x02AD1 ); if( entity == "csup" ) return QChar( 0x02AD0 ); if( entity == "csupe" ) return QChar( 0x02AD2 ); if( entity == "ctdot" ) return QChar( 0x022EF ); if( entity == "cudarrl" ) return QChar( 0x02938 ); if( entity == "cudarrr" ) return QChar( 0x02935 ); if( entity == "cuepr" ) return QChar( 0x022DE ); if( entity == "cuesc" ) return QChar( 0x022DF ); if( entity == "cularr" ) return QChar( 0x021B6 ); if( entity == "cularrp" ) return QChar( 0x0293D ); if( entity == "Cup" ) return QChar( 0x022D3 ); if( entity == "cup" ) return QChar( 0x0222A ); if( entity == "cupbrcap" ) return QChar( 0x02A48 ); if( entity == "CupCap" ) return QChar( 0x0224D ); if( entity == "cupcap" ) return QChar( 0x02A46 ); if( entity == "cupcup" ) return QChar( 0x02A4A ); if( entity == "cupdot" ) return QChar( 0x0228D ); if( entity == "cupor" ) return QChar( 0x02A45 ); if( entity == "curarr" ) return QChar( 0x021B7 ); if( entity == "curarrm" ) return QChar( 0x0293C ); if( entity == "curlyeqprec" ) return QChar( 0x022DE ); if( entity == "curlyeqsucc" ) return QChar( 0x022DF ); if( entity == "curlyvee" ) return QChar( 0x022CE ); if( entity == "curlywedge" ) return QChar( 0x022CF ); if( entity == "curren" ) return QChar( 0x000A4 ); if( entity == "curvearrowleft" ) return QChar( 0x021B6 ); if( entity == "curvearrowright" ) return QChar( 0x021B7 ); if( entity == "cuvee" ) return QChar( 0x022CE ); if( entity == "cuwed" ) return QChar( 0x022CF ); if( entity == "cwconint" ) return QChar( 0x02232 ); if( entity == "cwint" ) return QChar( 0x02231 ); if( entity == "cylcty" ) return QChar( 0x0232D ); break; case 'd': if( entity == "Dagger" ) return QChar( 0x02021 ); if( entity == "Dagger" ) return QChar( 0x02021 ); if( entity == "dagger" ) return QChar( 0x02020 ); if( entity == "dagger" ) return QChar( 0x02020 ); if( entity == "daleth" ) return QChar( 0x02138 ); if( entity == "Darr" ) return QChar( 0x021A1 ); if( entity == "dArr" ) return QChar( 0x021D3 ); if( entity == "darr" ) return QChar( 0x02193 ); if( entity == "dash" ) return QChar( 0x02010 ); if( entity == "Dashv" ) return QChar( 0x02AE4 ); if( entity == "dashv" ) return QChar( 0x022A3 ); if( entity == "dbkarow" ) return QChar( 0x0290F ); if( entity == "dblac" ) return QChar( 0x002DD ); if( entity == "Dcaron" ) return QChar( 0x0010E ); if( entity == "dcaron" ) return QChar( 0x0010F ); if( entity == "Dcy" ) return QChar( 0x00414 ); if( entity == "dcy" ) return QChar( 0x00434 ); if( entity == "DD" ) return QChar( 0x02145 ); if( entity == "dd" ) return QChar( 0x02146 ); if( entity == "ddagger" ) return QChar( 0x02021 ); if( entity == "ddarr" ) return QChar( 0x021CA ); if( entity == "DDotrahd" ) return QChar( 0x02911 ); if( entity == "ddotseq" ) return QChar( 0x02A77 ); if( entity == "deg" ) return QChar( 0x000B0 ); if( entity == "Del" ) return QChar( 0x02207 ); if( entity == "Delta" ) return QChar( 0x00394 ); if( entity == "delta" ) return QChar( 0x003B4 ); if( entity == "demptyv" ) return QChar( 0x029B1 ); if( entity == "dfisht" ) return QChar( 0x0297F ); if( entity == "Dfr" ) return QChar( 0x1D507 ); if( entity == "dfr" ) return QChar( 0x1D521 ); if( entity == "dHar" ) return QChar( 0x02965 ); if( entity == "dharl" ) return QChar( 0x021C3 ); if( entity == "dharr" ) return QChar( 0x021C2 ); if( entity == "DiacriticalAcute" ) return QChar( 0x000B4 ); if( entity == "DiacriticalDot" ) return QChar( 0x002D9 ); if( entity == "DiacriticalDoubleAcute" ) return QChar( 0x002DD ); if( entity == "DiacriticalGrave" ) return QChar( 0x00060 ); if( entity == "DiacriticalTilde" ) return QChar( 0x002DC ); if( entity == "diam" ) return QChar( 0x022C4 ); if( entity == "Diamond" ) return QChar( 0x022C4 ); if( entity == "diamond" ) return QChar( 0x022C4 ); if( entity == "diamondsuit" ) return QChar( 0x02666 ); if( entity == "diams" ) return QChar( 0x02666 ); if( entity == "die" ) return QChar( 0x000A8 ); if( entity == "DifferentialD" ) return QChar( 0x02146 ); if( entity == "digamma" ) return QChar( 0x003DD ); if( entity == "disin" ) return QChar( 0x022F2 ); if( entity == "div" ) return QChar( 0x000F7 ); if( entity == "divide" ) return QChar( 0x000F7 ); if( entity == "divideontimes" ) return QChar( 0x022C7 ); if( entity == "divonx" ) return QChar( 0x022C7 ); if( entity == "DJcy" ) return QChar( 0x00402 ); if( entity == "djcy" ) return QChar( 0x00452 ); if( entity == "dlcorn" ) return QChar( 0x0231E ); if( entity == "dlcrop" ) return QChar( 0x0230D ); if( entity == "dollar" ) return QChar( 0x00024 ); if( entity == "Dopf" ) return QChar( 0x1D53B ); if( entity == "dopf" ) return QChar( 0x1D555 ); if( entity == "Dot" ) return QChar( 0x000A8 ); if( entity == "dot" ) return QChar( 0x002D9 ); if( entity == "DotDot" ) return QChar( 0x020DC ); if( entity == "doteq" ) return QChar( 0x02250 ); if( entity == "doteqdot" ) return QChar( 0x02251 ); if( entity == "DotEqual" ) return QChar( 0x02250 ); if( entity == "dotminus" ) return QChar( 0x02238 ); if( entity == "dotplus" ) return QChar( 0x02214 ); if( entity == "dotsquare" ) return QChar( 0x022A1 ); if( entity == "doublebarwedge" ) return QChar( 0x02306 ); if( entity == "DoubleContourIntegral" ) return QChar( 0x0222F ); if( entity == "DoubleDot" ) return QChar( 0x000A8 ); if( entity == "DoubleDownArrow" ) return QChar( 0x021D3 ); if( entity == "DoubleLeftArrow" ) return QChar( 0x021D0 ); if( entity == "DoubleLeftRightArrow" ) return QChar( 0x021D4 ); if( entity == "DoubleLeftTee" ) return QChar( 0x02AE4 ); if( entity == "DoubleLongLeftArrow" ) return QChar( 0x027F8 ); if( entity == "DoubleLongLeftRightArrow" ) return QChar( 0x027FA ); if( entity == "DoubleLongRightArrow" ) return QChar( 0x027F9 ); if( entity == "DoubleRightArrow" ) return QChar( 0x021D2 ); if( entity == "DoubleRightTee" ) return QChar( 0x022A8 ); if( entity == "DoubleUpArrow" ) return QChar( 0x021D1 ); if( entity == "DoubleUpDownArrow" ) return QChar( 0x021D5 ); if( entity == "DoubleVerticalBar" ) return QChar( 0x02225 ); if( entity == "DownArrow" ) return QChar( 0x02193 ); if( entity == "Downarrow" ) return QChar( 0x021D3 ); if( entity == "downarrow" ) return QChar( 0x02193 ); if( entity == "DownArrowBar" ) return QChar( 0x02913 ); if( entity == "DownArrowUpArrow" ) return QChar( 0x021F5 ); if( entity == "DownBreve" ) return QChar( 0x00311 ); if( entity == "downdownarrows" ) return QChar( 0x021CA ); if( entity == "downharpoonleft" ) return QChar( 0x021C3 ); if( entity == "downharpoonright" ) return QChar( 0x021C2 ); if( entity == "DownLeftRightVector" ) return QChar( 0x02950 ); if( entity == "DownLeftTeeVector" ) return QChar( 0x0295E ); if( entity == "DownLeftVector" ) return QChar( 0x021BD ); if( entity == "DownLeftVectorBar" ) return QChar( 0x02956 ); if( entity == "DownRightTeeVector" ) return QChar( 0x0295F ); if( entity == "DownRightVector" ) return QChar( 0x021C1 ); if( entity == "DownRightVectorBar" ) return QChar( 0x02957 ); if( entity == "DownTee" ) return QChar( 0x022A4 ); if( entity == "DownTeeArrow" ) return QChar( 0x021A7 ); if( entity == "drbkarow" ) return QChar( 0x02910 ); if( entity == "drcorn" ) return QChar( 0x0231F ); if( entity == "drcrop" ) return QChar( 0x0230C ); if( entity == "Dscr" ) return QChar( 0x1D49F ); if( entity == "dscr" ) return QChar( 0x1D4B9 ); if( entity == "DScy" ) return QChar( 0x00405 ); if( entity == "dscy" ) return QChar( 0x00455 ); if( entity == "dsol" ) return QChar( 0x029F6 ); if( entity == "Dstrok" ) return QChar( 0x00110 ); if( entity == "dstrok" ) return QChar( 0x00111 ); if( entity == "dtdot" ) return QChar( 0x022F1 ); if( entity == "dtri" ) return QChar( 0x025BF ); if( entity == "dtrif" ) return QChar( 0x025BE ); if( entity == "duarr" ) return QChar( 0x021F5 ); if( entity == "duhar" ) return QChar( 0x0296F ); if( entity == "dwangle" ) return QChar( 0x029A6 ); if( entity == "DZcy" ) return QChar( 0x0040F ); if( entity == "dzcy" ) return QChar( 0x0045F ); if( entity == "dzigrarr" ) return QChar( 0x027FF ); break; case 'e': if( entity == "Eacute" ) return QChar( 0x000C9 ); if( entity == "eacute" ) return QChar( 0x000E9 ); if( entity == "easter" ) return QChar( 0x02A6E ); if( entity == "Ecaron" ) return QChar( 0x0011A ); if( entity == "ecaron" ) return QChar( 0x0011B ); if( entity == "ecir" ) return QChar( 0x02256 ); if( entity == "Ecirc" ) return QChar( 0x000CA ); if( entity == "ecirc" ) return QChar( 0x000EA ); if( entity == "ecolon" ) return QChar( 0x02255 ); if( entity == "Ecy" ) return QChar( 0x0042D ); if( entity == "ecy" ) return QChar( 0x0044D ); if( entity == "eDDot" ) return QChar( 0x02A77 ); if( entity == "Edot" ) return QChar( 0x00116 ); if( entity == "eDot" ) return QChar( 0x02251 ); if( entity == "edot" ) return QChar( 0x00117 ); if( entity == "ee" ) return QChar( 0x02147 ); if( entity == "efDot" ) return QChar( 0x02252 ); if( entity == "Efr" ) return QChar( 0x1D508 ); if( entity == "efr" ) return QChar( 0x1D522 ); if( entity == "eg" ) return QChar( 0x02A9A ); if( entity == "Egrave" ) return QChar( 0x000C8 ); if( entity == "egrave" ) return QChar( 0x000E8 ); if( entity == "egs" ) return QChar( 0x02A96 ); if( entity == "egsdot" ) return QChar( 0x02A98 ); if( entity == "el" ) return QChar( 0x02A99 ); if( entity == "Element" ) return QChar( 0x02208 ); if( entity == "elinters" ) return QChar( 0x0FFFD ); if( entity == "ell" ) return QChar( 0x02113 ); if( entity == "els" ) return QChar( 0x02A95 ); if( entity == "elsdot" ) return QChar( 0x02A97 ); if( entity == "Emacr" ) return QChar( 0x00112 ); if( entity == "emacr" ) return QChar( 0x00113 ); if( entity == "empty" ) return QChar( 0x02205 ); if( entity == "emptyset" ) return QChar( 0x02205 ); if( entity == "EmptySmallSquare" ) return QChar( 0x025FB ); if( entity == "emptyv" ) return QChar( 0x02205 ); if( entity == "EmptyVerySmallSquare" ) return QChar( 0x025AB ); if( entity == "emsp" ) return QChar( 0x02003 ); if( entity == "emsp13" ) return QChar( 0x02004 ); if( entity == "emsp14" ) return QChar( 0x02005 ); if( entity == "ENG" ) return QChar( 0x0014A ); if( entity == "eng" ) return QChar( 0x0014B ); if( entity == "ensp" ) return QChar( 0x02002 ); if( entity == "Eogon" ) return QChar( 0x00118 ); if( entity == "eogon" ) return QChar( 0x00119 ); if( entity == "Eopf" ) return QChar( 0x1D53C ); if( entity == "eopf" ) return QChar( 0x1D556 ); if( entity == "epar" ) return QChar( 0x022D5 ); if( entity == "eparsl" ) return QChar( 0x029E3 ); if( entity == "eplus" ) return QChar( 0x02A71 ); if( entity == "epsi" ) return QChar( 0x003F5 ); if( entity == "epsiv" ) return QChar( 0x003B5 ); if( entity == "eqcirc" ) return QChar( 0x02256 ); if( entity == "eqcolon" ) return QChar( 0x02255 ); if( entity == "eqsim" ) return QChar( 0x02242 ); if( entity == "eqslantgtr" ) return QChar( 0x02A96 ); if( entity == "eqslantless" ) return QChar( 0x02A95 ); if( entity == "Equal" ) return QChar( 0x02A75 ); if( entity == "equals" ) return QChar( 0x0003D ); if( entity == "EqualTilde" ) return QChar( 0x02242 ); if( entity == "equest" ) return QChar( 0x0225F ); if( entity == "Equilibrium" ) return QChar( 0x021CC ); if( entity == "equiv" ) return QChar( 0x02261 ); if( entity == "equivDD" ) return QChar( 0x02A78 ); if( entity == "eqvparsl" ) return QChar( 0x029E5 ); if( entity == "erarr" ) return QChar( 0x02971 ); if( entity == "erDot" ) return QChar( 0x02253 ); if( entity == "Escr" ) return QChar( 0x02130 ); if( entity == "escr" ) return QChar( 0x0212F ); if( entity == "esdot" ) return QChar( 0x02250 ); if( entity == "Esim" ) return QChar( 0x02A73 ); if( entity == "esim" ) return QChar( 0x02242 ); if( entity == "eta" ) return QChar( 0x003B7 ); if( entity == "ETH" ) return QChar( 0x000D0 ); if( entity == "eth" ) return QChar( 0x000F0 ); if( entity == "Euml" ) return QChar( 0x000CB ); if( entity == "euml" ) return QChar( 0x000EB ); if( entity == "excl" ) return QChar( 0x00021 ); if( entity == "exist" ) return QChar( 0x02203 ); if( entity == "Exists" ) return QChar( 0x02203 ); if( entity == "expectation" ) return QChar( 0x02130 ); if( entity == "ExponentialE" ) return QChar( 0x02147 ); if( entity == "exponentiale" ) return QChar( 0x02147 ); break; case 'f': if( entity == "fallingdotseq" ) return QChar( 0x02252 ); if( entity == "Fcy" ) return QChar( 0x00424 ); if( entity == "fcy" ) return QChar( 0x00444 ); if( entity == "female" ) return QChar( 0x02640 ); if( entity == "ffilig" ) return QChar( 0x0FB03 ); if( entity == "fflig" ) return QChar( 0x0FB00 ); if( entity == "ffllig" ) return QChar( 0x0FB04 ); if( entity == "Ffr" ) return QChar( 0x1D509 ); if( entity == "ffr" ) return QChar( 0x1D523 ); if( entity == "filig" ) return QChar( 0x0FB01 ); if( entity == "FilledSmallSquare" ) return QChar( 0x025FC ); if( entity == "FilledVerySmallSquare" ) return QChar( 0x025AA ); if( entity == "flat" ) return QChar( 0x0266D ); if( entity == "fllig" ) return QChar( 0x0FB02 ); if( entity == "fltns" ) return QChar( 0x025B1 ); if( entity == "fnof" ) return QChar( 0x00192 ); if( entity == "Fopf" ) return QChar( 0x1D53D ); if( entity == "fopf" ) return QChar( 0x1D557 ); if( entity == "ForAll" ) return QChar( 0x02200 ); if( entity == "forall" ) return QChar( 0x02200 ); if( entity == "fork" ) return QChar( 0x022D4 ); if( entity == "forkv" ) return QChar( 0x02AD9 ); if( entity == "Fouriertrf" ) return QChar( 0x02131 ); if( entity == "fpartint" ) return QChar( 0x02A0D ); if( entity == "frac12" ) return QChar( 0x000BD ); if( entity == "frac13" ) return QChar( 0x02153 ); if( entity == "frac14" ) return QChar( 0x000BC ); if( entity == "frac15" ) return QChar( 0x02155 ); if( entity == "frac16" ) return QChar( 0x02159 ); if( entity == "frac18" ) return QChar( 0x0215B ); if( entity == "frac23" ) return QChar( 0x02154 ); if( entity == "frac25" ) return QChar( 0x02156 ); if( entity == "frac34" ) return QChar( 0x000BE ); if( entity == "frac35" ) return QChar( 0x02157 ); if( entity == "frac38" ) return QChar( 0x0215C ); if( entity == "frac45" ) return QChar( 0x02158 ); if( entity == "frac56" ) return QChar( 0x0215A ); if( entity == "frac58" ) return QChar( 0x0215D ); if( entity == "frac78" ) return QChar( 0x0215E ); if( entity == "frown" ) return QChar( 0x02322 ); if( entity == "Fscr" ) return QChar( 0x02131 ); if( entity == "fscr" ) return QChar( 0x1D4BB ); break; case 'g': if( entity == "gacute" ) return QChar( 0x001F5 ); if( entity == "Gamma" ) return QChar( 0x00393 ); if( entity == "gamma" ) return QChar( 0x003B3 ); if( entity == "Gammad" ) return QChar( 0x003DC ); if( entity == "gammad" ) return QChar( 0x003DD ); if( entity == "gap" ) return QChar( 0x02A86 ); if( entity == "Gbreve" ) return QChar( 0x0011E ); if( entity == "gbreve" ) return QChar( 0x0011F ); if( entity == "Gcedil" ) return QChar( 0x00122 ); if( entity == "Gcirc" ) return QChar( 0x0011C ); if( entity == "gcirc" ) return QChar( 0x0011D ); if( entity == "Gcy" ) return QChar( 0x00413 ); if( entity == "gcy" ) return QChar( 0x00433 ); if( entity == "Gdot" ) return QChar( 0x00120 ); if( entity == "gdot" ) return QChar( 0x00121 ); if( entity == "gE" ) return QChar( 0x02267 ); if( entity == "ge" ) return QChar( 0x02265 ); if( entity == "gEl" ) return QChar( 0x02A8C ); if( entity == "gel" ) return QChar( 0x022DB ); if( entity == "geq" ) return QChar( 0x02265 ); if( entity == "geqq" ) return QChar( 0x02267 ); if( entity == "geqslant" ) return QChar( 0x02A7E ); if( entity == "ges" ) return QChar( 0x02A7E ); if( entity == "gescc" ) return QChar( 0x02AA9 ); if( entity == "gesdot" ) return QChar( 0x02A80 ); if( entity == "gesdoto" ) return QChar( 0x02A82 ); if( entity == "gesdotol" ) return QChar( 0x02A84 ); if( entity == "gesles" ) return QChar( 0x02A94 ); if( entity == "Gfr" ) return QChar( 0x1D50A ); if( entity == "gfr" ) return QChar( 0x1D524 ); if( entity == "Gg" ) return QChar( 0x022D9 ); if( entity == "gg" ) return QChar( 0x0226B ); if( entity == "ggg" ) return QChar( 0x022D9 ); if( entity == "gimel" ) return QChar( 0x02137 ); if( entity == "GJcy" ) return QChar( 0x00403 ); if( entity == "gjcy" ) return QChar( 0x00453 ); if( entity == "gl" ) return QChar( 0x02277 ); if( entity == "gla" ) return QChar( 0x02AA5 ); if( entity == "glE" ) return QChar( 0x02A92 ); if( entity == "glj" ) return QChar( 0x02AA4 ); if( entity == "gnap" ) return QChar( 0x02A8A ); if( entity == "gnapprox" ) return QChar( 0x02A8A ); if( entity == "gnE" ) return QChar( 0x02269 ); if( entity == "gne" ) return QChar( 0x02A88 ); if( entity == "gneq" ) return QChar( 0x02A88 ); if( entity == "gneqq" ) return QChar( 0x02269 ); if( entity == "gnsim" ) return QChar( 0x022E7 ); if( entity == "Gopf" ) return QChar( 0x1D53E ); if( entity == "gopf" ) return QChar( 0x1D558 ); if( entity == "grave" ) return QChar( 0x00060 ); if( entity == "GreaterEqual" ) return QChar( 0x02265 ); if( entity == "GreaterEqualLess" ) return QChar( 0x022DB ); if( entity == "GreaterFullEqual" ) return QChar( 0x02267 ); if( entity == "GreaterGreater" ) return QChar( 0x02AA2 ); if( entity == "GreaterLess" ) return QChar( 0x02277 ); if( entity == "GreaterSlantEqual" ) return QChar( 0x02A7E ); if( entity == "GreaterTilde" ) return QChar( 0x02273 ); if( entity == "Gscr" ) return QChar( 0x1D4A2 ); if( entity == "gscr" ) return QChar( 0x0210A ); if( entity == "gsim" ) return QChar( 0x02273 ); if( entity == "gsime" ) return QChar( 0x02A8E ); if( entity == "gsiml" ) return QChar( 0x02A90 ); if( entity == "Gt" ) return QChar( 0x0226B ); if( entity == "gt" ) return QChar( 0x0003E ); if( entity == "gtcc" ) return QChar( 0x02AA7 ); if( entity == "gtcir" ) return QChar( 0x02A7A ); if( entity == "gtdot" ) return QChar( 0x022D7 ); if( entity == "gtlPar" ) return QChar( 0x02995 ); if( entity == "gtquest" ) return QChar( 0x02A7C ); if( entity == "gtrapprox" ) return QChar( 0x02A86 ); if( entity == "gtrarr" ) return QChar( 0x02978 ); if( entity == "gtrdot" ) return QChar( 0x022D7 ); if( entity == "gtreqless" ) return QChar( 0x022DB ); if( entity == "gtreqqless" ) return QChar( 0x02A8C ); if( entity == "gtrless" ) return QChar( 0x02277 ); if( entity == "gtrsim" ) return QChar( 0x02273 ); break; case 'h': if( entity == "Hacek" ) return QChar( 0x002C7 ); if( entity == "hairsp" ) return QChar( 0x0200A ); if( entity == "half" ) return QChar( 0x000BD ); if( entity == "hamilt" ) return QChar( 0x0210B ); if( entity == "HARDcy" ) return QChar( 0x0042A ); if( entity == "hardcy" ) return QChar( 0x0044A ); if( entity == "hArr" ) return QChar( 0x021D4 ); if( entity == "harr" ) return QChar( 0x02194 ); if( entity == "harrcir" ) return QChar( 0x02948 ); if( entity == "harrw" ) return QChar( 0x021AD ); if( entity == "Hat" ) return QChar( 0x0005E ); if( entity == "hbar" ) return QChar( 0x0210F ); if( entity == "Hcirc" ) return QChar( 0x00124 ); if( entity == "hcirc" ) return QChar( 0x00125 ); if( entity == "hearts" ) return QChar( 0x02665 ); if( entity == "heartsuit" ) return QChar( 0x02665 ); if( entity == "hellip" ) return QChar( 0x02026 ); if( entity == "hercon" ) return QChar( 0x022B9 ); if( entity == "Hfr" ) return QChar( 0x0210C ); if( entity == "hfr" ) return QChar( 0x1D525 ); if( entity == "HilbertSpace" ) return QChar( 0x0210B ); if( entity == "hksearow" ) return QChar( 0x02925 ); if( entity == "hkswarow" ) return QChar( 0x02926 ); if( entity == "hoarr" ) return QChar( 0x021FF ); if( entity == "homtht" ) return QChar( 0x0223B ); if( entity == "hookleftarrow" ) return QChar( 0x021A9 ); if( entity == "hookrightarrow" ) return QChar( 0x021AA ); if( entity == "Hopf" ) return QChar( 0x0210D ); if( entity == "hopf" ) return QChar( 0x1D559 ); if( entity == "horbar" ) return QChar( 0x02015 ); if( entity == "HorizontalLine" ) return QChar( 0x02500 ); if( entity == "Hscr" ) return QChar( 0x0210B ); if( entity == "hscr" ) return QChar( 0x1D4BD ); if( entity == "hslash" ) return QChar( 0x0210F ); if( entity == "Hstrok" ) return QChar( 0x00126 ); if( entity == "hstrok" ) return QChar( 0x00127 ); if( entity == "HumpDownHump" ) return QChar( 0x0224E ); if( entity == "HumpEqual" ) return QChar( 0x0224F ); if( entity == "hybull" ) return QChar( 0x02043 ); if( entity == "hyphen" ) return QChar( 0x02010 ); break; case 'i': if( entity == "Iacute" ) return QChar( 0x000CD ); if( entity == "iacute" ) return QChar( 0x000ED ); if( entity == "ic" ) return QChar( 0x02063 ); if( entity == "Icirc" ) return QChar( 0x000CE ); if( entity == "icirc" ) return QChar( 0x000EE ); if( entity == "Icy" ) return QChar( 0x00418 ); if( entity == "icy" ) return QChar( 0x00438 ); if( entity == "Idot" ) return QChar( 0x00130 ); if( entity == "IEcy" ) return QChar( 0x00415 ); if( entity == "iecy" ) return QChar( 0x00435 ); if( entity == "iexcl" ) return QChar( 0x000A1 ); if( entity == "iff" ) return QChar( 0x021D4 ); if( entity == "Ifr" ) return QChar( 0x02111 ); if( entity == "ifr" ) return QChar( 0x1D526 ); if( entity == "Igrave" ) return QChar( 0x000CC ); if( entity == "igrave" ) return QChar( 0x000EC ); if( entity == "ii" ) return QChar( 0x02148 ); if( entity == "iiiint" ) return QChar( 0x02A0C ); if( entity == "iiint" ) return QChar( 0x0222D ); if( entity == "iinfin" ) return QChar( 0x029DC ); if( entity == "iiota" ) return QChar( 0x02129 ); if( entity == "IJlig" ) return QChar( 0x00132 ); if( entity == "ijlig" ) return QChar( 0x00133 ); if( entity == "Im" ) return QChar( 0x02111 ); if( entity == "Imacr" ) return QChar( 0x0012A ); if( entity == "imacr" ) return QChar( 0x0012B ); if( entity == "image" ) return QChar( 0x02111 ); if( entity == "ImaginaryI" ) return QChar( 0x02148 ); if( entity == "imagline" ) return QChar( 0x02110 ); if( entity == "imagpart" ) return QChar( 0x02111 ); if( entity == "imath" ) return QChar( 0x00131 ); if( entity == "imof" ) return QChar( 0x022B7 ); if( entity == "imped" ) return QChar( 0x001B5 ); if( entity == "Implies" ) return QChar( 0x021D2 ); if( entity == "in" ) return QChar( 0x02208 ); if( entity == "incare" ) return QChar( 0x02105 ); if( entity == "infin" ) return QChar( 0x0221E ); if( entity == "infintie" ) return QChar( 0x029DD ); if( entity == "inodot" ) return QChar( 0x00131 ); if( entity == "Int" ) return QChar( 0x0222C ); if( entity == "int" ) return QChar( 0x0222B ); if( entity == "intcal" ) return QChar( 0x022BA ); if( entity == "integers" ) return QChar( 0x02124 ); if( entity == "Integral" ) return QChar( 0x0222B ); if( entity == "intercal" ) return QChar( 0x022BA ); if( entity == "Intersection" ) return QChar( 0x022C2 ); if( entity == "intlarhk" ) return QChar( 0x02A17 ); if( entity == "intprod" ) return QChar( 0x02A3C ); if( entity == "InvisibleComma" ) return QChar( 0x02063 ); if( entity == "InvisibleTimes" ) return QChar( 0x02062 ); if( entity == "IOcy" ) return QChar( 0x00401 ); if( entity == "iocy" ) return QChar( 0x00451 ); if( entity == "Iogon" ) return QChar( 0x0012E ); if( entity == "iogon" ) return QChar( 0x0012F ); if( entity == "Iopf" ) return QChar( 0x1D540 ); if( entity == "iopf" ) return QChar( 0x1D55A ); if( entity == "iota" ) return QChar( 0x003B9 ); if( entity == "iprod" ) return QChar( 0x02A3C ); if( entity == "iquest" ) return QChar( 0x000BF ); if( entity == "Iscr" ) return QChar( 0x02110 ); if( entity == "iscr" ) return QChar( 0x1D4BE ); if( entity == "isin" ) return QChar( 0x02208 ); if( entity == "isindot" ) return QChar( 0x022F5 ); if( entity == "isinE" ) return QChar( 0x022F9 ); if( entity == "isins" ) return QChar( 0x022F4 ); if( entity == "isinsv" ) return QChar( 0x022F3 ); if( entity == "isinv" ) return QChar( 0x02208 ); if( entity == "it" ) return QChar( 0x02062 ); if( entity == "Itilde" ) return QChar( 0x00128 ); if( entity == "itilde" ) return QChar( 0x00129 ); if( entity == "Iukcy" ) return QChar( 0x00406 ); if( entity == "iukcy" ) return QChar( 0x00456 ); if( entity == "Iuml" ) return QChar( 0x000CF ); if( entity == "iuml" ) return QChar( 0x000EF ); break; case 'j': if( entity == "Jcirc" ) return QChar( 0x00134 ); if( entity == "jcirc" ) return QChar( 0x00135 ); if( entity == "Jcy" ) return QChar( 0x00419 ); if( entity == "jcy" ) return QChar( 0x00439 ); if( entity == "Jfr" ) return QChar( 0x1D50D ); if( entity == "jfr" ) return QChar( 0x1D527 ); if( entity == "jmath" ) return QChar( 0x0006A ); if( entity == "Jopf" ) return QChar( 0x1D541 ); if( entity == "jopf" ) return QChar( 0x1D55B ); if( entity == "Jscr" ) return QChar( 0x1D4A5 ); if( entity == "jscr" ) return QChar( 0x1D4BF ); if( entity == "Jsercy" ) return QChar( 0x00408 ); if( entity == "jsercy" ) return QChar( 0x00458 ); if( entity == "Jukcy" ) return QChar( 0x00404 ); if( entity == "jukcy" ) return QChar( 0x00454 ); break; case 'k': if( entity == "kappa" ) return QChar( 0x003BA ); if( entity == "kappav" ) return QChar( 0x003F0 ); if( entity == "Kcedil" ) return QChar( 0x00136 ); if( entity == "kcedil" ) return QChar( 0x00137 ); if( entity == "Kcy" ) return QChar( 0x0041A ); if( entity == "kcy" ) return QChar( 0x0043A ); if( entity == "Kfr" ) return QChar( 0x1D50E ); if( entity == "kfr" ) return QChar( 0x1D528 ); if( entity == "kgreen" ) return QChar( 0x00138 ); if( entity == "KHcy" ) return QChar( 0x00425 ); if( entity == "khcy" ) return QChar( 0x00445 ); if( entity == "KJcy" ) return QChar( 0x0040C ); if( entity == "kjcy" ) return QChar( 0x0045C ); if( entity == "Kopf" ) return QChar( 0x1D542 ); if( entity == "kopf" ) return QChar( 0x1D55C ); if( entity == "Kscr" ) return QChar( 0x1D4A6 ); if( entity == "kscr" ) return QChar( 0x1D4C0 ); break; case 'l': if( entity == "lAarr" ) return QChar( 0x021DA ); if( entity == "Lacute" ) return QChar( 0x00139 ); if( entity == "lacute" ) return QChar( 0x0013A ); if( entity == "laemptyv" ) return QChar( 0x029B4 ); if( entity == "lagran" ) return QChar( 0x02112 ); if( entity == "Lambda" ) return QChar( 0x0039B ); if( entity == "lambda" ) return QChar( 0x003BB ); if( entity == "Lang" ) return QChar( 0x0300A ); if( entity == "lang" ) return QChar( 0x02329 ); if( entity == "langd" ) return QChar( 0x02991 ); if( entity == "langle" ) return QChar( 0x02329 ); if( entity == "lap" ) return QChar( 0x02A85 ); if( entity == "Laplacetrf" ) return QChar( 0x02112 ); if( entity == "laquo" ) return QChar( 0x000AB ); if( entity == "Larr" ) return QChar( 0x0219E ); if( entity == "lArr" ) return QChar( 0x021D0 ); if( entity == "larr" ) return QChar( 0x02190 ); if( entity == "larrb" ) return QChar( 0x021E4 ); if( entity == "larrbfs" ) return QChar( 0x0291F ); if( entity == "larrfs" ) return QChar( 0x0291D ); if( entity == "larrhk" ) return QChar( 0x021A9 ); if( entity == "larrlp" ) return QChar( 0x021AB ); if( entity == "larrpl" ) return QChar( 0x02939 ); if( entity == "larrsim" ) return QChar( 0x02973 ); if( entity == "larrtl" ) return QChar( 0x021A2 ); if( entity == "lat" ) return QChar( 0x02AAB ); if( entity == "lAtail" ) return QChar( 0x0291B ); if( entity == "latail" ) return QChar( 0x02919 ); if( entity == "late" ) return QChar( 0x02AAD ); if( entity == "lBarr" ) return QChar( 0x0290E ); if( entity == "lbarr" ) return QChar( 0x0290C ); if( entity == "lbbrk" ) return QChar( 0x03014 ); if( entity == "lbrace" ) return QChar( 0x0007B ); if( entity == "lbrack" ) return QChar( 0x0005B ); if( entity == "lbrke" ) return QChar( 0x0298B ); if( entity == "lbrksld" ) return QChar( 0x0298F ); if( entity == "lbrkslu" ) return QChar( 0x0298D ); if( entity == "Lcaron" ) return QChar( 0x0013D ); if( entity == "lcaron" ) return QChar( 0x0013E ); if( entity == "Lcedil" ) return QChar( 0x0013B ); if( entity == "lcedil" ) return QChar( 0x0013C ); if( entity == "lceil" ) return QChar( 0x02308 ); if( entity == "lcub" ) return QChar( 0x0007B ); if( entity == "Lcy" ) return QChar( 0x0041B ); if( entity == "lcy" ) return QChar( 0x0043B ); if( entity == "ldca" ) return QChar( 0x02936 ); if( entity == "ldquo" ) return QChar( 0x0201C ); if( entity == "ldquor" ) return QChar( 0x0201E ); if( entity == "ldrdhar" ) return QChar( 0x02967 ); if( entity == "ldrushar" ) return QChar( 0x0294B ); if( entity == "ldsh" ) return QChar( 0x021B2 ); if( entity == "lE" ) return QChar( 0x02266 ); if( entity == "le" ) return QChar( 0x02264 ); if( entity == "LeftAngleBracket" ) return QChar( 0x02329 ); if( entity == "LeftArrow" ) return QChar( 0x02190 ); if( entity == "Leftarrow" ) return QChar( 0x021D0 ); if( entity == "leftarrow" ) return QChar( 0x02190 ); if( entity == "LeftArrowBar" ) return QChar( 0x021E4 ); if( entity == "LeftArrowRightArrow" ) return QChar( 0x021C6 ); if( entity == "leftarrowtail" ) return QChar( 0x021A2 ); if( entity == "LeftCeiling" ) return QChar( 0x02308 ); if( entity == "LeftDoubleBracket" ) return QChar( 0x0301A ); if( entity == "LeftDownTeeVector" ) return QChar( 0x02961 ); if( entity == "LeftDownVector" ) return QChar( 0x021C3 ); if( entity == "LeftDownVectorBar" ) return QChar( 0x02959 ); if( entity == "LeftFloor" ) return QChar( 0x0230A ); if( entity == "leftharpoondown" ) return QChar( 0x021BD ); if( entity == "leftharpoonup" ) return QChar( 0x021BC ); if( entity == "leftleftarrows" ) return QChar( 0x021C7 ); if( entity == "LeftRightArrow" ) return QChar( 0x02194 ); if( entity == "Leftrightarrow" ) return QChar( 0x021D4 ); if( entity == "leftrightarrow" ) return QChar( 0x02194 ); if( entity == "leftrightarrows" ) return QChar( 0x021C6 ); if( entity == "leftrightharpoons" ) return QChar( 0x021CB ); if( entity == "leftrightsquigarrow" ) return QChar( 0x021AD ); if( entity == "LeftRightVector" ) return QChar( 0x0294E ); if( entity == "LeftTee" ) return QChar( 0x022A3 ); if( entity == "LeftTeeArrow" ) return QChar( 0x021A4 ); if( entity == "LeftTeeVector" ) return QChar( 0x0295A ); if( entity == "leftthreetimes" ) return QChar( 0x022CB ); if( entity == "LeftTriangle" ) return QChar( 0x022B2 ); if( entity == "LeftTriangleBar" ) return QChar( 0x029CF ); if( entity == "LeftTriangleEqual" ) return QChar( 0x022B4 ); if( entity == "LeftUpDownVector" ) return QChar( 0x02951 ); if( entity == "LeftUpTeeVector" ) return QChar( 0x02960 ); if( entity == "LeftUpVector" ) return QChar( 0x021BF ); if( entity == "LeftUpVectorBar" ) return QChar( 0x02958 ); if( entity == "LeftVector" ) return QChar( 0x021BC ); if( entity == "LeftVectorBar" ) return QChar( 0x02952 ); if( entity == "lEg" ) return QChar( 0x02A8B ); if( entity == "leg" ) return QChar( 0x022DA ); if( entity == "leq" ) return QChar( 0x02264 ); if( entity == "leqq" ) return QChar( 0x02266 ); if( entity == "leqslant" ) return QChar( 0x02A7D ); if( entity == "les" ) return QChar( 0x02A7D ); if( entity == "lescc" ) return QChar( 0x02AA8 ); if( entity == "lesdot" ) return QChar( 0x02A7F ); if( entity == "lesdoto" ) return QChar( 0x02A81 ); if( entity == "lesdotor" ) return QChar( 0x02A83 ); if( entity == "lesges" ) return QChar( 0x02A93 ); if( entity == "lessapprox" ) return QChar( 0x02A85 ); if( entity == "lessdot" ) return QChar( 0x022D6 ); if( entity == "lesseqgtr" ) return QChar( 0x022DA ); if( entity == "lesseqqgtr" ) return QChar( 0x02A8B ); if( entity == "LessEqualGreater" ) return QChar( 0x022DA ); if( entity == "LessFullEqual" ) return QChar( 0x02266 ); if( entity == "LessGreater" ) return QChar( 0x02276 ); if( entity == "lessgtr" ) return QChar( 0x02276 ); if( entity == "LessLess" ) return QChar( 0x02AA1 ); if( entity == "lesssim" ) return QChar( 0x02272 ); if( entity == "LessSlantEqual" ) return QChar( 0x02A7D ); if( entity == "LessTilde" ) return QChar( 0x02272 ); if( entity == "lfisht" ) return QChar( 0x0297C ); if( entity == "lfloor" ) return QChar( 0x0230A ); if( entity == "Lfr" ) return QChar( 0x1D50F ); if( entity == "lfr" ) return QChar( 0x1D529 ); if( entity == "lg" ) return QChar( 0x02276 ); if( entity == "lgE" ) return QChar( 0x02A91 ); if( entity == "lHar" ) return QChar( 0x02962 ); if( entity == "lhard" ) return QChar( 0x021BD ); if( entity == "lharu" ) return QChar( 0x021BC ); if( entity == "lharul" ) return QChar( 0x0296A ); if( entity == "lhblk" ) return QChar( 0x02584 ); if( entity == "LJcy" ) return QChar( 0x00409 ); if( entity == "ljcy" ) return QChar( 0x00459 ); if( entity == "Ll" ) return QChar( 0x022D8 ); if( entity == "ll" ) return QChar( 0x0226A ); if( entity == "llarr" ) return QChar( 0x021C7 ); if( entity == "llcorner" ) return QChar( 0x0231E ); if( entity == "Lleftarrow" ) return QChar( 0x021DA ); if( entity == "llhard" ) return QChar( 0x0296B ); if( entity == "lltri" ) return QChar( 0x025FA ); if( entity == "Lmidot" ) return QChar( 0x0013F ); if( entity == "lmidot" ) return QChar( 0x00140 ); if( entity == "lmoust" ) return QChar( 0x023B0 ); if( entity == "lmoustache" ) return QChar( 0x023B0 ); if( entity == "lnap" ) return QChar( 0x02A89 ); if( entity == "lnapprox" ) return QChar( 0x02A89 ); if( entity == "lnE" ) return QChar( 0x02268 ); if( entity == "lne" ) return QChar( 0x02A87 ); if( entity == "lneq" ) return QChar( 0x02A87 ); if( entity == "lneqq" ) return QChar( 0x02268 ); if( entity == "lnsim" ) return QChar( 0x022E6 ); if( entity == "loang" ) return QChar( 0x03018 ); if( entity == "loarr" ) return QChar( 0x021FD ); if( entity == "lobrk" ) return QChar( 0x0301A ); if( entity == "LongLeftArrow" ) return QChar( 0x027F5 ); if( entity == "Longleftarrow" ) return QChar( 0x027F8 ); if( entity == "longleftarrow" ) return QChar( 0x027F5 ); if( entity == "LongLeftRightArrow" ) return QChar( 0x027F7 ); if( entity == "Longleftrightarrow" ) return QChar( 0x027FA ); if( entity == "longleftrightarrow" ) return QChar( 0x027F7 ); if( entity == "longmapsto" ) return QChar( 0x027FC ); if( entity == "LongRightArrow" ) return QChar( 0x027F6 ); if( entity == "Longrightarrow" ) return QChar( 0x027F9 ); if( entity == "longrightarrow" ) return QChar( 0x027F6 ); if( entity == "looparrowleft" ) return QChar( 0x021AB ); if( entity == "looparrowright" ) return QChar( 0x021AC ); if( entity == "lopar" ) return QChar( 0x02985 ); if( entity == "Lopf" ) return QChar( 0x1D543 ); if( entity == "lopf" ) return QChar( 0x1D55D ); if( entity == "loplus" ) return QChar( 0x02A2D ); if( entity == "lotimes" ) return QChar( 0x02A34 ); if( entity == "lowast" ) return QChar( 0x02217 ); if( entity == "lowbar" ) return QChar( 0x0005F ); if( entity == "LowerLeftArrow" ) return QChar( 0x02199 ); if( entity == "LowerRightArrow" ) return QChar( 0x02198 ); if( entity == "loz" ) return QChar( 0x025CA ); if( entity == "lozenge" ) return QChar( 0x025CA ); if( entity == "lozf" ) return QChar( 0x029EB ); if( entity == "lpar" ) return QChar( 0x00028 ); if( entity == "lparlt" ) return QChar( 0x02993 ); if( entity == "lrarr" ) return QChar( 0x021C6 ); if( entity == "lrcorner" ) return QChar( 0x0231F ); if( entity == "lrhar" ) return QChar( 0x021CB ); if( entity == "lrhard" ) return QChar( 0x0296D ); if( entity == "lrtri" ) return QChar( 0x022BF ); if( entity == "Lscr" ) return QChar( 0x02112 ); if( entity == "lscr" ) return QChar( 0x1D4C1 ); if( entity == "Lsh" ) return QChar( 0x021B0 ); if( entity == "lsh" ) return QChar( 0x021B0 ); if( entity == "lsim" ) return QChar( 0x02272 ); if( entity == "lsime" ) return QChar( 0x02A8D ); if( entity == "lsimg" ) return QChar( 0x02A8F ); if( entity == "lsqb" ) return QChar( 0x0005B ); if( entity == "lsquo" ) return QChar( 0x02018 ); if( entity == "lsquor" ) return QChar( 0x0201A ); if( entity == "Lstrok" ) return QChar( 0x00141 ); if( entity == "lstrok" ) return QChar( 0x00142 ); if( entity == "Lt" ) return QChar( 0x0226A ); if( entity == "lt" ) return QChar( 0x0003C ); if( entity == "ltcc" ) return QChar( 0x02AA6 ); if( entity == "ltcir" ) return QChar( 0x02A79 ); if( entity == "ltdot" ) return QChar( 0x022D6 ); if( entity == "lthree" ) return QChar( 0x022CB ); if( entity == "ltimes" ) return QChar( 0x022C9 ); if( entity == "ltlarr" ) return QChar( 0x02976 ); if( entity == "ltquest" ) return QChar( 0x02A7B ); if( entity == "ltri" ) return QChar( 0x025C3 ); if( entity == "ltrie" ) return QChar( 0x022B4 ); if( entity == "ltrif" ) return QChar( 0x025C2 ); if( entity == "ltrPar" ) return QChar( 0x02996 ); if( entity == "lurdshar" ) return QChar( 0x0294A ); if( entity == "luruhar" ) return QChar( 0x02966 ); break; case 'm': if( entity == "macr" ) return QChar( 0x000AF ); if( entity == "male" ) return QChar( 0x02642 ); if( entity == "malt" ) return QChar( 0x02720 ); if( entity == "maltese" ) return QChar( 0x02720 ); if( entity == "Map" ) return QChar( 0x02905 ); if( entity == "map" ) return QChar( 0x021A6 ); if( entity == "mapsto" ) return QChar( 0x021A6 ); if( entity == "mapstodown" ) return QChar( 0x021A7 ); if( entity == "mapstoleft" ) return QChar( 0x021A4 ); if( entity == "mapstoup" ) return QChar( 0x021A5 ); if( entity == "marker" ) return QChar( 0x025AE ); if( entity == "mcomma" ) return QChar( 0x02A29 ); if( entity == "Mcy" ) return QChar( 0x0041C ); if( entity == "mcy" ) return QChar( 0x0043C ); if( entity == "mdash" ) return QChar( 0x02014 ); if( entity == "mDDot" ) return QChar( 0x0223A ); if( entity == "measuredangle" ) return QChar( 0x02221 ); if( entity == "MediumSpace" ) return QChar( 0x0205F ); if( entity == "Mellintrf" ) return QChar( 0x02133 ); if( entity == "Mfr" ) return QChar( 0x1D510 ); if( entity == "mfr" ) return QChar( 0x1D52A ); if( entity == "mho" ) return QChar( 0x02127 ); if( entity == "micro" ) return QChar( 0x000B5 ); if( entity == "mid" ) return QChar( 0x02223 ); if( entity == "midast" ) return QChar( 0x0002A ); if( entity == "midcir" ) return QChar( 0x02AF0 ); if( entity == "middot" ) return QChar( 0x000B7 ); if( entity == "minus" ) return QChar( 0x02212 ); if( entity == "minusb" ) return QChar( 0x0229F ); if( entity == "minusd" ) return QChar( 0x02238 ); if( entity == "minusdu" ) return QChar( 0x02A2A ); if( entity == "MinusPlus" ) return QChar( 0x02213 ); if( entity == "mlcp" ) return QChar( 0x02ADB ); if( entity == "mldr" ) return QChar( 0x02026 ); if( entity == "mnplus" ) return QChar( 0x02213 ); if( entity == "models" ) return QChar( 0x022A7 ); if( entity == "Mopf" ) return QChar( 0x1D544 ); if( entity == "mopf" ) return QChar( 0x1D55E ); if( entity == "mp" ) return QChar( 0x02213 ); if( entity == "Mscr" ) return QChar( 0x02133 ); if( entity == "mscr" ) return QChar( 0x1D4C2 ); if( entity == "mstpos" ) return QChar( 0x0223E ); if( entity == "mu" ) return QChar( 0x003BC ); if( entity == "multimap" ) return QChar( 0x022B8 ); if( entity == "mumap" ) return QChar( 0x022B8 ); break; case 'n': if( entity == "nabla" ) return QChar( 0x02207 ); if( entity == "Nacute" ) return QChar( 0x00143 ); if( entity == "nacute" ) return QChar( 0x00144 ); if( entity == "nap" ) return QChar( 0x02249 ); if( entity == "napos" ) return QChar( 0x00149 ); if( entity == "napprox" ) return QChar( 0x02249 ); if( entity == "natur" ) return QChar( 0x0266E ); if( entity == "natural" ) return QChar( 0x0266E ); if( entity == "naturals" ) return QChar( 0x02115 ); if( entity == "nbsp" ) return QChar( 0x000A0 ); if( entity == "ncap" ) return QChar( 0x02A43 ); if( entity == "Ncaron" ) return QChar( 0x00147 ); if( entity == "ncaron" ) return QChar( 0x00148 ); if( entity == "Ncedil" ) return QChar( 0x00145 ); if( entity == "ncedil" ) return QChar( 0x00146 ); if( entity == "ncong" ) return QChar( 0x02247 ); if( entity == "ncup" ) return QChar( 0x02A42 ); if( entity == "Ncy" ) return QChar( 0x0041D ); if( entity == "ncy" ) return QChar( 0x0043D ); if( entity == "ndash" ) return QChar( 0x02013 ); if( entity == "ne" ) return QChar( 0x02260 ); if( entity == "nearhk" ) return QChar( 0x02924 ); if( entity == "neArr" ) return QChar( 0x021D7 ); if( entity == "nearr" ) return QChar( 0x02197 ); if( entity == "nearrow" ) return QChar( 0x02197 ); if( entity == "NegativeMediumSpace" ) return QChar( 0x0200B ); if( entity == "NegativeThickSpace" ) return QChar( 0x0200B ); if( entity == "NegativeThinSpace" ) return QChar( 0x0200B ); if( entity == "NegativeVeryThinSpace" ) return QChar( 0x0200B ); if( entity == "nequiv" ) return QChar( 0x02262 ); if( entity == "nesear" ) return QChar( 0x02928 ); if( entity == "NestedGreaterGreater" ) return QChar( 0x0226B ); if( entity == "NestedLessLess" ) return QChar( 0x0226A ); if( entity == "NewLine" ) return QChar( 0x0000A ); if( entity == "nexist" ) return QChar( 0x02204 ); if( entity == "nexists" ) return QChar( 0x02204 ); if( entity == "Nfr" ) return QChar( 0x1D511 ); if( entity == "nfr" ) return QChar( 0x1D52B ); if( entity == "nge" ) return QChar( 0x02271 ); if( entity == "ngeq" ) return QChar( 0x02271 ); if( entity == "ngsim" ) return QChar( 0x02275 ); if( entity == "ngt" ) return QChar( 0x0226F ); if( entity == "ngtr" ) return QChar( 0x0226F ); if( entity == "nhArr" ) return QChar( 0x021CE ); if( entity == "nharr" ) return QChar( 0x021AE ); if( entity == "nhpar" ) return QChar( 0x02AF2 ); if( entity == "ni" ) return QChar( 0x0220B ); if( entity == "nis" ) return QChar( 0x022FC ); if( entity == "nisd" ) return QChar( 0x022FA ); if( entity == "niv" ) return QChar( 0x0220B ); if( entity == "NJcy" ) return QChar( 0x0040A ); if( entity == "njcy" ) return QChar( 0x0045A ); if( entity == "nlArr" ) return QChar( 0x021CD ); if( entity == "nlarr" ) return QChar( 0x0219A ); if( entity == "nldr" ) return QChar( 0x02025 ); if( entity == "nle" ) return QChar( 0x02270 ); if( entity == "nLeftarrow" ) return QChar( 0x021CD ); if( entity == "nleftarrow" ) return QChar( 0x0219A ); if( entity == "nLeftrightarrow" ) return QChar( 0x021CE ); if( entity == "nleftrightarrow" ) return QChar( 0x021AE ); if( entity == "nleq" ) return QChar( 0x02270 ); if( entity == "nless" ) return QChar( 0x0226E ); if( entity == "nlsim" ) return QChar( 0x02274 ); if( entity == "nlt" ) return QChar( 0x0226E ); if( entity == "nltri" ) return QChar( 0x022EA ); if( entity == "nltrie" ) return QChar( 0x022EC ); if( entity == "nmid" ) return QChar( 0x02224 ); if( entity == "NoBreak" ) return QChar( 0x02060 ); if( entity == "NonBreakingSpace" ) return QChar( 0x000A0 ); if( entity == "Nopf" ) return QChar( 0x02115 ); if( entity == "nopf" ) return QChar( 0x1D55F ); if( entity == "Not" ) return QChar( 0x02AEC ); if( entity == "not" ) return QChar( 0x000AC ); if( entity == "NotCongruent" ) return QChar( 0x02262 ); if( entity == "NotCupCap" ) return QChar( 0x0226D ); if( entity == "NotDoubleVerticalBar" ) return QChar( 0x02226 ); if( entity == "NotElement" ) return QChar( 0x02209 ); if( entity == "NotEqual" ) return QChar( 0x02260 ); if( entity == "NotExists" ) return QChar( 0x02204 ); if( entity == "NotGreater" ) return QChar( 0x0226F ); if( entity == "NotGreaterEqual" ) return QChar( 0x02271 ); if( entity == "NotGreaterLess" ) return QChar( 0x02279 ); if( entity == "NotGreaterTilde" ) return QChar( 0x02275 ); if( entity == "notin" ) return QChar( 0x02209 ); if( entity == "notinva" ) return QChar( 0x02209 ); if( entity == "notinvb" ) return QChar( 0x022F7 ); if( entity == "notinvc" ) return QChar( 0x022F6 ); if( entity == "NotLeftTriangle" ) return QChar( 0x022EA ); if( entity == "NotLeftTriangleEqual" ) return QChar( 0x022EC ); if( entity == "NotLess" ) return QChar( 0x0226E ); if( entity == "NotLessEqual" ) return QChar( 0x02270 ); if( entity == "NotLessGreater" ) return QChar( 0x02278 ); if( entity == "NotLessTilde" ) return QChar( 0x02274 ); if( entity == "notni" ) return QChar( 0x0220C ); if( entity == "notniva" ) return QChar( 0x0220C ); if( entity == "notnivb" ) return QChar( 0x022FE ); if( entity == "notnivc" ) return QChar( 0x022FD ); if( entity == "NotPrecedes" ) return QChar( 0x02280 ); if( entity == "NotPrecedesSlantEqual" ) return QChar( 0x022E0 ); if( entity == "NotReverseElement" ) return QChar( 0x0220C ); if( entity == "NotRightTriangle" ) return QChar( 0x022EB ); if( entity == "NotRightTriangleEqual" ) return QChar( 0x022ED ); if( entity == "NotSquareSubsetEqual" ) return QChar( 0x022E2 ); if( entity == "NotSquareSupersetEqual" ) return QChar( 0x022E3 ); if( entity == "NotSubsetEqual" ) return QChar( 0x02288 ); if( entity == "NotSucceeds" ) return QChar( 0x02281 ); if( entity == "NotSucceedsSlantEqual" ) return QChar( 0x022E1 ); if( entity == "NotSupersetEqual" ) return QChar( 0x02289 ); if( entity == "NotTilde" ) return QChar( 0x02241 ); if( entity == "NotTildeEqual" ) return QChar( 0x02244 ); if( entity == "NotTildeFullEqual" ) return QChar( 0x02247 ); if( entity == "NotTildeTilde" ) return QChar( 0x02249 ); if( entity == "NotVerticalBar" ) return QChar( 0x02224 ); if( entity == "npar" ) return QChar( 0x02226 ); if( entity == "nparallel" ) return QChar( 0x02226 ); if( entity == "npolint" ) return QChar( 0x02A14 ); if( entity == "npr" ) return QChar( 0x02280 ); if( entity == "nprcue" ) return QChar( 0x022E0 ); if( entity == "nprec" ) return QChar( 0x02280 ); if( entity == "nrArr" ) return QChar( 0x021CF ); if( entity == "nrarr" ) return QChar( 0x0219B ); if( entity == "nRightarrow" ) return QChar( 0x021CF ); if( entity == "nrightarrow" ) return QChar( 0x0219B ); if( entity == "nrtri" ) return QChar( 0x022EB ); if( entity == "nrtrie" ) return QChar( 0x022ED ); if( entity == "nsc" ) return QChar( 0x02281 ); if( entity == "nsccue" ) return QChar( 0x022E1 ); if( entity == "Nscr" ) return QChar( 0x1D4A9 ); if( entity == "nscr" ) return QChar( 0x1D4C3 ); if( entity == "nshortmid" ) return QChar( 0x02224 ); if( entity == "nshortparallel" ) return QChar( 0x02226 ); if( entity == "nsim" ) return QChar( 0x02241 ); if( entity == "nsime" ) return QChar( 0x02244 ); if( entity == "nsimeq" ) return QChar( 0x02244 ); if( entity == "nsmid" ) return QChar( 0x02224 ); if( entity == "nspar" ) return QChar( 0x02226 ); if( entity == "nsqsube" ) return QChar( 0x022E2 ); if( entity == "nsqsupe" ) return QChar( 0x022E3 ); if( entity == "nsub" ) return QChar( 0x02284 ); if( entity == "nsube" ) return QChar( 0x02288 ); if( entity == "nsubseteq" ) return QChar( 0x02288 ); if( entity == "nsucc" ) return QChar( 0x02281 ); if( entity == "nsup" ) return QChar( 0x02285 ); if( entity == "nsupe" ) return QChar( 0x02289 ); if( entity == "nsupseteq" ) return QChar( 0x02289 ); if( entity == "ntgl" ) return QChar( 0x02279 ); if( entity == "Ntilde" ) return QChar( 0x000D1 ); if( entity == "ntilde" ) return QChar( 0x000F1 ); if( entity == "ntlg" ) return QChar( 0x02278 ); if( entity == "ntriangleleft" ) return QChar( 0x022EA ); if( entity == "ntrianglelefteq" ) return QChar( 0x022EC ); if( entity == "ntriangleright" ) return QChar( 0x022EB ); if( entity == "ntrianglerighteq" ) return QChar( 0x022ED ); if( entity == "nu" ) return QChar( 0x003BD ); if( entity == "num" ) return QChar( 0x00023 ); if( entity == "numero" ) return QChar( 0x02116 ); if( entity == "numsp" ) return QChar( 0x02007 ); if( entity == "nVDash" ) return QChar( 0x022AF ); if( entity == "nVdash" ) return QChar( 0x022AE ); if( entity == "nvDash" ) return QChar( 0x022AD ); if( entity == "nvdash" ) return QChar( 0x022AC ); if( entity == "nvHarr" ) return QChar( 0x02904 ); if( entity == "nvinfin" ) return QChar( 0x029DE ); if( entity == "nvlArr" ) return QChar( 0x02902 ); if( entity == "nvrArr" ) return QChar( 0x02903 ); if( entity == "nwarhk" ) return QChar( 0x02923 ); if( entity == "nwArr" ) return QChar( 0x021D6 ); if( entity == "nwarr" ) return QChar( 0x02196 ); if( entity == "nwarrow" ) return QChar( 0x02196 ); if( entity == "nwnear" ) return QChar( 0x02927 ); break; case 'o': if( entity == "Oacute" ) return QChar( 0x000D3 ); if( entity == "oacute" ) return QChar( 0x000F3 ); if( entity == "oast" ) return QChar( 0x0229B ); if( entity == "ocir" ) return QChar( 0x0229A ); if( entity == "Ocirc" ) return QChar( 0x000D4 ); if( entity == "ocirc" ) return QChar( 0x000F4 ); if( entity == "Ocy" ) return QChar( 0x0041E ); if( entity == "ocy" ) return QChar( 0x0043E ); if( entity == "odash" ) return QChar( 0x0229D ); if( entity == "Odblac" ) return QChar( 0x00150 ); if( entity == "odblac" ) return QChar( 0x00151 ); if( entity == "odiv" ) return QChar( 0x02A38 ); if( entity == "odot" ) return QChar( 0x02299 ); if( entity == "odsold" ) return QChar( 0x029BC ); if( entity == "OElig" ) return QChar( 0x00152 ); if( entity == "oelig" ) return QChar( 0x00153 ); if( entity == "ofcir" ) return QChar( 0x029BF ); if( entity == "Ofr" ) return QChar( 0x1D512 ); if( entity == "ofr" ) return QChar( 0x1D52C ); if( entity == "ogon" ) return QChar( 0x002DB ); if( entity == "Ograve" ) return QChar( 0x000D2 ); if( entity == "ograve" ) return QChar( 0x000F2 ); if( entity == "ogt" ) return QChar( 0x029C1 ); if( entity == "ohbar" ) return QChar( 0x029B5 ); if( entity == "ohm" ) return QChar( 0x02126 ); if( entity == "oint" ) return QChar( 0x0222E ); if( entity == "olarr" ) return QChar( 0x021BA ); if( entity == "olcir" ) return QChar( 0x029BE ); if( entity == "olcross" ) return QChar( 0x029BB ); if( entity == "olt" ) return QChar( 0x029C0 ); if( entity == "Omacr" ) return QChar( 0x0014C ); if( entity == "omacr" ) return QChar( 0x0014D ); if( entity == "Omega" ) return QChar( 0x003A9 ); if( entity == "omega" ) return QChar( 0x003C9 ); if( entity == "omid" ) return QChar( 0x029B6 ); if( entity == "ominus" ) return QChar( 0x02296 ); if( entity == "Oopf" ) return QChar( 0x1D546 ); if( entity == "oopf" ) return QChar( 0x1D560 ); if( entity == "opar" ) return QChar( 0x029B7 ); if( entity == "OpenCurlyDoubleQuote" ) return QChar( 0x0201C ); if( entity == "OpenCurlyQuote" ) return QChar( 0x02018 ); if( entity == "operp" ) return QChar( 0x029B9 ); if( entity == "oplus" ) return QChar( 0x02295 ); if( entity == "Or" ) return QChar( 0x02A54 ); if( entity == "or" ) return QChar( 0x02228 ); if( entity == "orarr" ) return QChar( 0x021BB ); if( entity == "ord" ) return QChar( 0x02A5D ); if( entity == "order" ) return QChar( 0x02134 ); if( entity == "orderof" ) return QChar( 0x02134 ); if( entity == "ordf" ) return QChar( 0x000AA ); if( entity == "ordm" ) return QChar( 0x000BA ); if( entity == "origof" ) return QChar( 0x022B6 ); if( entity == "oror" ) return QChar( 0x02A56 ); if( entity == "orslope" ) return QChar( 0x02A57 ); if( entity == "orv" ) return QChar( 0x02A5B ); if( entity == "oS" ) return QChar( 0x024C8 ); if( entity == "Oscr" ) return QChar( 0x1D4AA ); if( entity == "oscr" ) return QChar( 0x02134 ); if( entity == "Oslash" ) return QChar( 0x000D8 ); if( entity == "oslash" ) return QChar( 0x000F8 ); if( entity == "osol" ) return QChar( 0x02298 ); if( entity == "Otilde" ) return QChar( 0x000D5 ); if( entity == "otilde" ) return QChar( 0x000F5 ); if( entity == "Otimes" ) return QChar( 0x02A37 ); if( entity == "otimes" ) return QChar( 0x02297 ); if( entity == "otimesas" ) return QChar( 0x02A36 ); if( entity == "Ouml" ) return QChar( 0x000D6 ); if( entity == "ouml" ) return QChar( 0x000F6 ); if( entity == "ovbar" ) return QChar( 0x0233D ); if( entity == "OverBar" ) return QChar( 0x000AF ); if( entity == "OverBrace" ) return QChar( 0x0FE37 ); if( entity == "OverBracket" ) return QChar( 0x023B4 ); if( entity == "OverParenthesis" ) return QChar( 0x0FE35 ); break; case 'p': if( entity == "par" ) return QChar( 0x02225 ); if( entity == "para" ) return QChar( 0x000B6 ); if( entity == "parallel" ) return QChar( 0x02225 ); if( entity == "parsim" ) return QChar( 0x02AF3 ); if( entity == "parsl" ) return QChar( 0x02AFD ); if( entity == "part" ) return QChar( 0x02202 ); if( entity == "PartialD" ) return QChar( 0x02202 ); if( entity == "Pcy" ) return QChar( 0x0041F ); if( entity == "pcy" ) return QChar( 0x0043F ); if( entity == "percnt" ) return QChar( 0x00025 ); if( entity == "period" ) return QChar( 0x0002E ); if( entity == "permil" ) return QChar( 0x02030 ); if( entity == "perp" ) return QChar( 0x022A5 ); if( entity == "pertenk" ) return QChar( 0x02031 ); if( entity == "Pfr" ) return QChar( 0x1D513 ); if( entity == "pfr" ) return QChar( 0x1D52D ); if( entity == "Phi" ) return QChar( 0x003A6 ); if( entity == "phi" ) return QChar( 0x003D5 ); if( entity == "phiv" ) return QChar( 0x003C6 ); if( entity == "phmmat" ) return QChar( 0x02133 ); if( entity == "phone" ) return QChar( 0x0260E ); if( entity == "Pi" ) return QChar( 0x003A0 ); if( entity == "pi" ) return QChar( 0x003C0 ); if( entity == "pitchfork" ) return QChar( 0x022D4 ); if( entity == "piv" ) return QChar( 0x003D6 ); if( entity == "planck" ) return QChar( 0x0210F ); if( entity == "planckh" ) return QChar( 0x0210E ); if( entity == "plankv" ) return QChar( 0x0210F ); if( entity == "plus" ) return QChar( 0x0002B ); if( entity == "plusacir" ) return QChar( 0x02A23 ); if( entity == "plusb" ) return QChar( 0x0229E ); if( entity == "pluscir" ) return QChar( 0x02A22 ); if( entity == "plusdo" ) return QChar( 0x02214 ); if( entity == "plusdu" ) return QChar( 0x02A25 ); if( entity == "pluse" ) return QChar( 0x02A72 ); if( entity == "PlusMinus" ) return QChar( 0x000B1 ); if( entity == "plusmn" ) return QChar( 0x000B1 ); if( entity == "plussim" ) return QChar( 0x02A26 ); if( entity == "plustwo" ) return QChar( 0x02A27 ); if( entity == "pm" ) return QChar( 0x000B1 ); if( entity == "Poincareplane" ) return QChar( 0x0210C ); if( entity == "pointint" ) return QChar( 0x02A15 ); if( entity == "Popf" ) return QChar( 0x02119 ); if( entity == "popf" ) return QChar( 0x1D561 ); if( entity == "pound" ) return QChar( 0x000A3 ); if( entity == "Pr" ) return QChar( 0x02ABB ); if( entity == "pr" ) return QChar( 0x0227A ); if( entity == "prap" ) return QChar( 0x02AB7 ); if( entity == "prcue" ) return QChar( 0x0227C ); if( entity == "prE" ) return QChar( 0x02AB3 ); if( entity == "pre" ) return QChar( 0x02AAF ); if( entity == "prec" ) return QChar( 0x0227A ); if( entity == "precapprox" ) return QChar( 0x02AB7 ); if( entity == "preccurlyeq" ) return QChar( 0x0227C ); if( entity == "Precedes" ) return QChar( 0x0227A ); if( entity == "PrecedesEqual" ) return QChar( 0x02AAF ); if( entity == "PrecedesSlantEqual" ) return QChar( 0x0227C ); if( entity == "PrecedesTilde" ) return QChar( 0x0227E ); if( entity == "preceq" ) return QChar( 0x02AAF ); if( entity == "precnapprox" ) return QChar( 0x02AB9 ); if( entity == "precneqq" ) return QChar( 0x02AB5 ); if( entity == "precnsim" ) return QChar( 0x022E8 ); if( entity == "precsim" ) return QChar( 0x0227E ); if( entity == "Prime" ) return QChar( 0x02033 ); if( entity == "prime" ) return QChar( 0x02032 ); if( entity == "primes" ) return QChar( 0x02119 ); if( entity == "prnap" ) return QChar( 0x02AB9 ); if( entity == "prnE" ) return QChar( 0x02AB5 ); if( entity == "prnsim" ) return QChar( 0x022E8 ); if( entity == "prod" ) return QChar( 0x0220F ); if( entity == "Product" ) return QChar( 0x0220F ); if( entity == "profalar" ) return QChar( 0x0232E ); if( entity == "profline" ) return QChar( 0x02312 ); if( entity == "profsurf" ) return QChar( 0x02313 ); if( entity == "prop" ) return QChar( 0x0221D ); if( entity == "Proportion" ) return QChar( 0x02237 ); if( entity == "Proportional" ) return QChar( 0x0221D ); if( entity == "propto" ) return QChar( 0x0221D ); if( entity == "prsim" ) return QChar( 0x0227E ); if( entity == "prurel" ) return QChar( 0x022B0 ); if( entity == "Pscr" ) return QChar( 0x1D4AB ); if( entity == "pscr" ) return QChar( 0x1D4C5 ); if( entity == "Psi" ) return QChar( 0x003A8 ); if( entity == "psi" ) return QChar( 0x003C8 ); if( entity == "puncsp" ) return QChar( 0x02008 ); break; case 'q': if( entity == "Qfr" ) return QChar( 0x1D514 ); if( entity == "qfr" ) return QChar( 0x1D52E ); if( entity == "qint" ) return QChar( 0x02A0C ); if( entity == "Qopf" ) return QChar( 0x0211A ); if( entity == "qopf" ) return QChar( 0x1D562 ); if( entity == "qprime" ) return QChar( 0x02057 ); if( entity == "Qscr" ) return QChar( 0x1D4AC ); if( entity == "qscr" ) return QChar( 0x1D4C6 ); if( entity == "quaternions" ) return QChar( 0x0210D ); if( entity == "quatint" ) return QChar( 0x02A16 ); if( entity == "quest" ) return QChar( 0x0003F ); if( entity == "questeq" ) return QChar( 0x0225F ); if( entity == "quot" ) return QChar( 0x00022 ); break; case 'r': if( entity == "rAarr" ) return QChar( 0x021DB ); if( entity == "race" ) return QChar( 0x029DA ); if( entity == "Racute" ) return QChar( 0x00154 ); if( entity == "racute" ) return QChar( 0x00155 ); if( entity == "radic" ) return QChar( 0x0221A ); if( entity == "raemptyv" ) return QChar( 0x029B3 ); if( entity == "Rang" ) return QChar( 0x0300B ); if( entity == "rang" ) return QChar( 0x0232A ); if( entity == "rangd" ) return QChar( 0x02992 ); if( entity == "range" ) return QChar( 0x029A5 ); if( entity == "rangle" ) return QChar( 0x0232A ); if( entity == "raquo" ) return QChar( 0x000BB ); if( entity == "Rarr" ) return QChar( 0x021A0 ); if( entity == "rArr" ) return QChar( 0x021D2 ); if( entity == "rarr" ) return QChar( 0x02192 ); if( entity == "rarrap" ) return QChar( 0x02975 ); if( entity == "rarrb" ) return QChar( 0x021E5 ); if( entity == "rarrbfs" ) return QChar( 0x02920 ); if( entity == "rarrc" ) return QChar( 0x02933 ); if( entity == "rarrfs" ) return QChar( 0x0291E ); if( entity == "rarrhk" ) return QChar( 0x021AA ); if( entity == "rarrlp" ) return QChar( 0x021AC ); if( entity == "rarrpl" ) return QChar( 0x02945 ); if( entity == "rarrsim" ) return QChar( 0x02974 ); if( entity == "Rarrtl" ) return QChar( 0x02916 ); if( entity == "rarrtl" ) return QChar( 0x021A3 ); if( entity == "rarrw" ) return QChar( 0x0219D ); if( entity == "rAtail" ) return QChar( 0x0291C ); if( entity == "ratail" ) return QChar( 0x0291A ); if( entity == "ratio" ) return QChar( 0x02236 ); if( entity == "rationals" ) return QChar( 0x0211A ); if( entity == "RBarr" ) return QChar( 0x02910 ); if( entity == "rBarr" ) return QChar( 0x0290F ); if( entity == "rbarr" ) return QChar( 0x0290D ); if( entity == "rbbrk" ) return QChar( 0x03015 ); if( entity == "rbrace" ) return QChar( 0x0007D ); if( entity == "rbrack" ) return QChar( 0x0005D ); if( entity == "rbrke" ) return QChar( 0x0298C ); if( entity == "rbrksld" ) return QChar( 0x0298E ); if( entity == "rbrkslu" ) return QChar( 0x02990 ); if( entity == "Rcaron" ) return QChar( 0x00158 ); if( entity == "rcaron" ) return QChar( 0x00159 ); if( entity == "Rcedil" ) return QChar( 0x00156 ); if( entity == "rcedil" ) return QChar( 0x00157 ); if( entity == "rceil" ) return QChar( 0x02309 ); if( entity == "rcub" ) return QChar( 0x0007D ); if( entity == "Rcy" ) return QChar( 0x00420 ); if( entity == "rcy" ) return QChar( 0x00440 ); if( entity == "rdca" ) return QChar( 0x02937 ); if( entity == "rdldhar" ) return QChar( 0x02969 ); if( entity == "rdquo" ) return QChar( 0x0201D ); if( entity == "rdquor" ) return QChar( 0x0201D ); if( entity == "rdsh" ) return QChar( 0x021B3 ); if( entity == "Re" ) return QChar( 0x0211C ); if( entity == "real" ) return QChar( 0x0211C ); if( entity == "realine" ) return QChar( 0x0211B ); if( entity == "realpart" ) return QChar( 0x0211C ); if( entity == "reals" ) return QChar( 0x0211D ); if( entity == "rect" ) return QChar( 0x025AD ); if( entity == "reg" ) return QChar( 0x000AE ); if( entity == "ReverseElement" ) return QChar( 0x0220B ); if( entity == "ReverseEquilibrium" ) return QChar( 0x021CB ); if( entity == "ReverseUpEquilibrium" ) return QChar( 0x0296F ); if( entity == "rfisht" ) return QChar( 0x0297D ); if( entity == "rfloor" ) return QChar( 0x0230B ); if( entity == "Rfr" ) return QChar( 0x0211C ); if( entity == "rfr" ) return QChar( 0x1D52F ); if( entity == "rHar" ) return QChar( 0x02964 ); if( entity == "rhard" ) return QChar( 0x021C1 ); if( entity == "rharu" ) return QChar( 0x021C0 ); if( entity == "rharul" ) return QChar( 0x0296C ); if( entity == "rho" ) return QChar( 0x003C1 ); if( entity == "rhov" ) return QChar( 0x003F1 ); if( entity == "RightAngleBracket" ) return QChar( 0x0232A ); if( entity == "RightArrow" ) return QChar( 0x02192 ); if( entity == "Rightarrow" ) return QChar( 0x021D2 ); if( entity == "rightarrow" ) return QChar( 0x02192 ); if( entity == "RightArrowBar" ) return QChar( 0x021E5 ); if( entity == "RightArrowLeftArrow" ) return QChar( 0x021C4 ); if( entity == "rightarrowtail" ) return QChar( 0x021A3 ); if( entity == "RightCeiling" ) return QChar( 0x02309 ); if( entity == "RightDoubleBracket" ) return QChar( 0x0301B ); if( entity == "RightDownTeeVector" ) return QChar( 0x0295D ); if( entity == "RightDownVector" ) return QChar( 0x021C2 ); if( entity == "RightDownVectorBar" ) return QChar( 0x02955 ); if( entity == "RightFloor" ) return QChar( 0x0230B ); if( entity == "rightharpoondown" ) return QChar( 0x021C1 ); if( entity == "rightharpoonup" ) return QChar( 0x021C0 ); if( entity == "rightleftarrows" ) return QChar( 0x021C4 ); if( entity == "rightleftharpoons" ) return QChar( 0x021CC ); if( entity == "rightrightarrows" ) return QChar( 0x021C9 ); if( entity == "rightsquigarrow" ) return QChar( 0x0219D ); if( entity == "RightTee" ) return QChar( 0x022A2 ); if( entity == "RightTeeArrow" ) return QChar( 0x021A6 ); if( entity == "RightTeeVector" ) return QChar( 0x0295B ); if( entity == "rightthreetimes" ) return QChar( 0x022CC ); if( entity == "RightTriangle" ) return QChar( 0x022B3 ); if( entity == "RightTriangleBar" ) return QChar( 0x029D0 ); if( entity == "RightTriangleEqual" ) return QChar( 0x022B5 ); if( entity == "RightUpDownVector" ) return QChar( 0x0294F ); if( entity == "RightUpTeeVector" ) return QChar( 0x0295C ); if( entity == "RightUpVector" ) return QChar( 0x021BE ); if( entity == "RightUpVectorBar" ) return QChar( 0x02954 ); if( entity == "RightVector" ) return QChar( 0x021C0 ); if( entity == "RightVectorBar" ) return QChar( 0x02953 ); if( entity == "ring" ) return QChar( 0x002DA ); if( entity == "risingdotseq" ) return QChar( 0x02253 ); if( entity == "rlarr" ) return QChar( 0x021C4 ); if( entity == "rlhar" ) return QChar( 0x021CC ); if( entity == "rmoust" ) return QChar( 0x023B1 ); if( entity == "rmoustache" ) return QChar( 0x023B1 ); if( entity == "rnmid" ) return QChar( 0x02AEE ); if( entity == "roang" ) return QChar( 0x03019 ); if( entity == "roarr" ) return QChar( 0x021FE ); if( entity == "robrk" ) return QChar( 0x0301B ); if( entity == "ropar" ) return QChar( 0x02986 ); if( entity == "Ropf" ) return QChar( 0x0211D ); if( entity == "ropf" ) return QChar( 0x1D563 ); if( entity == "roplus" ) return QChar( 0x02A2E ); if( entity == "rotimes" ) return QChar( 0x02A35 ); if( entity == "RoundImplies" ) return QChar( 0x02970 ); if( entity == "rpar" ) return QChar( 0x00029 ); if( entity == "rpargt" ) return QChar( 0x02994 ); if( entity == "rppolint" ) return QChar( 0x02A12 ); if( entity == "rrarr" ) return QChar( 0x021C9 ); if( entity == "Rrightarrow" ) return QChar( 0x021DB ); if( entity == "Rscr" ) return QChar( 0x0211B ); if( entity == "rscr" ) return QChar( 0x1D4C7 ); if( entity == "Rsh" ) return QChar( 0x021B1 ); if( entity == "rsh" ) return QChar( 0x021B1 ); if( entity == "rsqb" ) return QChar( 0x0005D ); if( entity == "rsquo" ) return QChar( 0x02019 ); if( entity == "rsquor" ) return QChar( 0x02019 ); if( entity == "rthree" ) return QChar( 0x022CC ); if( entity == "rtimes" ) return QChar( 0x022CA ); if( entity == "rtri" ) return QChar( 0x025B9 ); if( entity == "rtrie" ) return QChar( 0x022B5 ); if( entity == "rtrif" ) return QChar( 0x025B8 ); if( entity == "rtriltri" ) return QChar( 0x029CE ); if( entity == "RuleDelayed" ) return QChar( 0x029F4 ); if( entity == "ruluhar" ) return QChar( 0x02968 ); if( entity == "rx" ) return QChar( 0x0211E ); break; case 's': if( entity == "Sacute" ) return QChar( 0x0015A ); if( entity == "sacute" ) return QChar( 0x0015B ); if( entity == "Sc" ) return QChar( 0x02ABC ); if( entity == "sc" ) return QChar( 0x0227B ); if( entity == "scap" ) return QChar( 0x02AB8 ); if( entity == "Scaron" ) return QChar( 0x00160 ); if( entity == "scaron" ) return QChar( 0x00161 ); if( entity == "sccue" ) return QChar( 0x0227D ); if( entity == "scE" ) return QChar( 0x02AB4 ); if( entity == "sce" ) return QChar( 0x02AB0 ); if( entity == "Scedil" ) return QChar( 0x0015E ); if( entity == "scedil" ) return QChar( 0x0015F ); if( entity == "Scirc" ) return QChar( 0x0015C ); if( entity == "scirc" ) return QChar( 0x0015D ); if( entity == "scnap" ) return QChar( 0x02ABA ); if( entity == "scnE" ) return QChar( 0x02AB6 ); if( entity == "scnsim" ) return QChar( 0x022E9 ); if( entity == "scpolint" ) return QChar( 0x02A13 ); if( entity == "scsim" ) return QChar( 0x0227F ); if( entity == "Scy" ) return QChar( 0x00421 ); if( entity == "scy" ) return QChar( 0x00441 ); if( entity == "sdot" ) return QChar( 0x022C5 ); if( entity == "sdotb" ) return QChar( 0x022A1 ); if( entity == "sdote" ) return QChar( 0x02A66 ); if( entity == "searhk" ) return QChar( 0x02925 ); if( entity == "seArr" ) return QChar( 0x021D8 ); if( entity == "searr" ) return QChar( 0x02198 ); if( entity == "searrow" ) return QChar( 0x02198 ); if( entity == "sect" ) return QChar( 0x000A7 ); if( entity == "semi" ) return QChar( 0x0003B ); if( entity == "seswar" ) return QChar( 0x02929 ); if( entity == "setminus" ) return QChar( 0x02216 ); if( entity == "setmn" ) return QChar( 0x02216 ); if( entity == "sext" ) return QChar( 0x02736 ); if( entity == "Sfr" ) return QChar( 0x1D516 ); if( entity == "sfr" ) return QChar( 0x1D530 ); if( entity == "sfrown" ) return QChar( 0x02322 ); if( entity == "sharp" ) return QChar( 0x0266F ); if( entity == "SHCHcy" ) return QChar( 0x00429 ); if( entity == "shchcy" ) return QChar( 0x00449 ); if( entity == "SHcy" ) return QChar( 0x00428 ); if( entity == "shcy" ) return QChar( 0x00448 ); if( entity == "ShortDownArrow" ) return QChar( 0x02193 ); if( entity == "ShortLeftArrow" ) return QChar( 0x02190 ); if( entity == "shortmid" ) return QChar( 0x02223 ); if( entity == "shortparallel" ) return QChar( 0x02225 ); if( entity == "ShortRightArrow" ) return QChar( 0x02192 ); if( entity == "ShortUpArrow" ) return QChar( 0x02191 ); if( entity == "shy" ) return QChar( 0x000AD ); if( entity == "Sigma" ) return QChar( 0x003A3 ); if( entity == "sigma" ) return QChar( 0x003C3 ); if( entity == "sigmav" ) return QChar( 0x003C2 ); if( entity == "sim" ) return QChar( 0x0223C ); if( entity == "simdot" ) return QChar( 0x02A6A ); if( entity == "sime" ) return QChar( 0x02243 ); if( entity == "simeq" ) return QChar( 0x02243 ); if( entity == "simg" ) return QChar( 0x02A9E ); if( entity == "simgE" ) return QChar( 0x02AA0 ); if( entity == "siml" ) return QChar( 0x02A9D ); if( entity == "simlE" ) return QChar( 0x02A9F ); if( entity == "simne" ) return QChar( 0x02246 ); if( entity == "simplus" ) return QChar( 0x02A24 ); if( entity == "simrarr" ) return QChar( 0x02972 ); if( entity == "slarr" ) return QChar( 0x02190 ); if( entity == "SmallCircle" ) return QChar( 0x02218 ); if( entity == "smallsetminus" ) return QChar( 0x02216 ); if( entity == "smashp" ) return QChar( 0x02A33 ); if( entity == "smeparsl" ) return QChar( 0x029E4 ); if( entity == "smid" ) return QChar( 0x02223 ); if( entity == "smile" ) return QChar( 0x02323 ); if( entity == "smt" ) return QChar( 0x02AAA ); if( entity == "smte" ) return QChar( 0x02AAC ); if( entity == "SOFTcy" ) return QChar( 0x0042C ); if( entity == "softcy" ) return QChar( 0x0044C ); if( entity == "sol" ) return QChar( 0x0002F ); if( entity == "solb" ) return QChar( 0x029C4 ); if( entity == "solbar" ) return QChar( 0x0233F ); if( entity == "Sopf" ) return QChar( 0x1D54A ); if( entity == "sopf" ) return QChar( 0x1D564 ); if( entity == "spades" ) return QChar( 0x02660 ); if( entity == "spadesuit" ) return QChar( 0x02660 ); if( entity == "spar" ) return QChar( 0x02225 ); if( entity == "sqcap" ) return QChar( 0x02293 ); if( entity == "sqcup" ) return QChar( 0x02294 ); if( entity == "Sqrt" ) return QChar( 0x0221A ); if( entity == "sqsub" ) return QChar( 0x0228F ); if( entity == "sqsube" ) return QChar( 0x02291 ); if( entity == "sqsubset" ) return QChar( 0x0228F ); if( entity == "sqsubseteq" ) return QChar( 0x02291 ); if( entity == "sqsup" ) return QChar( 0x02290 ); if( entity == "sqsupe" ) return QChar( 0x02292 ); if( entity == "sqsupset" ) return QChar( 0x02290 ); if( entity == "sqsupseteq" ) return QChar( 0x02292 ); if( entity == "squ" ) return QChar( 0x025A1 ); if( entity == "Square" ) return QChar( 0x025A1 ); if( entity == "square" ) return QChar( 0x025A1 ); if( entity == "SquareIntersection" ) return QChar( 0x02293 ); if( entity == "SquareSubset" ) return QChar( 0x0228F ); if( entity == "SquareSubsetEqual" ) return QChar( 0x02291 ); if( entity == "SquareSuperset" ) return QChar( 0x02290 ); if( entity == "SquareSupersetEqual" ) return QChar( 0x02292 ); if( entity == "SquareUnion" ) return QChar( 0x02294 ); if( entity == "squarf" ) return QChar( 0x025AA ); if( entity == "squf" ) return QChar( 0x025AA ); if( entity == "srarr" ) return QChar( 0x02192 ); if( entity == "Sscr" ) return QChar( 0x1D4AE ); if( entity == "sscr" ) return QChar( 0x1D4C8 ); if( entity == "ssetmn" ) return QChar( 0x02216 ); if( entity == "ssmile" ) return QChar( 0x02323 ); if( entity == "sstarf" ) return QChar( 0x022C6 ); if( entity == "Star" ) return QChar( 0x022C6 ); if( entity == "star" ) return QChar( 0x02606 ); if( entity == "starf" ) return QChar( 0x02605 ); if( entity == "straightepsilon" ) return QChar( 0x003F5 ); if( entity == "straightphi" ) return QChar( 0x003D5 ); if( entity == "strns" ) return QChar( 0x000AF ); if( entity == "Sub" ) return QChar( 0x022D0 ); if( entity == "sub" ) return QChar( 0x02282 ); if( entity == "subdot" ) return QChar( 0x02ABD ); if( entity == "subE" ) return QChar( 0x02AC5 ); if( entity == "sube" ) return QChar( 0x02286 ); if( entity == "subedot" ) return QChar( 0x02AC3 ); if( entity == "submult" ) return QChar( 0x02AC1 ); if( entity == "subnE" ) return QChar( 0x02ACB ); if( entity == "subne" ) return QChar( 0x0228A ); if( entity == "subplus" ) return QChar( 0x02ABF ); if( entity == "subrarr" ) return QChar( 0x02979 ); if( entity == "Subset" ) return QChar( 0x022D0 ); if( entity == "subset" ) return QChar( 0x02282 ); if( entity == "subseteq" ) return QChar( 0x02286 ); if( entity == "subseteqq" ) return QChar( 0x02AC5 ); if( entity == "SubsetEqual" ) return QChar( 0x02286 ); if( entity == "subsetneq" ) return QChar( 0x0228A ); if( entity == "subsetneqq" ) return QChar( 0x02ACB ); if( entity == "subsim" ) return QChar( 0x02AC7 ); if( entity == "subsub" ) return QChar( 0x02AD5 ); if( entity == "subsup" ) return QChar( 0x02AD3 ); if( entity == "succ" ) return QChar( 0x0227B ); if( entity == "succapprox" ) return QChar( 0x02AB8 ); if( entity == "succcurlyeq" ) return QChar( 0x0227D ); if( entity == "Succeeds" ) return QChar( 0x0227B ); if( entity == "SucceedsEqual" ) return QChar( 0x02AB0 ); if( entity == "SucceedsSlantEqual" ) return QChar( 0x0227D ); if( entity == "SucceedsTilde" ) return QChar( 0x0227F ); if( entity == "succeq" ) return QChar( 0x02AB0 ); if( entity == "succnapprox" ) return QChar( 0x02ABA ); if( entity == "succneqq" ) return QChar( 0x02AB6 ); if( entity == "succnsim" ) return QChar( 0x022E9 ); if( entity == "succsim" ) return QChar( 0x0227F ); if( entity == "SuchThat" ) return QChar( 0x0220B ); if( entity == "Sum" ) return QChar( 0x02211 ); if( entity == "sum" ) return QChar( 0x02211 ); if( entity == "sung" ) return QChar( 0x0266A ); if( entity == "Sup" ) return QChar( 0x022D1 ); if( entity == "sup" ) return QChar( 0x02283 ); if( entity == "sup1" ) return QChar( 0x000B9 ); if( entity == "sup2" ) return QChar( 0x000B2 ); if( entity == "sup3" ) return QChar( 0x000B3 ); if( entity == "supdot" ) return QChar( 0x02ABE ); if( entity == "supdsub" ) return QChar( 0x02AD8 ); if( entity == "supE" ) return QChar( 0x02AC6 ); if( entity == "supe" ) return QChar( 0x02287 ); if( entity == "supedot" ) return QChar( 0x02AC4 ); if( entity == "Superset" ) return QChar( 0x02283 ); if( entity == "SupersetEqual" ) return QChar( 0x02287 ); if( entity == "suphsub" ) return QChar( 0x02AD7 ); if( entity == "suplarr" ) return QChar( 0x0297B ); if( entity == "supmult" ) return QChar( 0x02AC2 ); if( entity == "supnE" ) return QChar( 0x02ACC ); if( entity == "supne" ) return QChar( 0x0228B ); if( entity == "supplus" ) return QChar( 0x02AC0 ); if( entity == "Supset" ) return QChar( 0x022D1 ); if( entity == "supset" ) return QChar( 0x02283 ); if( entity == "supseteq" ) return QChar( 0x02287 ); if( entity == "supseteqq" ) return QChar( 0x02AC6 ); if( entity == "supsetneq" ) return QChar( 0x0228B ); if( entity == "supsetneqq" ) return QChar( 0x02ACC ); if( entity == "supsim" ) return QChar( 0x02AC8 ); if( entity == "supsub" ) return QChar( 0x02AD4 ); if( entity == "supsup" ) return QChar( 0x02AD6 ); if( entity == "swarhk" ) return QChar( 0x02926 ); if( entity == "swArr" ) return QChar( 0x021D9 ); if( entity == "swarr" ) return QChar( 0x02199 ); if( entity == "swarrow" ) return QChar( 0x02199 ); if( entity == "swnwar" ) return QChar( 0x0292A ); if( entity == "szlig" ) return QChar( 0x000DF ); break; case 't': if( entity == "Tab" ) return QChar( 0x00009 ); if( entity == "target" ) return QChar( 0x02316 ); if( entity == "tau" ) return QChar( 0x003C4 ); if( entity == "tbrk" ) return QChar( 0x023B4 ); if( entity == "Tcaron" ) return QChar( 0x00164 ); if( entity == "tcaron" ) return QChar( 0x00165 ); if( entity == "Tcedil" ) return QChar( 0x00162 ); if( entity == "tcedil" ) return QChar( 0x00163 ); if( entity == "Tcy" ) return QChar( 0x00422 ); if( entity == "tcy" ) return QChar( 0x00442 ); if( entity == "tdot" ) return QChar( 0x020DB ); if( entity == "telrec" ) return QChar( 0x02315 ); if( entity == "Tfr" ) return QChar( 0x1D517 ); if( entity == "tfr" ) return QChar( 0x1D531 ); if( entity == "there4" ) return QChar( 0x02234 ); if( entity == "Therefore" ) return QChar( 0x02234 ); if( entity == "therefore" ) return QChar( 0x02234 ); if( entity == "Theta" ) return QChar( 0x00398 ); if( entity == "theta" ) return QChar( 0x003B8 ); if( entity == "thetav" ) return QChar( 0x003D1 ); if( entity == "thickapprox" ) return QChar( 0x02248 ); if( entity == "thicksim" ) return QChar( 0x0223C ); if( entity == "thinsp" ) return QChar( 0x02009 ); if( entity == "ThinSpace" ) return QChar( 0x02009 ); if( entity == "thkap" ) return QChar( 0x02248 ); if( entity == "thksim" ) return QChar( 0x0223C ); if( entity == "THORN" ) return QChar( 0x000DE ); if( entity == "thorn" ) return QChar( 0x000FE ); if( entity == "Tilde" ) return QChar( 0x0223C ); if( entity == "tilde" ) return QChar( 0x002DC ); if( entity == "TildeEqual" ) return QChar( 0x02243 ); if( entity == "TildeFullEqual" ) return QChar( 0x02245 ); if( entity == "TildeTilde" ) return QChar( 0x02248 ); if( entity == "times" ) return QChar( 0x000D7 ); if( entity == "timesb" ) return QChar( 0x022A0 ); if( entity == "timesbar" ) return QChar( 0x02A31 ); if( entity == "timesd" ) return QChar( 0x02A30 ); if( entity == "tint" ) return QChar( 0x0222D ); if( entity == "toea" ) return QChar( 0x02928 ); if( entity == "top" ) return QChar( 0x022A4 ); if( entity == "topbot" ) return QChar( 0x02336 ); if( entity == "topcir" ) return QChar( 0x02AF1 ); if( entity == "Topf" ) return QChar( 0x1D54B ); if( entity == "topf" ) return QChar( 0x1D565 ); if( entity == "topfork" ) return QChar( 0x02ADA ); if( entity == "tosa" ) return QChar( 0x02929 ); if( entity == "tprime" ) return QChar( 0x02034 ); if( entity == "trade" ) return QChar( 0x02122 ); if( entity == "triangle" ) return QChar( 0x025B5 ); if( entity == "triangledown" ) return QChar( 0x025BF ); if( entity == "triangleleft" ) return QChar( 0x025C3 ); if( entity == "trianglelefteq" ) return QChar( 0x022B4 ); if( entity == "triangleq" ) return QChar( 0x0225C ); if( entity == "triangleright" ) return QChar( 0x025B9 ); if( entity == "trianglerighteq" ) return QChar( 0x022B5 ); if( entity == "tridot" ) return QChar( 0x025EC ); if( entity == "trie" ) return QChar( 0x0225C ); if( entity == "triminus" ) return QChar( 0x02A3A ); if( entity == "TripleDot" ) return QChar( 0x020DB ); if( entity == "triplus" ) return QChar( 0x02A39 ); if( entity == "trisb" ) return QChar( 0x029CD ); if( entity == "tritime" ) return QChar( 0x02A3B ); if( entity == "trpezium" ) return QChar( 0x0FFFD ); if( entity == "Tscr" ) return QChar( 0x1D4AF ); if( entity == "tscr" ) return QChar( 0x1D4C9 ); if( entity == "TScy" ) return QChar( 0x00426 ); if( entity == "tscy" ) return QChar( 0x00446 ); if( entity == "TSHcy" ) return QChar( 0x0040B ); if( entity == "tshcy" ) return QChar( 0x0045B ); if( entity == "Tstrok" ) return QChar( 0x00166 ); if( entity == "tstrok" ) return QChar( 0x00167 ); if( entity == "twixt" ) return QChar( 0x0226C ); if( entity == "twoheadleftarrow" ) return QChar( 0x0219E ); if( entity == "twoheadrightarrow" ) return QChar( 0x021A0 ); break; case 'u': if( entity == "Uacute" ) return QChar( 0x000DA ); if( entity == "uacute" ) return QChar( 0x000FA ); if( entity == "Uarr" ) return QChar( 0x0219F ); if( entity == "uArr" ) return QChar( 0x021D1 ); if( entity == "uarr" ) return QChar( 0x02191 ); if( entity == "Uarrocir" ) return QChar( 0x02949 ); if( entity == "Ubrcy" ) return QChar( 0x0040E ); if( entity == "ubrcy" ) return QChar( 0x0045E ); if( entity == "Ubreve" ) return QChar( 0x0016C ); if( entity == "ubreve" ) return QChar( 0x0016D ); if( entity == "Ucirc" ) return QChar( 0x000DB ); if( entity == "ucirc" ) return QChar( 0x000FB ); if( entity == "Ucy" ) return QChar( 0x00423 ); if( entity == "ucy" ) return QChar( 0x00443 ); if( entity == "udarr" ) return QChar( 0x021C5 ); if( entity == "Udblac" ) return QChar( 0x00170 ); if( entity == "udblac" ) return QChar( 0x00171 ); if( entity == "udhar" ) return QChar( 0x0296E ); if( entity == "ufisht" ) return QChar( 0x0297E ); if( entity == "Ufr" ) return QChar( 0x1D518 ); if( entity == "ufr" ) return QChar( 0x1D532 ); if( entity == "Ugrave" ) return QChar( 0x000D9 ); if( entity == "ugrave" ) return QChar( 0x000F9 ); if( entity == "uHar" ) return QChar( 0x02963 ); if( entity == "uharl" ) return QChar( 0x021BF ); if( entity == "uharr" ) return QChar( 0x021BE ); if( entity == "uhblk" ) return QChar( 0x02580 ); if( entity == "ulcorn" ) return QChar( 0x0231C ); if( entity == "ulcorner" ) return QChar( 0x0231C ); if( entity == "ulcrop" ) return QChar( 0x0230F ); if( entity == "ultri" ) return QChar( 0x025F8 ); if( entity == "Umacr" ) return QChar( 0x0016A ); if( entity == "umacr" ) return QChar( 0x0016B ); if( entity == "uml" ) return QChar( 0x000A8 ); if( entity == "UnderBar" ) return QChar( 0x00332 ); if( entity == "UnderBrace" ) return QChar( 0x0FE38 ); if( entity == "UnderBracket" ) return QChar( 0x023B5 ); if( entity == "UnderParenthesis" ) return QChar( 0x0FE36 ); if( entity == "Union" ) return QChar( 0x022C3 ); if( entity == "UnionPlus" ) return QChar( 0x0228E ); if( entity == "Uogon" ) return QChar( 0x00172 ); if( entity == "uogon" ) return QChar( 0x00173 ); if( entity == "Uopf" ) return QChar( 0x1D54C ); if( entity == "uopf" ) return QChar( 0x1D566 ); if( entity == "UpArrow" ) return QChar( 0x02191 ); if( entity == "Uparrow" ) return QChar( 0x021D1 ); if( entity == "uparrow" ) return QChar( 0x02191 ); if( entity == "UpArrowBar" ) return QChar( 0x02912 ); if( entity == "UpArrowDownArrow" ) return QChar( 0x021C5 ); if( entity == "UpDownArrow" ) return QChar( 0x02195 ); if( entity == "Updownarrow" ) return QChar( 0x021D5 ); if( entity == "updownarrow" ) return QChar( 0x02195 ); if( entity == "UpEquilibrium" ) return QChar( 0x0296E ); if( entity == "upharpoonleft" ) return QChar( 0x021BF ); if( entity == "upharpoonright" ) return QChar( 0x021BE ); if( entity == "uplus" ) return QChar( 0x0228E ); if( entity == "UpperLeftArrow" ) return QChar( 0x02196 ); if( entity == "UpperRightArrow" ) return QChar( 0x02197 ); if( entity == "Upsi" ) return QChar( 0x003D2 ); if( entity == "upsi" ) return QChar( 0x003C5 ); if( entity == "Upsilon" ) return QChar( 0x003A5 ); if( entity == "upsilon" ) return QChar( 0x003C5 ); if( entity == "UpTee" ) return QChar( 0x022A5 ); if( entity == "UpTeeArrow" ) return QChar( 0x021A5 ); if( entity == "upuparrows" ) return QChar( 0x021C8 ); if( entity == "urcorn" ) return QChar( 0x0231D ); if( entity == "urcorner" ) return QChar( 0x0231D ); if( entity == "urcrop" ) return QChar( 0x0230E ); if( entity == "Uring" ) return QChar( 0x0016E ); if( entity == "uring" ) return QChar( 0x0016F ); if( entity == "urtri" ) return QChar( 0x025F9 ); if( entity == "Uscr" ) return QChar( 0x1D4B0 ); if( entity == "uscr" ) return QChar( 0x1D4CA ); if( entity == "utdot" ) return QChar( 0x022F0 ); if( entity == "Utilde" ) return QChar( 0x00168 ); if( entity == "utilde" ) return QChar( 0x00169 ); if( entity == "utri" ) return QChar( 0x025B5 ); if( entity == "utrif" ) return QChar( 0x025B4 ); if( entity == "uuarr" ) return QChar( 0x021C8 ); if( entity == "Uuml" ) return QChar( 0x000DC ); if( entity == "uuml" ) return QChar( 0x000FC ); if( entity == "uwangle" ) return QChar( 0x029A7 ); break; case 'v': if( entity == "vangrt" ) return QChar( 0x0299C ); if( entity == "varepsilon" ) return QChar( 0x003B5 ); if( entity == "varkappa" ) return QChar( 0x003F0 ); if( entity == "varnothing" ) return QChar( 0x02205 ); if( entity == "varphi" ) return QChar( 0x003C6 ); if( entity == "varpi" ) return QChar( 0x003D6 ); if( entity == "varpropto" ) return QChar( 0x0221D ); if( entity == "vArr" ) return QChar( 0x021D5 ); if( entity == "varr" ) return QChar( 0x02195 ); if( entity == "varrho" ) return QChar( 0x003F1 ); if( entity == "varsigma" ) return QChar( 0x003C2 ); if( entity == "vartheta" ) return QChar( 0x003D1 ); if( entity == "vartriangleleft" ) return QChar( 0x022B2 ); if( entity == "vartriangleright" ) return QChar( 0x022B3 ); if( entity == "Vbar" ) return QChar( 0x02AEB ); if( entity == "vBar" ) return QChar( 0x02AE8 ); if( entity == "vBarv" ) return QChar( 0x02AE9 ); if( entity == "Vcy" ) return QChar( 0x00412 ); if( entity == "vcy" ) return QChar( 0x00432 ); if( entity == "VDash" ) return QChar( 0x022AB ); if( entity == "Vdash" ) return QChar( 0x022A9 ); if( entity == "vDash" ) return QChar( 0x022A8 ); if( entity == "vdash" ) return QChar( 0x022A2 ); if( entity == "Vdashl" ) return QChar( 0x02AE6 ); if( entity == "Vee" ) return QChar( 0x022C1 ); if( entity == "vee" ) return QChar( 0x02228 ); if( entity == "veebar" ) return QChar( 0x022BB ); if( entity == "veeeq" ) return QChar( 0x0225A ); if( entity == "vellip" ) return QChar( 0x022EE ); if( entity == "Verbar" ) return QChar( 0x02016 ); if( entity == "verbar" ) return QChar( 0x0007C ); if( entity == "Vert" ) return QChar( 0x02016 ); if( entity == "vert" ) return QChar( 0x0007C ); if( entity == "VerticalBar" ) return QChar( 0x02223 ); if( entity == "VerticalLine" ) return QChar( 0x0007C ); if( entity == "VerticalSeparator" ) return QChar( 0x02758 ); if( entity == "VerticalTilde" ) return QChar( 0x02240 ); if( entity == "VeryThinSpace" ) return QChar( 0x0200A ); if( entity == "Vfr" ) return QChar( 0x1D519 ); if( entity == "vfr" ) return QChar( 0x1D533 ); if( entity == "vltri" ) return QChar( 0x022B2 ); if( entity == "Vopf" ) return QChar( 0x1D54D ); if( entity == "vopf" ) return QChar( 0x1D567 ); if( entity == "vprop" ) return QChar( 0x0221D ); if( entity == "vrtri" ) return QChar( 0x022B3 ); if( entity == "Vscr" ) return QChar( 0x1D4B1 ); if( entity == "vscr" ) return QChar( 0x1D4CB ); if( entity == "Vvdash" ) return QChar( 0x022AA ); if( entity == "vzigzag" ) return QChar( 0x0299A ); break; case 'w': if( entity == "Wcirc" ) return QChar( 0x00174 ); if( entity == "wcirc" ) return QChar( 0x00175 ); if( entity == "wedbar" ) return QChar( 0x02A5F ); if( entity == "Wedge" ) return QChar( 0x022C0 ); if( entity == "wedge" ) return QChar( 0x02227 ); if( entity == "wedgeq" ) return QChar( 0x02259 ); if( entity == "weierp" ) return QChar( 0x02118 ); if( entity == "Wfr" ) return QChar( 0x1D51A ); if( entity == "wfr" ) return QChar( 0x1D534 ); if( entity == "Wopf" ) return QChar( 0x1D54E ); if( entity == "wopf" ) return QChar( 0x1D568 ); if( entity == "wp" ) return QChar( 0x02118 ); if( entity == "wr" ) return QChar( 0x02240 ); if( entity == "wreath" ) return QChar( 0x02240 ); if( entity == "Wscr" ) return QChar( 0x1D4B2 ); if( entity == "wscr" ) return QChar( 0x1D4CC ); break; case 'x': if( entity == "xcap" ) return QChar( 0x022C2 ); if( entity == "xcirc" ) return QChar( 0x025EF ); if( entity == "xcup" ) return QChar( 0x022C3 ); if( entity == "xdtri" ) return QChar( 0x025BD ); if( entity == "Xfr" ) return QChar( 0x1D51B ); if( entity == "xfr" ) return QChar( 0x1D535 ); if( entity == "xhArr" ) return QChar( 0x027FA ); if( entity == "xharr" ) return QChar( 0x027F7 ); if( entity == "Xi" ) return QChar( 0x0039E ); if( entity == "xi" ) return QChar( 0x003BE ); if( entity == "xlArr" ) return QChar( 0x027F8 ); if( entity == "xlarr" ) return QChar( 0x027F5 ); if( entity == "xmap" ) return QChar( 0x027FC ); if( entity == "xnis" ) return QChar( 0x022FB ); if( entity == "xodot" ) return QChar( 0x02A00 ); if( entity == "Xopf" ) return QChar( 0x1D54F ); if( entity == "xopf" ) return QChar( 0x1D569 ); if( entity == "xoplus" ) return QChar( 0x02A01 ); if( entity == "xotime" ) return QChar( 0x02A02 ); if( entity == "xrArr" ) return QChar( 0x027F9 ); if( entity == "xrarr" ) return QChar( 0x027F6 ); if( entity == "Xscr" ) return QChar( 0x1D4B3 ); if( entity == "xscr" ) return QChar( 0x1D4CD ); if( entity == "xsqcup" ) return QChar( 0x02A06 ); if( entity == "xuplus" ) return QChar( 0x02A04 ); if( entity == "xutri" ) return QChar( 0x025B3 ); if( entity == "xvee" ) return QChar( 0x022C1 ); if( entity == "xwedge" ) return QChar( 0x022C0 ); break; case 'y': if( entity == "Yacute" ) return QChar( 0x000DD ); if( entity == "yacute" ) return QChar( 0x000FD ); if( entity == "YAcy" ) return QChar( 0x0042F ); if( entity == "yacy" ) return QChar( 0x0044F ); if( entity == "Ycirc" ) return QChar( 0x00176 ); if( entity == "ycirc" ) return QChar( 0x00177 ); if( entity == "Ycy" ) return QChar( 0x0042B ); if( entity == "ycy" ) return QChar( 0x0044B ); if( entity == "yen" ) return QChar( 0x000A5 ); if( entity == "Yfr" ) return QChar( 0x1D51C ); if( entity == "yfr" ) return QChar( 0x1D536 ); if( entity == "YIcy" ) return QChar( 0x00407 ); if( entity == "yicy" ) return QChar( 0x00457 ); if( entity == "Yopf" ) return QChar( 0x1D550 ); if( entity == "yopf" ) return QChar( 0x1D56A ); if( entity == "Yscr" ) return QChar( 0x1D4B4 ); if( entity == "yscr" ) return QChar( 0x1D4CE ); if( entity == "YUcy" ) return QChar( 0x0042E ); if( entity == "yucy" ) return QChar( 0x0044E ); if( entity == "Yuml" ) return QChar( 0x00178 ); if( entity == "yuml" ) return QChar( 0x000FF ); break; case 'z': if( entity == "Zacute" ) return QChar( 0x00179 ); if( entity == "zacute" ) return QChar( 0x0017A ); if( entity == "Zcaron" ) return QChar( 0x0017D ); if( entity == "zcaron" ) return QChar( 0x0017E ); if( entity == "Zcy" ) return QChar( 0x00417 ); if( entity == "zcy" ) return QChar( 0x00437 ); if( entity == "Zdot" ) return QChar( 0x0017B ); if( entity == "zdot" ) return QChar( 0x0017C ); if( entity == "zeetrf" ) return QChar( 0x02128 ); if( entity == "ZeroWidthSpace" ) return QChar( 0x0200B ); if( entity == "zeta" ) return QChar( 0x003B6 ); if( entity == "Zfr" ) return QChar( 0x02128 ); if( entity == "zfr" ) return QChar( 0x1D537 ); if( entity == "ZHcy" ) return QChar( 0x00416 ); if( entity == "zhcy" ) return QChar( 0x00436 ); if( entity == "zigrarr" ) return QChar( 0x021DD ); if( entity == "Zopf" ) return QChar( 0x02124 ); if( entity == "zopf" ) return QChar( 0x1D56B ); if( entity == "Zscr" ) return QChar( 0x1D4B5 ); if( entity == "zscr" ) return QChar( 0x1D4CF ); break; default: break; } return QChar(); } bool Dictionary::queryOperator( const QString& queriedOperator, Form form ) { if( queriedOperator.isEmpty() || queriedOperator.isNull() ) return false; if( queriedOperator == "(" && form == Prefix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == ")" && form == Postfix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "[" && form == Prefix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "]" && form == Postfix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "{" && form == Prefix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "}" && form == Postfix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "”" && form == Postfix ) { m_fence = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "’" && form == Postfix ) { m_fence = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⟨" && form == Prefix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⌈" && form == Prefix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⟦" && form == Prefix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⌊" && form == Prefix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "“" && form == Prefix ) { m_fence = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "‘" && form == Prefix ) { m_fence = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⟩" && form == Postfix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⌉" && form == Postfix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⟧" && form == Postfix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⌋" && form == Postfix ) { m_fence = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⁣" && form == Infix ) { m_separator = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "," && form == Infix ) { m_separator = true; m_lspace = "0em"; m_rspace = "verythickmathspace"; return true; } if( queriedOperator == "─" && form == Infix ) { m_stretchy = true; m_minsize = "0"; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "|" && form == Infix ) { m_stretchy = true; m_minsize = "0"; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == ";" && form == Infix ) { m_separator = true; m_lspace = "0em"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == ";" && form == Postfix ) { m_separator = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == ":=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≔" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∵" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∴" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "❘" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "//" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∷" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "&" && form == Prefix ) { m_lspace = "0em"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "&" && form == Postfix ) { m_lspace = "thickmathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "*=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "-=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "+=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "/=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "->" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == ":" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == ".." && form == Postfix ) { m_lspace = "mediummathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "..." && form == Postfix ) { m_lspace = "mediummathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "∋" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⫤" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊨" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊤" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊣" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊢" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇒" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥰" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "|" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "||" && form == Infix ) { m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "⩔" && form == Infix ) { m_stretchy = true; m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "&&" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⩓" && form == Infix ) { m_stretchy = true; m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "&" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "!" && form == Prefix ) { m_lspace = "0em"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⫬" && form == Prefix ) { m_lspace = "0em"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∃" && form == Prefix ) { m_lspace = "0em"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∀" && form == Prefix ) { m_lspace = "0em"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∄" && form == Prefix ) { m_lspace = "0em"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∈" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∉" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∌" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊏̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋢" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊐̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋣" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊂⃒" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊈" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊃⃒" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊉" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∋" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊏" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊑" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊐" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊒" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋐" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊆" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊃" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊇" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇐" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇔" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇒" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥐" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥞" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↽" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥖" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥟" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇁" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥗" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "←" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇤" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇆" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↔" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥎" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↤" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥚" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↼" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥒" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↙" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↘" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "→" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇥" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇄" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↦" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥛" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇀" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⥓" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "←" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "→" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↖" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "↗" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "<" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == ">" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "!=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "==" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "<=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == ">=" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≡" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≍" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≐" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∥" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⩵" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≂" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇌" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≥" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋛" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≧" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪢" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≷" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⩾" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≳" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≎" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≏" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊲" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⧏" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊴" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≤" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋚" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≦" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≶" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪡" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⩽" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≲" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≫" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≪" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≢" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≭" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∦" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≠" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≂̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≯" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≱" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≧̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≫̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≹" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⩾̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≵" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≎̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≏̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋪" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⧏̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋬" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≮" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≰" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≪̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⩽̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≴" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪢̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪡̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊀" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪯̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋠" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋫" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⧐̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋭" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊁" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪰̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⋡" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≿̸" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≁" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≄" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≇" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≉" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∤" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≺" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪯" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≼" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≾" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∷" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∝" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⇋" && form == Infix ) { m_stretchy = true; m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊳" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⧐" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊵" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≻" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⪰" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≽" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≿" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∼" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≃" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≅" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "≈" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊥" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "∣" && form == Infix ) { m_lspace = "thickmathspace"; m_rspace = "thickmathspace"; return true; } if( queriedOperator == "⊔" && form == Infix ) { m_stretchy = true; m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "⋃" && form == Infix ) { m_stretchy = true; m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "⊎" && form == Infix ) { m_stretchy = true; m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "-" && form == Infix ) { m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "+" && form == Infix ) { m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "⋂" && form == Infix ) { m_stretchy = true; m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "∓" && form == Infix ) { m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "±" && form == Infix ) { m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "⊓" && form == Infix ) { m_stretchy = true; m_lspace = "mediummathspace"; m_rspace = "mediummathspace"; return true; } if( queriedOperator == "⋁" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊖" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊕" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "∑" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋃" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊎" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "lim" && form == Prefix ) { m_movablelimits = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "max" && form == Prefix ) { m_movablelimits = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "min" && form == Prefix ) { m_movablelimits = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊖" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊕" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "∲" && form == Prefix ) { m_largeop = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "∮" && form == Prefix ) { m_largeop = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "∳" && form == Prefix ) { m_largeop = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "∯" && form == Prefix ) { m_largeop = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "∫" && form == Prefix ) { m_largeop = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⋓" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋒" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "≀" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋀" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊗" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "∐" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "∏" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋂" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "∐" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋆" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊙" && form == Prefix ) { m_largeop = true; m_movablelimits = true; m_lspace = "0em"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "*" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⁢" && form == Infix ) { m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "·" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⊗" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋁" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋀" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "⋄" && form == Infix ) { m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "∖" && form == Infix ) { m_stretchy = true; m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "/" && form == Infix ) { m_stretchy = true; m_lspace = "thinmathspace"; m_rspace = "thinmathspace"; return true; } if( queriedOperator == "-" && form == Prefix ) { m_lspace = "0em"; m_rspace = "veryverythinmathspace"; return true; } if( queriedOperator == "+" && form == Prefix ) { m_lspace = "0em"; m_rspace = "veryverythinmathspace"; return true; } if( queriedOperator == "∓" && form == Prefix ) { m_lspace = "0em"; m_rspace = "veryverythinmathspace"; return true; } if( queriedOperator == "±" && form == Prefix ) { m_lspace = "0em"; m_rspace = "veryverythinmathspace"; return true; } if( queriedOperator == "." && form == Infix ) { m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⨯" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "**" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⊙" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "∘" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "□" && form == Prefix ) { m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "∇" && form == Prefix ) { m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "∂" && form == Prefix ) { m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "ⅅ" && form == Prefix ) { m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "ⅆ" && form == Prefix ) { m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "√" && form == Prefix ) { m_stretchy = true; m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⇓" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⟸" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⟺" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⟹" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⇑" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⇕" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↓" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⤓" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⇵" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↧" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥡" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⇃" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥙" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥑" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥠" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↿" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥘" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⟵" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⟷" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⟶" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥯" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥝" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⇂" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥕" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥏" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥜" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↾" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥔" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↓" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↑" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↑" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⤒" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⇅" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↕" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⥮" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "↥" && form == Infix ) { m_stretchy = true; m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "^" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "<>" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "'" && form == Postfix ) { m_lspace = "verythinmathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "!" && form == Postfix ) { m_lspace = "verythinmathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "!!" && form == Postfix ) { m_lspace = "verythinmathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "~" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "@" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "--" && form == Postfix ) { m_lspace = "verythinmathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "--" && form == Prefix ) { m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "++" && form == Postfix ) { m_lspace = "verythinmathspace"; m_rspace = "0em"; return true; } if( queriedOperator == "++" && form == Prefix ) { m_lspace = "0em"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "⁡" && form == Infix ) { m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "?" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "_" && form == Infix ) { m_lspace = "verythinmathspace"; m_rspace = "verythinmathspace"; return true; } if( queriedOperator == "˘" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "¸" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "`" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "˙" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "˝" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "←" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "↔" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⥎" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "↼" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "´" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "→" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⇀" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "˜" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "¨" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "̑" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "ˇ" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "^" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "‾" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⏞" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⎴" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⏜" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⃛" && form == Postfix ) { m_accent = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "_" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⏟" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⎵" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } if( queriedOperator == "⏝" && form == Postfix ) { m_accent = true; m_stretchy = true; m_lspace = "0em"; m_rspace = "0em"; return true; } return false; } diff --git a/plugins/musicshape/core/MusicXmlReader.cpp b/plugins/musicshape/core/MusicXmlReader.cpp index e9edede7d71..24db016391f 100644 --- a/plugins/musicshape/core/MusicXmlReader.cpp +++ b/plugins/musicshape/core/MusicXmlReader.cpp @@ -1,337 +1,337 @@ /* This file is part of the KDE project * Copyright (C) 2007 Marijn Kruisselbrink * * 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 "MusicXmlReader.h" #include "Sheet.h" #include "Part.h" #include "Chord.h" #include "Voice.h" #include "VoiceBar.h" #include "KeySignature.h" #include "Bar.h" #include "Clef.h" #include "TimeSignature.h" #include "Note.h" #include #include #include #include namespace MusicCore { MusicXmlReader::MusicXmlReader(const char* musicNamespace) : m_namespace(musicNamespace) { } static Duration parseDuration(const QString& type, int length, int div) { if (type == "128th") return HundredTwentyEighthNote; else if (type == "64th") return SixtyFourthNote; else if (type == "32nd") return ThirtySecondNote; else if (type == "16th") return SixteenthNote; else if (type == "eighth") return EighthNote; else if (type == "quarter") return QuarterNote; else if (type == "half") return HalfNote; else if (type == "whole") return WholeNote; else if (type == "breve") return BreveNote; // else try to parse it from length qreal fact = 26880.0 / div; int ticks = (int) round(length * fact); // TODO: take number of dots into account if (ticks <= Note128Length) return HundredTwentyEighthNote; else if (ticks <= Note64Length) return SixtyFourthNote; else if (ticks <= Note32Length) return ThirtySecondNote; else if (ticks <= Note16Length) return SixteenthNote; else if (ticks <= Note8Length) return EighthNote; else if (ticks <= QuarterLength) return QuarterNote; else if (ticks <= HalfLength) return HalfNote; else if (ticks <= WholeLength) return WholeNote; else return BreveNote; } Sheet* MusicXmlReader::loadSheet(const KoXmlElement& scoreElement) { Sheet* sheet = new Sheet(); QHash parts; KoXmlElement partList = namedItem(scoreElement, "part-list"); if (partList.isNull()) { //kDebug() << "no part list found"; return 0; } KoXmlElement elem; forEachElement(elem, partList) { if (checkNamespace(elem) && elem.localName() == "score-part") { QString id = elem.attribute("id"); QString name = getProperty(elem, "part-name"); QString abbr = getProperty(elem, "part-abbreviation"); Part* p = sheet->addPart(name); p->setShortName(abbr); // always add one voice and one staff p->addVoice(); p->addStaff(); parts[id] = p; } } forEachElement(elem, scoreElement) { if (checkNamespace(elem) && elem.localName() == "part") { QString id = elem.attribute("id"); loadPart(elem, parts[id]); } } return sheet; } QString MusicXmlReader::getProperty(const KoXmlElement& elem, const char *propName) { KoXmlElement propElem = namedItem(elem, propName); return propElem.text(); } KoXmlElement MusicXmlReader::namedItem(const KoXmlNode& node, const char* localName) { if (m_namespace) { return KoXml::namedItemNS(node, m_namespace, localName); } else { return node.namedItem(localName).toElement(); } } bool MusicXmlReader::checkNamespace(const KoXmlNode& node) { return !m_namespace || node.namespaceURI() == m_namespace; } Clef* MusicXmlReader::loadClef(const KoXmlElement& element, Staff* staff) { QString shapeStr = getProperty(element, "sign"); Clef::ClefShape shape = Clef::GClef; int line = 2; if (shapeStr == "G") { line = 2; shape = Clef::GClef; } else if (shapeStr == "F") { line = 4; shape = Clef::FClef; } else if (shapeStr == "C") { line = 3; shape = Clef::CClef; } QString lineStr = getProperty(element, "line"); if (!lineStr.isNull()) line = lineStr.toInt(); int octave = 0; QString octaveStr = getProperty(element, "clef-octave-change"); if (!octaveStr.isNull()) octave = octaveStr.toInt(); return new Clef(staff, 0, shape, line, octave); } TimeSignature* MusicXmlReader::loadTimeSignature(const KoXmlElement& element, Staff* staff) { int beats = getProperty(element, "beats").toInt(); int beat = getProperty(element, "beat-type").toInt(); return new TimeSignature(staff, 0, beats, beat); } void MusicXmlReader::loadPart(const KoXmlElement& partElement, Part* part) { Sheet* sheet = part->sheet(); KoXmlElement barElem; int curBar = 0; int curDivisions = 26880; Chord* lastNote = NULL; forEachElement(barElem, partElement) { if (!checkNamespace(barElem) || barElem.localName() != "measure") continue; Bar* bar = NULL; if (curBar >= sheet->barCount()) { bar = sheet->addBar(); } else { bar = sheet->bar(curBar); } QList > beams; for (int i = 0; i < 6; i++) beams.append(QList()); KoXmlElement e; forEachElement(e, barElem) { if (!checkNamespace(e)) continue; if (e.localName() == "attributes") { KoXmlElement attr; QString staffCountStr = getProperty(e, "staves"); if (!staffCountStr.isNull()) { int staffCount = staffCountStr.toInt(); while (staffCount > part->staffCount()) { part->addStaff(); } } forEachElement(attr, e) { if (!checkNamespace(attr)) continue; if (attr.localName() == "divisions") { curDivisions = attr.text().toInt(); } else if (attr.localName() == "key") { QString number = attr.attribute("number"); int firstStaffId = 0; int lastStaffId = part->staffCount()-1; if (!number.isNull()) firstStaffId = lastStaffId = number.toInt() - 1; for (int staffId = firstStaffId; staffId <= lastStaffId; staffId++) { KeySignature* ks = new KeySignature(part->staff(staffId), 0, getProperty(attr, "fifths").toInt()); bar->addStaffElement(ks); } } else if (attr.localName() == "clef") { QString number = attr.attribute("number"); int staffId = 0; if (!number.isNull()) staffId = number.toInt() - 1; Clef* clef = loadClef(attr, part->staff(staffId)); bar->addStaffElement(clef); } else if (attr.localName() == "time") { QString number = attr.attribute("number"); int firstStaffId = 0; int lastStaffId = part->staffCount()-1; if (!number.isNull()) firstStaffId = lastStaffId = number.toInt() - 1; for (int staffId = firstStaffId; staffId <= lastStaffId; staffId++) { TimeSignature* ts = loadTimeSignature(attr, part->staff(staffId)); bar->addStaffElement(ts); } } } } else if (e.localName() == "note") { QString staffStr = getProperty(e, "staff"); int staffId = 0; if (!staffStr.isNull()) staffId = staffStr.toInt() - 1; if (namedItem(e, "chord").isNull()) { // no chord element, so this is the start of a new chord int length = getProperty(e, "duration").toInt(); QString type = getProperty(e, "type"); Duration duration = parseDuration(type, length, curDivisions); QString voiceStr = getProperty(e, "voice"); int voiceId = 0; if (!voiceStr.isNull()) voiceId = voiceStr.toInt() - 1; while (voiceId >= part->voiceCount()) { part->addVoice(); } int nDots = 0; KoXmlElement dot; forEachElement(dot, e) { if (checkNamespace(dot) && dot.localName() == "dot") nDots++; } Staff* staff = part->staff(staffId); lastNote = new Chord(staff, duration, nDots); Voice* voice = part->voice(voiceId); voice->bar(bar)->addElement(lastNote); KoXmlElement beam; forEachElement(beam, e) if (checkNamespace(beam) && beam.localName() == "beam") { int number = beam.attribute("number").toInt() - 1; if (number < 0 || number > 5) continue; QString type = beam.text(); if (type == "begin") { beams[number].clear(); beams[number].append(lastNote); } else if (type == "continue") { beams[number].append(lastNote); } else if (type == "end") { beams[number].append(lastNote); Chord* startNote = beams[number][0]; foreach (Chord* c, beams[number]) { c->setBeam(number, startNote, lastNote); } beams[number].clear(); } else if (type == "forward hook") { lastNote->setBeam(number, lastNote, lastNote, BeamForwardHook); beams[number].clear(); } else if (type == "backward hook") { lastNote->setBeam(number, lastNote, lastNote, BeamBackwardHook); beams[number].clear(); } } } KoXmlElement pitch = namedItem(e, "pitch"); if (!pitch.isNull()) { QString step = getProperty(pitch, "step"); int octave = getProperty(pitch, "octave").toInt(); - int note = step[0].toAscii() - 'A'; + int note = step[0].toLatin1() - 'A'; note -= 2; if (note < 0) note += 7; note += (octave - 4) * 7; int alter = 0; QString alterStr = getProperty(pitch, "alter"); if (!alterStr.isNull()) alter = alterStr.toInt(); QString accidental = getProperty(e, "accidental"); if (accidental == "double-sharp" || accidental == "sharp-sharp") { alter = 2; } else if (accidental == "sharp" || accidental == "natural-sharp") { alter = 1; } else if (accidental == "natural") { alter = 0; } else if (accidental == "flat" || accidental == "natural-flat") { alter = -1; } else if (accidental == "double-flat" || accidental == "flat-flat") { alter = -2; } Note* theNote = lastNote->addNote(part->staff(staffId), note, alter); KoXmlElement tie = namedItem(e, "tie"); if (!tie.isNull()) { if (tie.attribute("type") == "start") { theNote->setStartTie(true); } } } } } curBar++; } } } // namespace MusicCore diff --git a/plugins/pathshapes/enhancedpath/EnhancedPathFormula.cpp b/plugins/pathshapes/enhancedpath/EnhancedPathFormula.cpp index 48e977ae06d..c1383e32272 100644 --- a/plugins/pathshapes/enhancedpath/EnhancedPathFormula.cpp +++ b/plugins/pathshapes/enhancedpath/EnhancedPathFormula.cpp @@ -1,821 +1,821 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * 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 "EnhancedPathFormula.h" #include "EnhancedPathShape.h" #include #include #include /* The formula parsing, compiling and evaluating is based on kspreads formula engine written by Ariya Hidayat. There is a DESIGN.html file in the kspreads directory which explains how the engine is working. The engine was stripped down a little to only support the operations needed for the odf enhanced path formula spec. */ // helper function: return operator of given token text FormulaToken::Operator matchOperator(const QString &text); // helper function: return true for valid identifier character bool isIdentifier(QChar ch); // helper function: give operator precedence // e.g. '+' is 1 while '*' is 3 int opPrecedence(FormulaToken::Operator op); // helper function: return function of given token text EnhancedPathFormula::Function matchFunction(const QString & text); // helper function: return function name from function identifier QString matchFunction(EnhancedPathFormula::Function function); class FormulaToken; class FormulaTokenStack : public QVector { public: FormulaTokenStack() : QVector(), topIndex(0) { ensureSpace(); } bool isEmpty() const { return topIndex == 0; } unsigned itemCount() const { return topIndex; } void push(const FormulaToken& token) { ensureSpace(); insert(topIndex++, token); } FormulaToken pop() { return (topIndex > 0) ? FormulaToken(at(--topIndex)) : FormulaToken(); } const FormulaToken& top() { return top(0); } const FormulaToken& top(unsigned index) { static FormulaToken null; if (topIndex > index) return at(topIndex-index-1); return null; } private: void ensureSpace() { while((int) topIndex >= size()) resize(size() + 10); } unsigned topIndex; }; class Opcode { public: enum { Nop = 0, Load, Ref, Function, Add, Sub, Neg, Mul, Div }; unsigned type; unsigned index; Opcode(): type(Nop), index(0) {} Opcode(unsigned t): type(t), index(0) {} Opcode(unsigned t, unsigned i): type(t), index(i) {} }; EnhancedPathFormula::EnhancedPathFormula(const QString &text, EnhancedPathShape *parent) : m_valid(false), m_compiled(false), m_error(ErrorNone), m_text(text), m_parent(parent) { Q_ASSERT(m_parent); } EnhancedPathFormula::~EnhancedPathFormula() { } qreal EnhancedPathFormula::evaluate() { // shortcut if (m_error != ErrorNone) return 0.0; // lazy evaluation if (!m_compiled) { TokenList tokens = scan(m_text); if (m_error != ErrorNone) debugTokens(tokens); if (!compile(tokens)) { debugOpcodes(); m_error = ErrorCompile; return false; } m_compiled = true; } QStack stack; // stack.reserve(3) here so that the stack is not resized all the time // this reduces the number of a/de/re-llocations for documents with // a lot of enhanced path shapes quite a lot. stack.reserve(3); int index = 0; if (!m_valid) { m_error = ErrorParse; return 0.0; } for (int pc = 0; pc < m_codes.count(); pc++) { QVariant ret; // for the function caller Opcode& opcode = m_codes[pc]; index = opcode.index; switch(opcode.type) { // no operation case Opcode::Nop: break; // load a constant, push to stack case Opcode::Load: stack.push(m_constants[index]); break; // unary operation case Opcode::Neg: { bool success = false; qreal value = stack.pop().toDouble(&success); if (success) // do nothing if we got an error value *= -1.0; stack.push(QVariant(value)); break; } // binary operation: take two values from stack, do the operation, // push the result to stack case Opcode::Add: { qreal val2 = stack.pop().toDouble(); qreal val1 = stack.pop().toDouble(); stack.push(QVariant(val1 + val2)); break; } case Opcode::Sub: { qreal val2 = stack.pop().toDouble(); qreal val1 = stack.pop().toDouble(); stack.push(QVariant(val1 - val2)); break; } case Opcode::Mul: { qreal val2 = stack.pop().toDouble(); qreal val1 = stack.pop().toDouble(); stack.push(QVariant(val1 * val2)); break; } case Opcode::Div: { qreal val2 = stack.pop().toDouble(); qreal val1 = stack.pop().toDouble(); stack.push(QVariant(val1 / val2)); break; } case Opcode::Ref: { QString reference = m_constants[index].toString(); // push function name if it is a function, else push evaluated reference Function function = matchFunction(reference); if (FunctionUnknown == function) stack.push(QVariant(m_parent->evaluateReference(reference))); else stack.push(function); break; } // calling function case Opcode::Function: { // sanity check, this should not happen unless opcode is wrong // (i.e. there's a bug in the compile() function) if (stack.count() < index) { kWarning() << "not enough arguments for function " << m_text; m_error = ErrorValue; // not enough arguments return 0.0; } /// prepare function arguments QList args; for (; index; index--) { qreal value = stack.pop().toDouble(); args.push_front(value); } // function identifier as int value int function = stack.pop().toInt(); stack.push(QVariant(evaluateFunction((Function)function, args))); break; } default: break; } } // more than one value in stack ? unsuccessful execution... if (stack.count() != 1) { m_error = ErrorValue; return 0.0; } return stack.pop().toDouble(); } qreal EnhancedPathFormula::evaluateFunction(Function function, const QList &arguments) const { switch(function) { case EnhancedPathFormula::FunctionAbs: return fabs(arguments[0]); break; case EnhancedPathFormula::FunctionSqrt: return sqrt(arguments[0]); break; case EnhancedPathFormula::FunctionSin: return sin(arguments[0]); break; case EnhancedPathFormula::FunctionCos: return cos(arguments[0]); break; case EnhancedPathFormula::FunctionTan: return tan(arguments[0]); break; case EnhancedPathFormula::FunctionAtan: return atan(arguments[0]); break; case EnhancedPathFormula::FunctionAtan2: // TODO atan2 with one argument as in odf spec ??? return atan2(arguments[0], arguments[1]); break; case EnhancedPathFormula::FunctionMin: return qMin(arguments[0], arguments[1]); break; case EnhancedPathFormula::FunctionMax: return qMax(arguments[0], arguments[1]); break; case EnhancedPathFormula::FunctionIf: if (arguments[0] > 0.0) return arguments[1]; else return arguments[2]; break; default: return 0.0; } return 0.0; } TokenList EnhancedPathFormula::scan(const QString &formula) const { // parsing state enum { Start, Finish, Bad, InNumber, InDecimal, InExpIndicator, InExponent, InString, InIdentifier } state; TokenList tokens; int i = 0; state = Start; int tokenStart = 0; QString tokenText; QString expr = formula + QChar(); // main loop while((state != Bad) && (state != Finish) && (i < expr.length())) { QChar ch = expr[i]; switch(state) { case Start: tokenStart = i; // skip any whitespaces if (ch.isSpace()) { i++; } else if (ch.isDigit()) { // check for number state = InNumber; } // beginning with alphanumeric ? // could be identifier, function, function reference, modifier reference else if (isIdentifier(ch)) { state = InIdentifier; } else if (ch == '.') { // decimal dot ? tokenText.append(expr[i++]); state = InDecimal; } else if (ch == QChar::Null) { // terminator character state = Finish; } else { // look for operator match QString opString(ch); int op = matchOperator(opString); // any matched operator ? if (op != FormulaToken::OperatorInvalid) { i++; tokens.append(FormulaToken(FormulaToken::TypeOperator, opString, tokenStart)); } else { state = Bad; } } break; case InIdentifier: // consume as long as alpha, dollar sign, question mark, or digit if (isIdentifier(ch) || ch.isDigit()) { tokenText.append(expr[i++]); } else if (ch == '(') { // a '(' ? then this must be a function identifier tokens.append(FormulaToken(FormulaToken::TypeFunction, tokenText, tokenStart)); tokenStart = i; tokenText = ""; state = Start; } else { // we're done with identifier tokens.append(FormulaToken(FormulaToken::TypeReference, tokenText, tokenStart)); tokenStart = i; tokenText = ""; state = Start; } break; case InNumber: // consume as long as it's digit if (ch.isDigit()) { tokenText.append(expr[i++]); } else if (ch == '.') { // decimal dot ? tokenText.append('.'); i++; state = InDecimal; } else if (ch.toUpper() == 'E') { // exponent ? tokenText.append('E'); i++; state = InExpIndicator; } else { // we're done with integer number tokens.append(FormulaToken(FormulaToken::TypeNumber, tokenText, tokenStart)); tokenText = ""; state = Start; }; break; case InDecimal: // consume as long as it's digit if (ch.isDigit()) { tokenText.append(expr[i++]); } else if (ch.toUpper() == 'E') { // exponent ? tokenText.append('E'); i++; state = InExpIndicator; } else { // we're done with floating-point number tokens.append(FormulaToken(FormulaToken::TypeNumber, tokenText, tokenStart)); tokenText = ""; state = Start; }; break; case InExpIndicator: // possible + or - right after E, e.g 1.23E+12 or 4.67E-8 if ((ch == '+') || (ch == '-')) { tokenText.append(expr[i++]); } else if (ch.isDigit()) { // consume as long as it's digit state = InExponent; } else { // invalid thing here state = Bad; } break; case InExponent: // consume as long as it's digit if (ch.isDigit()) { tokenText.append(expr[i++]); } else { // we're done with floating-point number tokens.append(FormulaToken(FormulaToken::TypeNumber, tokenText, tokenStart)); tokenText = ""; state = Start; } break; case Bad: default: break; } } return tokens; } bool EnhancedPathFormula::compile(const TokenList &tokens) { // sanity check if (tokens.count() == 0) return false; FormulaTokenStack syntaxStack; QStack argStack; unsigned argCount = 1; for (int i = 0; i <= tokens.count(); i++) { // helper token: InvalidOp is end-of-formula FormulaToken token = (i < tokens.count()) ? tokens[i] : FormulaToken(FormulaToken::TypeOperator); FormulaToken::Type tokenType = token.type(); // unknown token is invalid if (tokenType == FormulaToken::TypeUnknown) break; // for constants, push immediately to stack // generate code to load from a constant if (tokenType == FormulaToken::TypeNumber) { syntaxStack.push(token); m_constants.append(QVariant(token.asNumber())); m_codes.append(Opcode(Opcode::Load, m_constants.count()-1)); } // for identifier, push immediately to stack // generate code to load from reference if (tokenType == FormulaToken::TypeFunction || tokenType == FormulaToken::TypeReference) { syntaxStack.push(token); m_constants.append(QVariant(token.text())); m_codes.append(Opcode(Opcode::Ref, m_constants.count()-1)); } // are we entering a function ? // if token is operator, and stack already has: id (arg if (tokenType == FormulaToken::TypeOperator && syntaxStack.itemCount() >= 3) { FormulaToken arg = syntaxStack.top(); FormulaToken par = syntaxStack.top(1); FormulaToken id = syntaxStack.top(2); if (!arg.isOperator() && par.asOperator() == FormulaToken::OperatorLeftPar && id.isFunction()) { argStack.push(argCount); argCount = 1; } } // for any other operator, try to apply all parsing rules if (tokenType == FormulaToken::TypeOperator) { // repeat until no more rule applies for (; ;) { bool ruleFound = false; // rule for function arguments, if token is , or) // id (arg1 , arg2 -> id (arg if (!ruleFound) if (syntaxStack.itemCount() >= 5) if ((token.asOperator() == FormulaToken::OperatorRightPar) || (token.asOperator() == FormulaToken::OperatorComma)) { FormulaToken arg2 = syntaxStack.top(); FormulaToken sep = syntaxStack.top(1); FormulaToken arg1 = syntaxStack.top(2); FormulaToken par = syntaxStack.top(3); FormulaToken id = syntaxStack.top(4); if (!arg2.isOperator()) if (sep.asOperator() == FormulaToken::OperatorComma) if (!arg1.isOperator()) if (par.asOperator() == FormulaToken::OperatorLeftPar) if (id.isFunction()) { ruleFound = true; syntaxStack.pop(); syntaxStack.pop(); argCount++; } } // rule for function last argument: // id (arg) -> arg if (!ruleFound) if (syntaxStack.itemCount() >= 4) { FormulaToken par2 = syntaxStack.top(); FormulaToken arg = syntaxStack.top(1); FormulaToken par1 = syntaxStack.top(2); FormulaToken id = syntaxStack.top(3); if (par2.asOperator() == FormulaToken::OperatorRightPar) if (!arg.isOperator()) if (par1.asOperator() == FormulaToken::OperatorLeftPar) if (id.isFunction()) { ruleFound = true; syntaxStack.pop(); syntaxStack.pop(); syntaxStack.pop(); syntaxStack.pop(); syntaxStack.push(arg); m_codes.append(Opcode(Opcode::Function, argCount)); argCount = argStack.empty() ? 0 : argStack.pop(); } } // rule for parenthesis: (Y) -> Y if (!ruleFound) if (syntaxStack.itemCount() >= 3) { FormulaToken right = syntaxStack.top(); FormulaToken y = syntaxStack.top(1); FormulaToken left = syntaxStack.top(2); if (right.isOperator()) if (!y.isOperator()) if (left.isOperator()) if (right.asOperator() == FormulaToken::OperatorRightPar) if (left.asOperator() == FormulaToken::OperatorLeftPar) { ruleFound = true; syntaxStack.pop(); syntaxStack.pop(); syntaxStack.pop(); syntaxStack.push(y); } } // rule for binary operator: A (op) B -> A // conditions: precedence of op >= precedence of token // action: push (op) to result // e.g. "A * B" becomes 'A' if token is operator '+' if (!ruleFound) if (syntaxStack.itemCount() >= 3) { FormulaToken b = syntaxStack.top(); FormulaToken op = syntaxStack.top(1); FormulaToken a = syntaxStack.top(2); if (!a.isOperator()) if (!b.isOperator()) if (op.isOperator()) if (token.asOperator() != FormulaToken::OperatorLeftPar) if (opPrecedence(op.asOperator()) >= opPrecedence(token.asOperator())) { ruleFound = true; syntaxStack.pop(); syntaxStack.pop(); syntaxStack.pop(); syntaxStack.push(b); switch(op.asOperator()) { // simple binary operations case FormulaToken::OperatorAdd: m_codes.append(Opcode::Add); break; case FormulaToken::OperatorSub: m_codes.append(Opcode::Sub); break; case FormulaToken::OperatorMul: m_codes.append(Opcode::Mul); break; case FormulaToken::OperatorDiv: m_codes.append(Opcode::Div); break; default: break; } } } // rule for unary operator: (op1) (op2) X -> (op1) X // conditions: op2 is unary, token is not '(' // action: push (op2) to result // e.g. "* - 2" becomes '*' if (!ruleFound) if (token.asOperator() != FormulaToken::OperatorLeftPar) if (syntaxStack.itemCount() >= 3) { FormulaToken x = syntaxStack.top(); FormulaToken op2 = syntaxStack.top(1); FormulaToken op1 = syntaxStack.top(2); if (!x.isOperator()) if (op1.isOperator()) if (op2.isOperator()) if ((op2.asOperator() == FormulaToken::OperatorAdd) || (op2.asOperator() == FormulaToken::OperatorSub)) { ruleFound = true; syntaxStack.pop(); syntaxStack.pop(); syntaxStack.push(x); if (op2.asOperator() == FormulaToken::OperatorSub) m_codes.append(Opcode(Opcode::Neg)); } } // auxiliary rule for unary operator: (op) X -> X // conditions: op is unary, op is first in syntax stack, token is not '(' // action: push (op) to result if (!ruleFound) if (token.asOperator() != FormulaToken::OperatorLeftPar) if (syntaxStack.itemCount() == 2) { FormulaToken x = syntaxStack.top(); FormulaToken op = syntaxStack.top(1); if (!x.isOperator()) if (op.isOperator()) if ((op.asOperator() == FormulaToken::OperatorAdd) || (op.asOperator() == FormulaToken::OperatorSub)) { ruleFound = true; syntaxStack.pop(); syntaxStack.pop(); syntaxStack.push(x); if (op.asOperator() == FormulaToken::OperatorSub) m_codes.append(Opcode(Opcode::Neg)); } } if (!ruleFound) break; } syntaxStack.push(token); } } // syntaxStack must left only one operand and end-of-formula (i.e. InvalidOp) m_valid = false; if (syntaxStack.itemCount() == 2) if (syntaxStack.top().isOperator()) if (syntaxStack.top().asOperator() == FormulaToken::OperatorInvalid) if (!syntaxStack.top(1).isOperator()) m_valid = true; // bad parsing ? clean-up everything if (! m_valid) { m_constants.clear(); m_codes.clear(); kWarning() << "compiling of "<< m_text << " failed"; } return m_valid; } QString EnhancedPathFormula::toString() const { return m_text; } FormulaToken::FormulaToken(Type type, const QString &text, int position) : m_type(type), m_text(text), m_position(position) { } FormulaToken::FormulaToken(const FormulaToken &token) { if (this != &token) *this = token; } FormulaToken & FormulaToken::operator=(const FormulaToken &rhs) { if (this == &rhs) return *this; m_type = rhs.m_type; m_text = rhs.m_text; m_position = rhs.m_position; return *this; } qreal FormulaToken::asNumber() const { if (isNumber()) return m_text.toDouble(); else return 0.0; } FormulaToken::Operator FormulaToken::asOperator() const { if (isOperator()) return matchOperator(m_text); else return OperatorInvalid; } // helper function: return operator of given token text FormulaToken::Operator matchOperator(const QString &text) { if (text.length() != 1) return FormulaToken::OperatorInvalid; - QChar c(text[0]); - switch(c.toAscii()) { + const char c = text[0].toLatin1(); + switch(c) { case '+': return FormulaToken::OperatorAdd; break; case '-': return FormulaToken::OperatorSub; break; case '*': return FormulaToken::OperatorMul; break; case '/': return FormulaToken::OperatorDiv; break; case '(': return FormulaToken::OperatorLeftPar; break; case ')': return FormulaToken::OperatorRightPar; break; case ',': return FormulaToken::OperatorComma; break; default : return FormulaToken::OperatorInvalid; break; } } // helper function: return true for valid identifier character bool isIdentifier(QChar ch) { return (ch.unicode() == '?') || (ch.unicode() == '$') || (ch.isLetter()); } // helper function: give operator precedence // e.g. '+' is 1 while '*' is 3 int opPrecedence(FormulaToken::Operator op) { int prec = -1; switch(op) { case FormulaToken::OperatorMul: prec = 5; break; case FormulaToken::OperatorDiv: prec = 6; break; case FormulaToken::OperatorAdd: prec = 3; break; case FormulaToken::OperatorSub: prec = 3; break; case FormulaToken::OperatorComma: prec = 0; break; case FormulaToken::OperatorRightPar: prec = 0; break; case FormulaToken::OperatorLeftPar: prec = -1; break; default: prec = -1; break; } return prec; } EnhancedPathFormula::Function matchFunction(const QString &text) { if (text == "abs") return EnhancedPathFormula::FunctionAbs; if (text == "sqrt") return EnhancedPathFormula::FunctionSqrt; if (text == "sin") return EnhancedPathFormula::FunctionSin; if (text == "cos") return EnhancedPathFormula::FunctionCos; if (text == "tan") return EnhancedPathFormula::FunctionTan; if (text == "atan") return EnhancedPathFormula::FunctionAtan; if (text == "atan2") return EnhancedPathFormula::FunctionAtan2; if (text == "min") return EnhancedPathFormula::FunctionMin; if (text == "max") return EnhancedPathFormula::FunctionMax; if (text == "if") return EnhancedPathFormula::FunctionIf; return EnhancedPathFormula::FunctionUnknown; } QString matchFunction(EnhancedPathFormula::Function function) { switch(function) { case EnhancedPathFormula::FunctionAbs: return "fabs"; break; case EnhancedPathFormula::FunctionSqrt: return "sqrt"; break; case EnhancedPathFormula::FunctionSin: return "sin"; break; case EnhancedPathFormula::FunctionCos: return "cos"; break; case EnhancedPathFormula::FunctionTan: return "tan"; break; case EnhancedPathFormula::FunctionAtan: return "atan"; break; case EnhancedPathFormula::FunctionAtan2: return "atan2"; break; case EnhancedPathFormula::FunctionMin: return "min"; break; case EnhancedPathFormula::FunctionMax: return "max"; break; case EnhancedPathFormula::FunctionIf: return "if"; break; default: break; } return "unknown"; } void EnhancedPathFormula::debugTokens(const TokenList &tokens) { #ifndef NDEBUG for (int i = 0; i < tokens.count(); i++) kDebug() << tokens[i].text(); #else Q_UNUSED(tokens); #endif } void EnhancedPathFormula::debugOpcodes() { #ifndef NDEBUG foreach (const Opcode &c, m_codes) { QString ctext; switch(c.type) { case Opcode::Load: ctext = QString("Load #%1").arg(c.index); break; case Opcode::Ref: ctext = QString("Ref #%1").arg(c.index); break; case Opcode::Function: ctext = QString("Function (%1)").arg(c.index); break; case Opcode::Add: ctext = "Add"; break; case Opcode::Sub: ctext = "Sub"; break; case Opcode::Mul: ctext = "Mul"; break; case Opcode::Div: ctext = "Div"; break; case Opcode::Neg: ctext = "Neg"; break; default: ctext = "Unknown"; break; } kDebug() << ctext; } #endif } diff --git a/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp b/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp index 046e4897f83..de7e7dde3e4 100644 --- a/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp +++ b/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp @@ -1,682 +1,682 @@ /* This file is part of the KDE project * Copyright (C) 2007,2010,2011 Jan Hambrecht * Copyright (C) 2009-2010 Thomas Zander * Copyright (C) 2010 Carlos Licea * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Contact: Suresh Chande suresh.chande@nokia.com * Copyright (C) 2009-2010 Thorsten Zachmann * * 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 "EnhancedPathShape.h" #include "EnhancedPathCommand.h" #include "EnhancedPathParameter.h" #include "EnhancedPathHandle.h" #include "EnhancedPathFormula.h" #include #include #include #include #include #include #include EnhancedPathShape::EnhancedPathShape(const QRect &viewBox) : m_viewBox(viewBox) , m_viewBoxOffset(0.0, 0.0) , m_mirrorVertically(false) , m_mirrorHorizontally(false) , m_pathStretchPointX(-1) , m_pathStretchPointY(-1) , m_cacheResults(false) { } EnhancedPathShape::~EnhancedPathShape() { reset(); } void EnhancedPathShape::reset() { qDeleteAll(m_commands); m_commands.clear(); qDeleteAll(m_enhancedHandles); m_enhancedHandles.clear(); setHandles(QList()); qDeleteAll(m_formulae); m_formulae.clear(); qDeleteAll(m_parameters); m_parameters.clear(); m_modifiers.clear(); m_viewMatrix.reset(); m_viewBoxOffset = QPointF(); clear(); m_textArea.clear(); } void EnhancedPathShape::moveHandleAction(int handleId, const QPointF & point, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); EnhancedPathHandle *handle = m_enhancedHandles[ handleId ]; if (handle) { handle->changePosition(shapeToViewbox(point)); } } void EnhancedPathShape::updatePath(const QSizeF & size) { if (isParametricShape()) { clear(); enableResultCache(true); foreach (EnhancedPathCommand *cmd, m_commands) cmd->execute(); enableResultCache(false); qreal stretchPointsScale = 1; bool isStretched = useStretchPoints(size, stretchPointsScale); m_viewBound = outline().boundingRect(); m_mirrorMatrix.reset(); m_mirrorMatrix.translate(m_viewBound.center().x(), m_viewBound.center().y()); m_mirrorMatrix.scale(m_mirrorHorizontally ? -1 : 1, m_mirrorVertically ? -1 : 1); m_mirrorMatrix.translate(-m_viewBound.center().x(), -m_viewBound.center().y()); QTransform matrix(1.0, 0.0, 0.0, 1.0, m_viewBoxOffset.x(), m_viewBoxOffset.y()); // if stretch points are set than stretch the path manually if (isStretched) { //if the path was stretched manually the stretch matrix is not more valid //and it has to be recalculated so that stretching in x and y direction is the same matrix.scale(stretchPointsScale, stretchPointsScale); matrix = m_mirrorMatrix * matrix; } else { matrix = m_mirrorMatrix * m_viewMatrix * matrix; } foreach (KoSubpath *subpath, m_subpaths) { foreach (KoPathPoint *point, *subpath) { point->map(matrix); } } const int handleCount = m_enhancedHandles.count(); QList handles; for (int i = 0; i < handleCount; ++i) handles.append(matrix.map(m_enhancedHandles[i]->position())); setHandles(handles); normalize(); } } void EnhancedPathShape::setSize(const QSizeF &newSize) { // handle offset KoParameterShape::setSize(newSize); // calculate scaling factors from viewbox size to shape size qreal xScale = m_viewBound.width() == 0 ? 1 : newSize.width()/m_viewBound.width(); qreal yScale = m_viewBound.height() == 0 ? 1 : newSize.height()/m_viewBound.height(); // create view matrix, take mirroring into account m_viewMatrix.reset(); m_viewMatrix.scale(xScale, yScale); updatePath(newSize); } QPointF EnhancedPathShape::normalize() { QPointF offset = KoParameterShape::normalize(); m_viewBoxOffset -= offset; return offset; } QPointF EnhancedPathShape::shapeToViewbox(const QPointF &point) const { return (m_mirrorMatrix * m_viewMatrix).inverted().map( point-m_viewBoxOffset ); } void EnhancedPathShape::evaluateHandles() { const int handleCount = m_enhancedHandles.count(); QList handles; for (int i = 0; i < handleCount; ++i) handles.append(m_enhancedHandles[i]->position()); setHandles(handles); } QRect EnhancedPathShape::viewBox() const { return m_viewBox; } qreal EnhancedPathShape::evaluateReference(const QString &reference) { if (reference.isEmpty()) return 0.0; - QChar c = reference[0]; + const char c = reference[0].toLatin1(); qreal res = 0.0; - switch(c.toAscii()) { + switch(c) { // referenced modifier case '$': { bool success = false; int modifierIndex = reference.mid(1).toInt(&success); res = m_modifiers.value(modifierIndex); break; } // referenced formula case '?': { QString fname = reference.mid(1); if (m_cacheResults && m_resultChache.contains(fname)) { res = m_resultChache.value(fname); } else { FormulaStore::const_iterator formulaIt = m_formulae.constFind(fname); if (formulaIt != m_formulae.constEnd()) { EnhancedPathFormula * formula = formulaIt.value(); if (formula) { res = formula->evaluate(); if (m_cacheResults) m_resultChache.insert(fname, res); } } } break; } // maybe an identifier ? default: EnhancedPathNamedParameter p(reference, this); res = p.evaluate(); break; } return res; } qreal EnhancedPathShape::evaluateConstantOrReference(const QString &val) { bool ok = true; qreal res = val.toDouble(&ok); if (ok) return res; return evaluateReference(val); } void EnhancedPathShape::modifyReference(const QString &reference, qreal value) { if (reference.isEmpty()) return; - QChar c = reference[0]; + const char c = reference[0].toLatin1(); - if (c.toAscii() == '$') { + if (c == '$') { bool success = false; int modifierIndex = reference.mid(1).toInt(&success); if (modifierIndex >= 0 && modifierIndex < m_modifiers.count()) m_modifiers[modifierIndex] = value; } } EnhancedPathParameter * EnhancedPathShape::parameter(const QString & text) { Q_ASSERT(! text.isEmpty()); ParameterStore::const_iterator parameterIt = m_parameters.constFind(text); if (parameterIt != m_parameters.constEnd()) { return parameterIt.value(); } else { EnhancedPathParameter *parameter = 0; - QChar c = text[0]; - if (c.toAscii() == '$' || c.toAscii() == '?') { + const char c = text[0].toLatin1(); + if (c == '$' || c == '?') { parameter = new EnhancedPathReferenceParameter(text, this); } else { bool success = false; qreal constant = text.toDouble(&success); if (success) { parameter = new EnhancedPathConstantParameter(constant, this); } else { Identifier identifier = EnhancedPathNamedParameter::identifierFromString(text); if (identifier != IdentifierUnknown) parameter = new EnhancedPathNamedParameter(identifier, this); } } if (parameter) m_parameters[text] = parameter; return parameter; } } void EnhancedPathShape::addFormula(const QString &name, const QString &formula) { if (name.isEmpty() || formula.isEmpty()) return; m_formulae[name] = new EnhancedPathFormula(formula, this); } void EnhancedPathShape::addHandle(const QMap &handle) { if (handle.isEmpty()) return; if (! handle.contains("draw:handle-position")) return; QVariant position = handle.value("draw:handle-position"); QStringList tokens = position.toString().simplified().split(' '); if (tokens.count() < 2) return; EnhancedPathHandle *newHandle = new EnhancedPathHandle(this); newHandle->setPosition(parameter(tokens[0]), parameter(tokens[1])); // check if we have a polar handle if (handle.contains("draw:handle-polar")) { QVariant polar = handle.value("draw:handle-polar"); QStringList tokens = polar.toString().simplified().split(' '); if (tokens.count() == 2) { newHandle->setPolarCenter(parameter(tokens[0]), parameter(tokens[1])); QVariant minRadius = handle.value("draw:handle-radius-range-minimum"); QVariant maxRadius = handle.value("draw:handle-radius-range-maximum"); if (minRadius.isValid() && maxRadius.isValid()) newHandle->setRadiusRange(parameter(minRadius.toString()), parameter(maxRadius.toString())); } } else { QVariant minX = handle.value("draw:handle-range-x-minimum"); QVariant maxX = handle.value("draw:handle-range-x-maximum"); if (minX.isValid() && maxX.isValid()) newHandle->setRangeX(parameter(minX.toString()), parameter(maxX.toString())); QVariant minY = handle.value("draw:handle-range-y-minimum"); QVariant maxY = handle.value("draw:handle-range-y-maximum"); if (minY.isValid() && maxY.isValid()) newHandle->setRangeY(parameter(minY.toString()), parameter(maxY.toString())); } m_enhancedHandles.append(newHandle); evaluateHandles(); } void EnhancedPathShape::addModifiers(const QString &modifiers) { if (modifiers.isEmpty()) return; QStringList tokens = modifiers.simplified().split(' '); int tokenCount = tokens.count(); for (int i = 0; i < tokenCount; ++i) m_modifiers.append(tokens[i].toDouble()); } void EnhancedPathShape::addCommand(const QString &command) { addCommand(command, true); } void EnhancedPathShape::addCommand(const QString &command, bool triggerUpdate) { QString commandStr = command.simplified(); if (commandStr.isEmpty()) return; // the first character is the command EnhancedPathCommand *cmd = new EnhancedPathCommand(commandStr[0], this); // strip command char commandStr = commandStr.mid(1).simplified(); // now parse the command parameters if (!commandStr.isEmpty()) { QStringList tokens = commandStr.split(' '); for (int i = 0; i < tokens.count(); ++i) { cmd->addParameter(parameter(tokens[i])); } } m_commands.append(cmd); if (triggerUpdate) updatePath(size()); } bool EnhancedPathShape::useStretchPoints(const QSizeF &size, qreal &scale) { bool retval = false; if (m_pathStretchPointX != -1 && m_pathStretchPointY != -1) { qreal scaleX = size.width(); qreal scaleY = size.height(); if (m_viewBox.width() / m_viewBox.height() < scaleX / scaleY) { qreal deltaX = (scaleX * m_viewBox.height()) / scaleY - m_viewBox.width(); foreach (KoSubpath *subpath, m_subpaths) { foreach (KoPathPoint *currPoint, *subpath) { if (currPoint->point().x() >= m_pathStretchPointX && currPoint->controlPoint1().x() >= m_pathStretchPointX && currPoint->controlPoint2().x() >= m_pathStretchPointX) { currPoint->setPoint(QPointF(currPoint->point().x() + deltaX, currPoint->point().y())); currPoint->setControlPoint1(QPointF(currPoint->controlPoint1().x() + deltaX, currPoint->controlPoint1().y())); currPoint->setControlPoint2(QPointF(currPoint->controlPoint2().x() + deltaX, currPoint->controlPoint2().y())); retval = true; } } } scale = scaleY / m_viewBox.height(); } else if (m_viewBox.width() / m_viewBox.height() > scaleX / scaleY) { qreal deltaY = (m_viewBox.width() * scaleY) / scaleX - m_viewBox.height(); foreach (KoSubpath *subpath, m_subpaths) { foreach (KoPathPoint *currPoint, *subpath) { if (currPoint->point().y() >= m_pathStretchPointY && currPoint->controlPoint1().y() >= m_pathStretchPointY && currPoint->controlPoint2().y() >= m_pathStretchPointY) { currPoint->setPoint(QPointF(currPoint->point().x(), currPoint->point().y() + deltaY)); currPoint->setControlPoint1(QPointF(currPoint->controlPoint1().x(), currPoint->controlPoint1().y() + deltaY)); currPoint->setControlPoint2(QPointF(currPoint->controlPoint2().x(), currPoint->controlPoint2().y() + deltaY)); retval = true; } } } scale = scaleX / m_viewBox.width(); } } return retval; } void EnhancedPathShape::saveOdf(KoShapeSavingContext &context) const { if (isParametricShape()) { context.xmlWriter().startElement("draw:custom-shape"); const QSizeF currentSize = outline().boundingRect().size(); // save the right position so that when loading we fit the viewbox // to the right position without getting any wrong scaling // -> calculate the right position from the current 0 position / viewbound ratio // this is e.g. the case when there is a callout that goes into negative viewbound coordinates QPointF topLeft = m_viewBound.topLeft(); QPointF diff; if (qAbs(topLeft.x()) > 1E-5) { diff.setX(topLeft.x()*currentSize.width()/m_viewBound.width()); } if (qAbs(topLeft.y()) > 1E-5) { diff.setY(topLeft.y()*currentSize.height()/m_viewBound.height()); } if (diff.isNull()) { saveOdfAttributes(context, OdfAllAttributes&~OdfSize); } else { //FIXME: this needs to be fixed for shapes that are transformed by rotation or skewing QTransform offset(context.shapeOffset(this)); QTransform newOffset(offset); newOffset.translate(-diff.x(), -diff.y()); context.addShapeOffset(this, newOffset); saveOdfAttributes(context, OdfAllAttributes&~OdfSize); if (offset.isIdentity()) { context.removeShapeOffset(this); } else { context.addShapeOffset(this, offset); } } // save the right size so that when loading we fit the viewbox // to the right size without getting any wrong scaling // -> calculate the right size from the current size/viewbound ratio context.xmlWriter().addAttributePt("svg:width", currentSize.width() == 0 ? 0 : m_viewBox.width()*currentSize.width()/m_viewBound.width()); context.xmlWriter().addAttributePt("svg:height", currentSize.height() == 0 ? 0 : m_viewBox.height()*currentSize.height()/m_viewBound.height()); saveText(context); context.xmlWriter().startElement("draw:enhanced-geometry"); context.xmlWriter().addAttribute("svg:viewBox", QString("%1 %2 %3 %4").arg(m_viewBox.x()).arg(m_viewBox.y()).arg(m_viewBox.width()).arg(m_viewBox.height())); if (m_pathStretchPointX != -1) { context.xmlWriter().addAttribute("draw:path-stretchpoint-x", m_pathStretchPointX); } if (m_pathStretchPointY != -1) { context.xmlWriter().addAttribute("draw:path-stretchpoint-y", m_pathStretchPointY); } if (m_mirrorHorizontally) { context.xmlWriter().addAttribute("draw:mirror-horizontal", "true"); } if (m_mirrorVertically) { context.xmlWriter().addAttribute("draw:mirror-vertical", "true"); } QString modifiers; foreach (qreal modifier, m_modifiers) modifiers += QString::number(modifier) + ' '; context.xmlWriter().addAttribute("draw:modifiers", modifiers.trimmed()); if (m_textArea.size() >= 4) { context.xmlWriter().addAttribute("draw:text-areas", m_textArea.join(" ")); } QString path; foreach (EnhancedPathCommand * c, m_commands) path += c->toString() + ' '; context.xmlWriter().addAttribute("draw:enhanced-path", path.trimmed()); FormulaStore::const_iterator i = m_formulae.constBegin(); for (; i != m_formulae.constEnd(); ++i) { context.xmlWriter().startElement("draw:equation"); context.xmlWriter().addAttribute("draw:name", i.key()); context.xmlWriter().addAttribute("draw:formula", i.value()->toString()); context.xmlWriter().endElement(); // draw:equation } foreach (EnhancedPathHandle * handle, m_enhancedHandles) handle->saveOdf(context); context.xmlWriter().endElement(); // draw:enhanced-geometry saveOdfCommonChildElements(context); context.xmlWriter().endElement(); // draw:custom-shape } else { KoPathShape::saveOdf(context); } } bool EnhancedPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context) { reset(); const KoXmlElement enhancedGeometry(KoXml::namedItemNS(element, KoXmlNS::draw, "enhanced-geometry" ) ); if (!enhancedGeometry.isNull() ) { setPathStretchPointX(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-x","-1").toDouble()); setPathStretchPointY(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-y","-1").toDouble()); // load the modifiers QString modifiers = enhancedGeometry.attributeNS(KoXmlNS::draw, "modifiers", ""); if (! modifiers.isEmpty()) { addModifiers(modifiers); } m_textArea = enhancedGeometry.attributeNS(KoXmlNS::draw, "text-areas", "").split(' '); if (m_textArea.size() >= 4) { setResizeBehavior(TextFollowsPreferredTextRect); } KoXmlElement grandChild; forEachElement(grandChild, enhancedGeometry) { if (grandChild.namespaceURI() != KoXmlNS::draw) continue; if (grandChild.localName() == "equation") { QString name = grandChild.attributeNS(KoXmlNS::draw, "name"); QString formula = grandChild.attributeNS(KoXmlNS::draw, "formula"); addFormula(name, formula); } else if (grandChild.localName() == "handle") { EnhancedPathHandle * handle = new EnhancedPathHandle(this); if (handle->loadOdf(grandChild, context)) { m_enhancedHandles.append(handle); evaluateHandles(); } else { delete handle; } } } setMirrorHorizontally(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-horizontal") == "true"); setMirrorVertically(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-vertical") == "true"); // load the enhanced path data QString path = enhancedGeometry.attributeNS(KoXmlNS::draw, "enhanced-path", ""); #ifndef NWORKAROUND_ODF_BUGS KoOdfWorkaround::fixEnhancedPath(path, enhancedGeometry, context); #endif // load the viewbox m_viewBox = loadOdfViewbox(enhancedGeometry); if (!path.isEmpty()) { parsePathData(path); } if (m_viewBox.isEmpty()) { // if there is no view box defined make it is big as the path. m_viewBox = m_viewBound.toAlignedRect(); } } QSizeF size; size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString()))); size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString()))); // the viewbox is to be fitted into the size of the shape, so before setting // the size we just loaded // we set the viewbox to be the basis to calculate // the viewbox matrix from m_viewBound = m_viewBox; setSize(size); QPointF pos; pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString()))); pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString()))); setPosition(pos - m_viewMatrix.map(QPointF(0, 0)) - m_viewBoxOffset); loadOdfAttributes(element, context, OdfMandatories | OdfTransformation | OdfAdditionalAttributes | OdfCommonChildElements); loadText(element, context); return true; } void EnhancedPathShape::parsePathData(const QString &data) { if (data.isEmpty()) return; int start = -1; bool separator = true; for (int i = 0; i < data.length(); ++i) { QChar ch = data.at(i); ushort uni_ch = ch.unicode(); if (separator && (uni_ch == 'M' || uni_ch == 'L' || uni_ch == 'C' || uni_ch == 'Z' || uni_ch == 'N' || uni_ch == 'F' || uni_ch == 'S' || uni_ch == 'T' || uni_ch == 'U' || uni_ch == 'A' || uni_ch == 'B' || uni_ch == 'W' || uni_ch == 'V' || uni_ch == 'X' || uni_ch == 'Y' || uni_ch == 'Q')) { if (start != -1) { // process last chars addCommand(data.mid(start, i - start), false); } start = i; } separator = ch.isSpace(); } if (start < data.length()) addCommand(data.mid(start)); if (start != -1) updatePath(size()); } void EnhancedPathShape::setMirrorHorizontally(bool mirrorHorizontally) { if( m_mirrorHorizontally != mirrorHorizontally) { m_mirrorHorizontally = mirrorHorizontally; updatePath(size()); } } void EnhancedPathShape::setMirrorVertically(bool mirrorVertically) { if( m_mirrorVertically != mirrorVertically) { m_mirrorVertically = mirrorVertically; updatePath(size()); } } void EnhancedPathShape::shapeChanged(ChangeType type, KoShape *shape) { KoParameterShape::shapeChanged(type, shape); if (!shape || shape == this) { if (type == ParentChanged || type == ParameterChanged) { updateTextArea(); } } } void EnhancedPathShape::updateTextArea() { if (m_textArea.size() >= 4) { QRectF r = m_viewBox; r.setLeft(evaluateConstantOrReference(m_textArea[0])); r.setTop(evaluateConstantOrReference(m_textArea[1])); r.setRight(evaluateConstantOrReference(m_textArea[2])); r.setBottom(evaluateConstantOrReference(m_textArea[3])); r = m_viewMatrix.mapRect(r).translated(m_viewBoxOffset); setPreferredTextRect(r); } } void EnhancedPathShape::enableResultCache(bool enable) { m_resultChache.clear(); m_cacheResults = enable; } void EnhancedPathShape::setPathStretchPointX(qreal pathStretchPointX) { if (m_pathStretchPointX != pathStretchPointX) { m_pathStretchPointX = pathStretchPointX; } } void EnhancedPathShape::setPathStretchPointY(qreal pathStretchPointY) { if (m_pathStretchPointY != pathStretchPointY) { m_pathStretchPointY = pathStretchPointY; } } diff --git a/plugins/reporting/barcode/3of9.cpp b/plugins/reporting/barcode/3of9.cpp index 4eb54501564..a1b9431e473 100644 --- a/plugins/reporting/barcode/3of9.cpp +++ b/plugins/reporting/barcode/3of9.cpp @@ -1,188 +1,188 @@ /* * OpenRPT report writer and rendering engine * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com) * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * This file contains the implementation of the 3of9 barcode renderer. * All this code assumes a 100dpi rendering surface for it's calculations. */ #include #include #include #include #include #include #include "renderobjects.h" struct code3of9 { char code; int values[9]; }; const struct code3of9 _3of9codes[] = { { '0', { 0, 0, 0, 1, 1, 0, 1, 0, 0 } }, { '1', { 1, 0, 0, 1, 0, 0, 0, 0, 1 } }, { '2', { 0, 0, 1, 1, 0, 0, 0, 0, 1 } }, { '3', { 1, 0, 1, 1, 0, 0, 0, 0, 0 } }, { '4', { 0, 0, 0, 1, 1, 0, 0, 0, 1 } }, { '5', { 1, 0, 0, 1, 1, 0, 0, 0, 0 } }, { '6', { 0, 0, 1, 1, 1, 0, 0, 0, 0 } }, { '7', { 0, 0, 0, 1, 0, 0, 1, 0, 1 } }, { '8', { 1, 0, 0, 1, 0, 0, 1, 0, 0 } }, { '9', { 0, 0, 1, 1, 0, 0, 1, 0, 0 } }, { 'A', { 1, 0, 0, 0, 0, 1, 0, 0, 1 } }, { 'B', { 0, 0, 1, 0, 0, 1, 0, 0, 1 } }, { 'C', { 1, 0, 1, 0, 0, 1, 0, 0, 0 } }, { 'D', { 0, 0, 0, 0, 1, 1, 0, 0, 1 } }, { 'E', { 1, 0, 0, 0, 1, 1, 0, 0, 0 } }, { 'F', { 0, 0, 1, 0, 1, 1, 0, 0, 0 } }, { 'G', { 0, 0, 0, 0, 0, 1, 1, 0, 1 } }, { 'H', { 1, 0, 0, 0, 0, 1, 1, 0, 0 } }, { 'I', { 0, 0, 1, 0, 0, 1, 1, 0, 0 } }, { 'J', { 0, 0, 0, 0, 1, 1, 1, 0, 0 } }, { 'K', { 1, 0, 0, 0, 0, 0, 0, 1, 1 } }, { 'L', { 0, 0, 1, 0, 0, 0, 0, 1, 1 } }, { 'M', { 1, 0, 1, 0, 0, 0, 0, 1, 0 } }, { 'N', { 0, 0, 0, 0, 1, 0, 0, 1, 1 } }, { 'O', { 1, 0, 0, 0, 1, 0, 0, 1, 0 } }, { 'P', { 0, 0, 1, 0, 1, 0, 0, 1, 0 } }, { 'Q', { 0, 0, 0, 0, 0, 0, 1, 1, 1 } }, { 'R', { 1, 0, 0, 0, 0, 0, 1, 1, 0 } }, { 'S', { 0, 0, 1, 0, 0, 0, 1, 1, 0 } }, { 'T', { 0, 0, 0, 0, 1, 0, 1, 1, 0 } }, { 'U', { 1, 1, 0, 0, 0, 0, 0, 0, 1 } }, { 'V', { 0, 1, 1, 0, 0, 0, 0, 0, 1 } }, { 'W', { 1, 1, 1, 0, 0, 0, 0, 0, 0 } }, { 'X', { 0, 1, 0, 0, 1, 0, 0, 0, 1 } }, { 'Y', { 1, 1, 0, 0, 1, 0, 0, 0, 0 } }, { 'Z', { 0, 1, 1, 0, 1, 0, 0, 0, 0 } }, { '-', { 0, 1, 0, 0, 0, 0, 1, 0, 1 } }, { '.', { 1, 1, 0, 0, 0, 0, 1, 0, 0 } }, { ' ', { 0, 1, 1, 0, 0, 0, 1, 0, 0 } }, { '$', { 0, 1, 0, 1, 0, 1, 0, 0, 0 } }, { '/', { 0, 1, 0, 1, 0, 0, 0, 1, 0 } }, { '+', { 0, 1, 0, 0, 0, 1, 0, 1, 0 } }, { '%', { 0, 0, 0, 1, 0, 1, 0, 1, 0 } }, { '*', { 0, 1, 0, 0, 1, 0, 1, 0, 0 } }, // this is a special start/stop character { '\0', { 0, 0, 0, 0, 0, 0, 0, 0, 0 } } // null termininator of list }; int codeIndex(QChar code) { // we are a case insensitive search - code = code.toUpper(); + const char latin1Code = code.toUpper().toLatin1(); for (int idx = 0; _3of9codes[idx].code != '\0'; idx++) { - if (_3of9codes[idx].code == code.toAscii()) return idx; + if (_3of9codes[idx].code == latin1Code) return idx; } return -1; // couldn't find it } void render3of9(OROPage * page, const QRectF & r, const QString & _str, int align) { QString str = _str; // lets determine some core attributes about this barcode qreal narrow_bar = 1; // a narrow bar is 1/100th inch wide qreal interchange_gap = narrow_bar; // the space between each 'set' of bars int bar_width_mult = 2; // the wide bar width multiple of the narrow bar // this is our mandatory minimum quiet zone qreal quiet_zone = narrow_bar * 10; if (quiet_zone < 0.1) quiet_zone = 0.1; // what kind of area do we have to work with qreal draw_width = r.width(); qreal draw_height = r.height(); // how long is the value we need to encode? int val_length = str.length(); // L = (C + 2)(3N + 6)X + (C + 1)I // L length of barcode (excluding quite zone) in units same as X and I // C the number of characters in the value excluding the start/stop // N the bar width multiple for wide bars // X the width of a bar (pixels in our case) // I the interchange gap in the same units as X (value is same as X for our case) qreal L; qreal C = val_length; qreal N = bar_width_mult; qreal X = narrow_bar; qreal I = interchange_gap; L = ((C + 2.0) * (3.0 * N + 6.0) * X) + ((C + 1.0) * I); // now we have the actual width the barcode will be so can determine the actual // size of the quiet zone (we assume we center the barcode in the given area // what should we do if the area is too small???? // At the moment the way the code is written is we will always start at the minimum // required quiet zone if we don't have enough space.... I guess we'll just have over-run // to the right // // calculate the starting position based on the alignment option // for left align we don't need to do anything as the values are already setup for it if (align == 1) { // center qreal nqz = (draw_width - L) / 2.0; if (nqz > quiet_zone) quiet_zone = nqz; } else if (align > 1) // right quiet_zone = draw_width - (L + quiet_zone); //else if(align < 1) {} // left : do nothing qreal pos = r.left() + quiet_zone; qreal top = r.top(); // ok we need to prepend and append the str with a * //str = QString().sprintf("*%s*",(const char*)str); str = '*' + str + '*'; QPen pen(Qt::NoPen); QBrush brush(QColor("black")); for (int i = 0; i < str.length(); i++) { // loop through each char and render the barcode QChar c = str.at(i); int idx = codeIndex(c); kDebug() << idx; if (idx == -1) { qDebug("Encountered a non-compliant character while rendering a 3of9 barcode -- skipping"); continue; } bool space = false; for (int b = 0; b < 9; b++, space = !space) { qreal w = (_3of9codes[idx].values[b] == 1 ? narrow_bar * bar_width_mult : narrow_bar); kDebug() << w << space; if (!space) { ORORect * rect = new ORORect(); rect->setPen(pen); rect->setBrush(brush); rect->setRect(QRectF(pos, top, w, draw_height)); page->addPrimitive(rect); } pos += w; } pos += interchange_gap; } } diff --git a/plugins/reporting/barcode/3of9paint.cpp b/plugins/reporting/barcode/3of9paint.cpp index 57547c6744c..297cf007d3e 100644 --- a/plugins/reporting/barcode/3of9paint.cpp +++ b/plugins/reporting/barcode/3of9paint.cpp @@ -1,187 +1,187 @@ /* * OpenRPT report writer and rendering engine * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com) * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * This file contains the implementation of the 3of9 barcode renderer. * All this code assumes a 100dpi rendering surface for it's calculations. */ #include #include #include #include #include struct code3of9 { char code; int values[9]; }; const struct code3of9 _3of9codes[] = { { '0', { 0, 0, 0, 1, 1, 0, 1, 0, 0 } }, { '1', { 1, 0, 0, 1, 0, 0, 0, 0, 1 } }, { '2', { 0, 0, 1, 1, 0, 0, 0, 0, 1 } }, { '3', { 1, 0, 1, 1, 0, 0, 0, 0, 0 } }, { '4', { 0, 0, 0, 1, 1, 0, 0, 0, 1 } }, { '5', { 1, 0, 0, 1, 1, 0, 0, 0, 0 } }, { '6', { 0, 0, 1, 1, 1, 0, 0, 0, 0 } }, { '7', { 0, 0, 0, 1, 0, 0, 1, 0, 1 } }, { '8', { 1, 0, 0, 1, 0, 0, 1, 0, 0 } }, { '9', { 0, 0, 1, 1, 0, 0, 1, 0, 0 } }, { 'A', { 1, 0, 0, 0, 0, 1, 0, 0, 1 } }, { 'B', { 0, 0, 1, 0, 0, 1, 0, 0, 1 } }, { 'C', { 1, 0, 1, 0, 0, 1, 0, 0, 0 } }, { 'D', { 0, 0, 0, 0, 1, 1, 0, 0, 1 } }, { 'E', { 1, 0, 0, 0, 1, 1, 0, 0, 0 } }, { 'F', { 0, 0, 1, 0, 1, 1, 0, 0, 0 } }, { 'G', { 0, 0, 0, 0, 0, 1, 1, 0, 1 } }, { 'H', { 1, 0, 0, 0, 0, 1, 1, 0, 0 } }, { 'I', { 0, 0, 1, 0, 0, 1, 1, 0, 0 } }, { 'J', { 0, 0, 0, 0, 1, 1, 1, 0, 0 } }, { 'K', { 1, 0, 0, 0, 0, 0, 0, 1, 1 } }, { 'L', { 0, 0, 1, 0, 0, 0, 0, 1, 1 } }, { 'M', { 1, 0, 1, 0, 0, 0, 0, 1, 0 } }, { 'N', { 0, 0, 0, 0, 1, 0, 0, 1, 1 } }, { 'O', { 1, 0, 0, 0, 1, 0, 0, 1, 0 } }, { 'P', { 0, 0, 1, 0, 1, 0, 0, 1, 0 } }, { 'Q', { 0, 0, 0, 0, 0, 0, 1, 1, 1 } }, { 'R', { 1, 0, 0, 0, 0, 0, 1, 1, 0 } }, { 'S', { 0, 0, 1, 0, 0, 0, 1, 1, 0 } }, { 'T', { 0, 0, 0, 0, 1, 0, 1, 1, 0 } }, { 'U', { 1, 1, 0, 0, 0, 0, 0, 0, 1 } }, { 'V', { 0, 1, 1, 0, 0, 0, 0, 0, 1 } }, { 'W', { 1, 1, 1, 0, 0, 0, 0, 0, 0 } }, { 'X', { 0, 1, 0, 0, 1, 0, 0, 0, 1 } }, { 'Y', { 1, 1, 0, 0, 1, 0, 0, 0, 0 } }, { 'Z', { 0, 1, 1, 0, 1, 0, 0, 0, 0 } }, { '-', { 0, 1, 0, 0, 0, 0, 1, 0, 1 } }, { '.', { 1, 1, 0, 0, 0, 0, 1, 0, 0 } }, { ' ', { 0, 1, 1, 0, 0, 0, 1, 0, 0 } }, { '$', { 0, 1, 0, 1, 0, 1, 0, 0, 0 } }, { '/', { 0, 1, 0, 1, 0, 0, 0, 1, 0 } }, { '+', { 0, 1, 0, 0, 0, 1, 0, 1, 0 } }, { '%', { 0, 0, 0, 1, 0, 1, 0, 1, 0 } }, { '*', { 0, 1, 0, 0, 1, 0, 1, 0, 0 } }, // this is a special start/stop character { '\0', { 0, 0, 0, 0, 0, 0, 0, 0, 0 } } // null termininator of list }; int codeIndexP(QChar code) { // we are a case insensitive search - code = code.toUpper(); + const char latin1Code = code.toUpper().toLatin1(); for (int idx = 0; _3of9codes[idx].code != '\0'; idx++) { - if (_3of9codes[idx].code == code.toAscii()) return idx; + if (_3of9codes[idx].code == latin1Code) return idx; } return -1; // couldn't find it } void render3of9(const QRect & r, const QString & _str, int align, QPainter * pPainter) { QString str = _str; // lets determine some core attributes about this barcode int narrow_bar = 1; // a narrow bar is 1px wide int interchange_gap = narrow_bar; // the space between each 'set' of bars int bar_width_mult = 2; // the wide bar width multiple of the narrow bar // this is are mandatory minimum quiet zone int quiet_zone = narrow_bar * 10; if (quiet_zone < 10) quiet_zone = 10; // what kind of area do we have to work with int draw_width = r.width(); int draw_height = r.height(); // how long is the value we need to encode? int val_length = str.length(); // L = (C + 2)(3N + 6)X + (C + 1)I // L length of barcode (excluding quite zone) in units same as X and I // C the number of characters in the value excluding the start/stop // N the bar width multiple for wide bars // X the width of a bar (pixels in our case) // I the interchange gap in the same units as X (value is same as X for our case) int L; int C = val_length; int N = bar_width_mult; int X = narrow_bar; int I = interchange_gap; L = ((C + 2) * (3 * N + 6) * X) + ((C + 1) * I); // now we have the actual width the barcode will be so can determine the actual // size of the quiet zone (we assume we center the barcode in the given area // what should we do if the area is too small???? // At the moment the way the code is written is we will always start at the minimum // required quiet zone if we don't have enough space.... I guess we'll just have over-run // to the right // // calculate the starting position based on the alignment option // for left align we don't need to do anything as the values are already setup for it if (align == 1) { // center int nqz = (draw_width - L) / 2; if (nqz > quiet_zone) quiet_zone = nqz; } else if (align > 1) { // right quiet_zone = draw_width - (L + quiet_zone); } // else if(align < 1) {} // left : do nothing int pos = r.left() + quiet_zone; int top = r.top(); // ok we need to prepend and append the str with a * str = '*' + str + '*'; if (pPainter) { pPainter->save(); QPen oneWide(pPainter->pen()); oneWide.setWidth(1); #ifndef Q_WS_WIN32 oneWide.setJoinStyle(Qt::MiterJoin); #endif pPainter->setPen(oneWide); pPainter->setBrush(pPainter->pen().color()); } for (int i = 0; i < str.length(); i++) { // loop through each char and render the barcode QChar c = str.at(i); int idx = codeIndexP(c); if (idx == -1) { qDebug("Encountered a non-compliant character while rendering a 3of9 barcode -- skipping"); continue; } bool space = false; for (int b = 0; b < 9; b++, space = !space) { int w = (_3of9codes[idx].values[b] == 1 ? narrow_bar * bar_width_mult : narrow_bar); if (!space && pPainter) { pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color()); } pos += w; } pos += interchange_gap; } if (pPainter) { pPainter->restore(); } } diff --git a/plugins/reporting/barcode/code128.cpp b/plugins/reporting/barcode/code128.cpp index 5a1e373dafe..2bab68760ed 100644 --- a/plugins/reporting/barcode/code128.cpp +++ b/plugins/reporting/barcode/code128.cpp @@ -1,347 +1,348 @@ /* * OpenRPT report writer and rendering engine * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com) * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * This file contains the implementation of the Code 128 barcode renderer. * All this code assumes a 100dpi rendering surface for it's calculations. */ #include #include #include #include #include #include #include "renderobjects.h" static const int SETA = 0; static const int SETB = 1; static const int SETC = 2; static const char FNC1 = (char)130; static const char FNC2 = (char)131; static const char FNC3 = (char)132; static const char FNC4 = (char)133; static const char SHIFT = (char)134; static const char CODEA = (char)135; static const char CODEB = (char)136; static const char CODEC = (char)137; static const char STARTA = (char)138; static const char STARTB = (char)139; static const char STARTC = (char)140; struct code128 { char codea; char codeb; char codec; int values[6]; bool _null; }; static const struct code128 _128codes[] = { // A , B , C , { B S B S B S }, NULL? }, { ' ', ' ', 0, { 2, 1, 2, 2, 2, 2 }, false }, { '!', '!', 1, { 2, 2, 2, 1, 2, 2 }, false }, { '"', '"', 2, { 2, 2, 2, 2, 2, 1 }, false }, { '#', '#', 3, { 1, 2, 1, 2, 2, 3 }, false }, { '$', '$', 4, { 1, 2, 1, 3, 2, 2 }, false }, { '%', '%', 5, { 1, 3, 1, 2, 2, 2 }, false }, { '&', '&', 6, { 1, 2, 2, 2, 1, 3 }, false }, { '\'', '\'', 7, { 1, 2, 2, 3, 1, 2 }, false }, { '(', '(', 8, { 1, 3, 2, 2, 1, 2 }, false }, { ')', ')', 9, { 2, 2, 1, 2, 1, 3 }, false }, { '*', '*', 10, { 2, 2, 1, 3, 1, 2 }, false }, { '+', '+', 11, { 2, 3, 1, 2, 1, 2 }, false }, { ',', ',', 12, { 1, 1, 2, 2, 3, 2 }, false }, { '-', '-', 13, { 1, 2, 2, 1, 3, 2 }, false }, { '.', '.', 14, { 1, 2, 2, 2, 3, 1 }, false }, { '/', '/', 15, { 1, 1, 3, 2, 2, 2 }, false }, { '0', '0', 16, { 1, 2, 3, 1, 2, 2 }, false }, { '1', '1', 17, { 1, 2, 3, 2, 2, 1 }, false }, { '2', '2', 18, { 2, 2, 3, 2, 1, 1 }, false }, { '3', '3', 19, { 2, 2, 1, 1, 3, 2 }, false }, { '4', '4', 20, { 2, 2, 1, 2, 3, 1 }, false }, { '5', '5', 21, { 2, 1, 3, 2, 1, 2 }, false }, { '6', '6', 22, { 2, 2, 3, 1, 1, 2 }, false }, { '7', '7', 23, { 3, 1, 2, 1, 3, 1 }, false }, { '8', '8', 24, { 3, 1, 1, 2, 2, 2 }, false }, { '9', '9', 25, { 3, 2, 1, 1, 2, 2 }, false }, { ':', ':', 26, { 3, 2, 1, 2, 2, 1 }, false }, { ';', ';', 27, { 3, 1, 2, 2, 1, 2 }, false }, { '<', '<', 28, { 3, 2, 2, 1, 1, 2 }, false }, { '=', '=', 29, { 3, 2, 2, 2, 1, 1 }, false }, { '>', '>', 30, { 2, 1, 2, 1, 2, 3 }, false }, { '?', '?', 31, { 2, 1, 2, 3, 2, 1 }, false }, { '@', '@', 32, { 2, 3, 2, 1, 2, 1 }, false }, { 'A', 'A', 33, { 1, 1, 1, 3, 2, 3 }, false }, { 'B', 'B', 34, { 1, 3, 1, 1, 2, 3 }, false }, { 'C', 'C', 35, { 1, 3, 1, 3, 2, 1 }, false }, { 'D', 'D', 36, { 1, 1, 2, 3, 1, 3 }, false }, { 'E', 'E', 37, { 1, 3, 2, 1, 1, 3 }, false }, { 'F', 'F', 38, { 1, 3, 2, 3, 1, 1 }, false }, { 'G', 'G', 39, { 2, 1, 1, 3, 1, 3 }, false }, { 'H', 'H', 40, { 2, 3, 1, 1, 1, 3 }, false }, { 'I', 'I', 41, { 2, 3, 1, 3, 1, 1 }, false }, { 'J', 'J', 42, { 1, 1, 2, 1, 3, 3 }, false }, { 'K', 'K', 43, { 1, 1, 2, 3, 3, 1 }, false }, { 'L', 'L', 44, { 1, 3, 2, 1, 3, 1 }, false }, { 'M', 'M', 45, { 1, 1, 3, 1, 2, 3 }, false }, { 'N', 'N', 46, { 1, 1, 3, 3, 2, 1 }, false }, { 'O', 'O', 47, { 1, 3, 3, 1, 2, 1 }, false }, { 'P', 'P', 48, { 3, 1, 3, 1, 2, 1 }, false }, { 'Q', 'Q', 49, { 2, 1, 1, 3, 3, 1 }, false }, { 'R', 'R', 50, { 2, 3, 1, 1, 3, 1 }, false }, { 'S', 'S', 51, { 2, 1, 3, 1, 1, 3 }, false }, { 'T', 'T', 52, { 2, 1, 3, 3, 1, 1 }, false }, { 'U', 'U', 53, { 2, 1, 3, 1, 3, 1 }, false }, { 'V', 'V', 54, { 3, 1, 1, 1, 2, 3 }, false }, { 'W', 'W', 55, { 3, 1, 1, 3, 2, 1 }, false }, { 'X', 'X', 56, { 3, 3, 1, 1, 2, 1 }, false }, { 'Y', 'Y', 57, { 3, 1, 2, 1, 1, 3 }, false }, { 'Z', 'Z', 58, { 3, 1, 2, 3, 1, 1 }, false }, { '[', '[', 59, { 3, 3, 2, 1, 1, 1 }, false }, { '\\', '\\', 60, { 3, 1, 4, 1, 1, 1 }, false }, { ']', ']', 61, { 2, 2, 1, 4, 1, 1 }, false }, { '^', '^', 62, { 4, 3, 1, 1, 1, 1 }, false }, { '_', '_', 63, { 1, 1, 1, 2, 2, 4 }, false }, { 0x00, '`', 64, { 1, 1, 1, 4, 2, 2 }, false }, // NUL { 0x01, 'a', 65, { 1, 2, 1, 1, 2, 4 }, false }, // SOH { 0x02, 'b', 66, { 1, 2, 1, 4, 2, 1 }, false }, // STX { 0x03, 'c', 67, { 1, 4, 1, 1, 2, 2 }, false }, // ETX { 0x04, 'd', 68, { 1, 4, 1, 2, 2, 1 }, false }, // EOT { 0x05, 'e', 69, { 1, 1, 2, 2, 1, 4 }, false }, // ENQ { 0x06, 'f', 70, { 1, 1, 2, 4, 1, 2 }, false }, // ACK { 0x07, 'g', 71, { 1, 2, 2, 1, 1, 4 }, false }, // BEL { 0x08, 'h', 72, { 1, 2, 2, 4, 1, 1 }, false }, // BS { 0x09, 'i', 73, { 1, 4, 2, 1, 1, 2 }, false }, // HT { 0x0A, 'j', 74, { 1, 4, 2, 2, 1, 1 }, false }, // LF { 0x0B, 'k', 75, { 2, 4, 1, 2, 1, 1 }, false }, // VT { 0x0C, 'l', 76, { 2, 2, 1, 1, 1, 4 }, false }, // FF { 0x0D, 'm', 77, { 4, 1, 3, 1, 1, 1 }, false }, // CR { 0x0E, 'n', 78, { 2, 4, 1, 1, 1, 2 }, false }, // SO { 0x0F, 'o', 79, { 1, 3, 4, 1, 1, 1 }, false }, // SI { 0x10, 'p', 80, { 1, 1, 1, 2, 4, 2 }, false }, // DLE { 0x11, 'q', 81, { 1, 2, 1, 1, 4, 2 }, false }, // DC1 { 0x12, 'r', 82, { 1, 2, 1, 2, 4, 1 }, false }, // DC2 { 0x13, 's', 83, { 1, 1, 4, 2, 1, 2 }, false }, // DC3 { 0x14, 't', 84, { 1, 2, 4, 1, 1, 2 }, false }, // DC4 { 0x15, 'u', 85, { 1, 2, 4, 2, 1, 1 }, false }, // NAK { 0x16, 'v', 86, { 4, 1, 1, 2, 1, 2 }, false }, // SYN { 0x17, 'w', 87, { 4, 2, 1, 1, 1, 2 }, false }, // ETB { 0x18, 'x', 88, { 4, 2, 1, 2, 1, 1 }, false }, // CAN { 0x19, 'y', 89, { 2, 1, 2, 1, 4, 1 }, false }, // EM { 0x1A, 'z', 90, { 2, 1, 4, 1, 2, 1 }, false }, // SUB { 0x1B, '{', 91, { 4, 1, 2, 1, 2, 1 }, false }, // ESC { 0x1C, '|', 92, { 1, 1, 1, 1, 4, 3 }, false }, // FS { 0x1D, '}', 93, { 1, 1, 1, 3, 4, 1 }, false }, // GS { 0x1E, '~', 94, { 1, 3, 1, 1, 4, 1 }, false }, // RS { 0x1F, 0x7F, 95, { 1, 1, 4, 1, 1, 3 }, false }, // US DEL { FNC3, FNC3, 96, { 1, 1, 4, 3, 1, 1 }, false }, // FNC3 FNC3 { FNC2, FNC2, 97, { 4, 1, 1, 1, 1, 3 }, false }, // FNC2 FNC2 { SHIFT, SHIFT, 98, { 4, 1, 1, 3, 1, 1 }, false }, // SHIFT SHIFT { CODEC, CODEC, 99, { 1, 1, 3, 1, 4, 1 }, false }, // CODEC CODEC { CODEB, FNC4, CODEB, { 1, 1, 4, 1, 3, 1 }, false }, // CODEB FNC4 CODEB { FNC4, CODEA, CODEA, { 3, 1, 1, 1, 4, 1 }, false }, // FNC4 CODEA CODEA { FNC1, FNC1, FNC1, { 4, 1, 1, 1, 3, 1 }, false }, // FNC1 FNC1 FNC1 { STARTA, STARTA, STARTA, { 2, 1, 1, 4, 1, 2 }, false }, // STARTA { STARTB, STARTB, STARTB, { 2, 1, 1, 2, 1, 4 }, false }, // STARTB { STARTC, STARTC, STARTC, { 2, 1, 1, 2, 3, 2 }, false }, // STARTC { '\0', '\0', '\0', { 0, 0, 0, 0, 0, 0 }, true } // null termininator of list }; // STOP CHARACTER { 2 3 3 1 1 1 2 } int code128Index(QChar code, int set) { + const char latin1Code = code.toLatin1(); for (int idx = 0; _128codes[idx]._null == false; ++idx) { - if (set == SETA && _128codes[idx].codea == code.toAscii()) return idx; - if (set == SETB && _128codes[idx].codeb == code.toAscii()) return idx; - if (set == SETC && _128codes[idx].codec == code.toAscii()) return idx; + if (set == SETA && _128codes[idx].codea == latin1Code) return idx; + if (set == SETB && _128codes[idx].codeb == latin1Code) return idx; + if (set == SETC && _128codes[idx].codec == latin1Code) return idx; } return -1; // couldn't find it } void renderCode128(OROPage * page, const QRectF & r, const QString & _str, int align) { QVector str; int i = 0; // create the list.. if the list is empty then just set a start code and move on if (_str.isEmpty()) str.push_back(104); else { int rank_a = 0; int rank_b = 0; int rank_c = 0; QChar c; for (i = 0; i < _str.length(); ++i) { c = _str.at(i); rank_a += (code128Index(c, SETA) != -1 ? 1 : 0); rank_b += (code128Index(c, SETB) != -1 ? 1 : 0); rank_c += (c >= '0' && c <= '9' ? 1 : 0); } if (rank_c == _str.length() && ((rank_c % 2) == 0 || rank_c > 4)) { // every value in the is a digit so we are going to go with mode C // and we have an even number or we have more than 4 values i = 0; if ((rank_c % 2) == 1) { str.push_back(104); // START B c = _str.at(0); str.push_back(code128Index(c, SETB)); str.push_back(99); // MODE C i = 1; } else str.push_back(105); // START C for (i = i; i < _str.length(); i += 2) { char a, b; c = _str.at(i); - a = c.toAscii(); + a = c.toLatin1(); a -= 48; c = _str.at(i + 1); - b = c.toAscii(); + b = c.toLatin1(); b -= 48; str.push_back(int((a * 10) + b)); } } else { // start in the mode that had the higher number of hits and then // just shift into the opposite mode as needed int set = (rank_a > rank_b ? SETA : SETB); str.push_back((rank_a > rank_b ? 103 : 104)); int v = -1; for (i = 0; i < _str.length(); ++i) { c = _str.at(i); v = code128Index(c, set); if (v == -1) { v = code128Index(c, (set == SETA ? SETB : SETA)); if (v != -1) { str.push_back(98); // SHIFT str.push_back(v); } } else str.push_back(v); } } } // calculate and append the checksum value to the list int checksum = str.at(0); for (i = 1; i < str.size(); ++i) checksum += (str.at(i) * i); checksum = checksum % 103; str.push_back(checksum); // lets determine some core attributes about this barcode qreal bar_width = 1; // the width of the base unit bar 1/100 inch // this is are mandatory minimum quiet zone qreal quiet_zone = bar_width * 10; if (quiet_zone < 0.1) quiet_zone = 0.1; // what kind of area do we have to work with qreal draw_width = r.width(); qreal draw_height = r.height(); // how long is the value we need to encode? int val_length = str.size() - 2; // we include start and checksum in are list so // subtract them out for our calculations // L = (11C + 35)X // L length of barcode (excluding quite zone) in units same as X and I // C the number of characters in the value excluding the start/stop and checksum characters // X the width of a bar (pixels in our case) qreal L; qreal C = val_length; qreal X = bar_width; L = (((11.0 * C) + 35.0) * X); // now we have the actual width the barcode will be so can determine the actual // size of the quiet zone (we assume we center the barcode in the given area // what should we do if the area is too small???? // At the moment the way the code is written is we will always start at the minimum // required quiet zone if we don't have enough space.... I guess we'll just have over-run // to the right // // calculate the starting position based on the alignment option // for left align we don't need to do anything as the values are already setup for it if (align == 1) { // center qreal nqz = (draw_width - L) / 2.0; if (nqz > quiet_zone) quiet_zone = nqz; } else if (align > 1) // right quiet_zone = draw_width - (L + quiet_zone); // else if(align < 1) {} // left : do nothing qreal pos = r.left() + quiet_zone; qreal top = r.top(); QPen pen(Qt::NoPen); QBrush brush(QColor("black")); bool space = false; int idx = 0, b = 0; qreal w = 0.0; for (i = 0; i < str.size(); ++i) { // loop through each value and render the barcode idx = str.at(i); if (idx < 0 || idx > 105) { qDebug("Encountered a non-compliant element while rendering a 3of9 barcode -- skipping"); continue; } space = false; for (b = 0; b < 6; ++b, space = !space) { w = _128codes[idx].values[b] * bar_width; if (!space) { ORORect * rect = new ORORect(); rect->setPen(pen); rect->setBrush(brush); rect->setRect(QRectF(pos, top, w, draw_height)); page->addPrimitive(rect); } pos += w; } } // we have to do the stop character separately like this because it has // 7 elements in it's bar sequence rather than 6 like the others int STOP_CHARACTER[] = { 2, 3, 3, 1, 1, 1, 2 }; space = false; for (b = 0; b < 7; ++b, space = !space) { w = STOP_CHARACTER[b] * bar_width; if (!space) { ORORect * rect = new ORORect(); rect->setPen(pen); rect->setBrush(brush); rect->setRect(QRectF(pos, top, w, draw_height)); page->addPrimitive(rect); } pos += w; } } diff --git a/plugins/reporting/barcode/code128paint.cpp b/plugins/reporting/barcode/code128paint.cpp index 6ae01fed92f..8eb4f32850e 100644 --- a/plugins/reporting/barcode/code128paint.cpp +++ b/plugins/reporting/barcode/code128paint.cpp @@ -1,350 +1,351 @@ /* * OpenRPT report writer and rendering engine * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com) * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * This file contains the implementation of the Code 128 barcode renderer. * All this code assumes a 100dpi rendering surface for it's calculations. */ #include #include #include #include #include #include #include "renderobjects.h" static const int SETA = 0; static const int SETB = 1; static const int SETC = 2; static const char FNC1 = (char)130; static const char FNC2 = (char)131; static const char FNC3 = (char)132; static const char FNC4 = (char)133; static const char SHIFT = (char)134; static const char CODEA = (char)135; static const char CODEB = (char)136; static const char CODEC = (char)137; static const char STARTA = (char)138; static const char STARTB = (char)139; static const char STARTC = (char)140; struct code128 { char codea; char codeb; char codec; int values[6]; bool _null; }; static const struct code128 _128codes[] = { // A , B , C , { B S B S B S }, NULL? }, { ' ', ' ', 0, { 2, 1, 2, 2, 2, 2 }, false }, { '!', '!', 1, { 2, 2, 2, 1, 2, 2 }, false }, { '"', '"', 2, { 2, 2, 2, 2, 2, 1 }, false }, { '#', '#', 3, { 1, 2, 1, 2, 2, 3 }, false }, { '$', '$', 4, { 1, 2, 1, 3, 2, 2 }, false }, { '%', '%', 5, { 1, 3, 1, 2, 2, 2 }, false }, { '&', '&', 6, { 1, 2, 2, 2, 1, 3 }, false }, { '\'', '\'', 7, { 1, 2, 2, 3, 1, 2 }, false }, { '(', '(', 8, { 1, 3, 2, 2, 1, 2 }, false }, { ')', ')', 9, { 2, 2, 1, 2, 1, 3 }, false }, { '*', '*', 10, { 2, 2, 1, 3, 1, 2 }, false }, { '+', '+', 11, { 2, 3, 1, 2, 1, 2 }, false }, { ',', ',', 12, { 1, 1, 2, 2, 3, 2 }, false }, { '-', '-', 13, { 1, 2, 2, 1, 3, 2 }, false }, { '.', '.', 14, { 1, 2, 2, 2, 3, 1 }, false }, { '/', '/', 15, { 1, 1, 3, 2, 2, 2 }, false }, { '0', '0', 16, { 1, 2, 3, 1, 2, 2 }, false }, { '1', '1', 17, { 1, 2, 3, 2, 2, 1 }, false }, { '2', '2', 18, { 2, 2, 3, 2, 1, 1 }, false }, { '3', '3', 19, { 2, 2, 1, 1, 3, 2 }, false }, { '4', '4', 20, { 2, 2, 1, 2, 3, 1 }, false }, { '5', '5', 21, { 2, 1, 3, 2, 1, 2 }, false }, { '6', '6', 22, { 2, 2, 3, 1, 1, 2 }, false }, { '7', '7', 23, { 3, 1, 2, 1, 3, 1 }, false }, { '8', '8', 24, { 3, 1, 1, 2, 2, 2 }, false }, { '9', '9', 25, { 3, 2, 1, 1, 2, 2 }, false }, { ':', ':', 26, { 3, 2, 1, 2, 2, 1 }, false }, { ';', ';', 27, { 3, 1, 2, 2, 1, 2 }, false }, { '<', '<', 28, { 3, 2, 2, 1, 1, 2 }, false }, { '=', '=', 29, { 3, 2, 2, 2, 1, 1 }, false }, { '>', '>', 30, { 2, 1, 2, 1, 2, 3 }, false }, { '?', '?', 31, { 2, 1, 2, 3, 2, 1 }, false }, { '@', '@', 32, { 2, 3, 2, 1, 2, 1 }, false }, { 'A', 'A', 33, { 1, 1, 1, 3, 2, 3 }, false }, { 'B', 'B', 34, { 1, 3, 1, 1, 2, 3 }, false }, { 'C', 'C', 35, { 1, 3, 1, 3, 2, 1 }, false }, { 'D', 'D', 36, { 1, 1, 2, 3, 1, 3 }, false }, { 'E', 'E', 37, { 1, 3, 2, 1, 1, 3 }, false }, { 'F', 'F', 38, { 1, 3, 2, 3, 1, 1 }, false }, { 'G', 'G', 39, { 2, 1, 1, 3, 1, 3 }, false }, { 'H', 'H', 40, { 2, 3, 1, 1, 1, 3 }, false }, { 'I', 'I', 41, { 2, 3, 1, 3, 1, 1 }, false }, { 'J', 'J', 42, { 1, 1, 2, 1, 3, 3 }, false }, { 'K', 'K', 43, { 1, 1, 2, 3, 3, 1 }, false }, { 'L', 'L', 44, { 1, 3, 2, 1, 3, 1 }, false }, { 'M', 'M', 45, { 1, 1, 3, 1, 2, 3 }, false }, { 'N', 'N', 46, { 1, 1, 3, 3, 2, 1 }, false }, { 'O', 'O', 47, { 1, 3, 3, 1, 2, 1 }, false }, { 'P', 'P', 48, { 3, 1, 3, 1, 2, 1 }, false }, { 'Q', 'Q', 49, { 2, 1, 1, 3, 3, 1 }, false }, { 'R', 'R', 50, { 2, 3, 1, 1, 3, 1 }, false }, { 'S', 'S', 51, { 2, 1, 3, 1, 1, 3 }, false }, { 'T', 'T', 52, { 2, 1, 3, 3, 1, 1 }, false }, { 'U', 'U', 53, { 2, 1, 3, 1, 3, 1 }, false }, { 'V', 'V', 54, { 3, 1, 1, 1, 2, 3 }, false }, { 'W', 'W', 55, { 3, 1, 1, 3, 2, 1 }, false }, { 'X', 'X', 56, { 3, 3, 1, 1, 2, 1 }, false }, { 'Y', 'Y', 57, { 3, 1, 2, 1, 1, 3 }, false }, { 'Z', 'Z', 58, { 3, 1, 2, 3, 1, 1 }, false }, { '[', '[', 59, { 3, 3, 2, 1, 1, 1 }, false }, { '\\', '\\', 60, { 3, 1, 4, 1, 1, 1 }, false }, { ']', ']', 61, { 2, 2, 1, 4, 1, 1 }, false }, { '^', '^', 62, { 4, 3, 1, 1, 1, 1 }, false }, { '_', '_', 63, { 1, 1, 1, 2, 2, 4 }, false }, { 0x00, '`', 64, { 1, 1, 1, 4, 2, 2 }, false }, // NUL { 0x01, 'a', 65, { 1, 2, 1, 1, 2, 4 }, false }, // SOH { 0x02, 'b', 66, { 1, 2, 1, 4, 2, 1 }, false }, // STX { 0x03, 'c', 67, { 1, 4, 1, 1, 2, 2 }, false }, // ETX { 0x04, 'd', 68, { 1, 4, 1, 2, 2, 1 }, false }, // EOT { 0x05, 'e', 69, { 1, 1, 2, 2, 1, 4 }, false }, // ENQ { 0x06, 'f', 70, { 1, 1, 2, 4, 1, 2 }, false }, // ACK { 0x07, 'g', 71, { 1, 2, 2, 1, 1, 4 }, false }, // BEL { 0x08, 'h', 72, { 1, 2, 2, 4, 1, 1 }, false }, // BS { 0x09, 'i', 73, { 1, 4, 2, 1, 1, 2 }, false }, // HT { 0x0A, 'j', 74, { 1, 4, 2, 2, 1, 1 }, false }, // LF { 0x0B, 'k', 75, { 2, 4, 1, 2, 1, 1 }, false }, // VT { 0x0C, 'l', 76, { 2, 2, 1, 1, 1, 4 }, false }, // FF { 0x0D, 'm', 77, { 4, 1, 3, 1, 1, 1 }, false }, // CR { 0x0E, 'n', 78, { 2, 4, 1, 1, 1, 2 }, false }, // SO { 0x0F, 'o', 79, { 1, 3, 4, 1, 1, 1 }, false }, // SI { 0x10, 'p', 80, { 1, 1, 1, 2, 4, 2 }, false }, // DLE { 0x11, 'q', 81, { 1, 2, 1, 1, 4, 2 }, false }, // DC1 { 0x12, 'r', 82, { 1, 2, 1, 2, 4, 1 }, false }, // DC2 { 0x13, 's', 83, { 1, 1, 4, 2, 1, 2 }, false }, // DC3 { 0x14, 't', 84, { 1, 2, 4, 1, 1, 2 }, false }, // DC4 { 0x15, 'u', 85, { 1, 2, 4, 2, 1, 1 }, false }, // NAK { 0x16, 'v', 86, { 4, 1, 1, 2, 1, 2 }, false }, // SYN { 0x17, 'w', 87, { 4, 2, 1, 1, 1, 2 }, false }, // ETB { 0x18, 'x', 88, { 4, 2, 1, 2, 1, 1 }, false }, // CAN { 0x19, 'y', 89, { 2, 1, 2, 1, 4, 1 }, false }, // EM { 0x1A, 'z', 90, { 2, 1, 4, 1, 2, 1 }, false }, // SUB { 0x1B, '{', 91, { 4, 1, 2, 1, 2, 1 }, false }, // ESC { 0x1C, '|', 92, { 1, 1, 1, 1, 4, 3 }, false }, // FS { 0x1D, '}', 93, { 1, 1, 1, 3, 4, 1 }, false }, // GS { 0x1E, '~', 94, { 1, 3, 1, 1, 4, 1 }, false }, // RS { 0x1F, 0x7F, 95, { 1, 1, 4, 1, 1, 3 }, false }, // US DEL { FNC3, FNC3, 96, { 1, 1, 4, 3, 1, 1 }, false }, // FNC3 FNC3 { FNC2, FNC2, 97, { 4, 1, 1, 1, 1, 3 }, false }, // FNC2 FNC2 { SHIFT, SHIFT, 98, { 4, 1, 1, 3, 1, 1 }, false }, // SHIFT SHIFT { CODEC, CODEC, 99, { 1, 1, 3, 1, 4, 1 }, false }, // CODEC CODEC { CODEB, FNC4, CODEB, { 1, 1, 4, 1, 3, 1 }, false }, // CODEB FNC4 CODEB { FNC4, CODEA, CODEA, { 3, 1, 1, 1, 4, 1 }, false }, // FNC4 CODEA CODEA { FNC1, FNC1, FNC1, { 4, 1, 1, 1, 3, 1 }, false }, // FNC1 FNC1 FNC1 { STARTA, STARTA, STARTA, { 2, 1, 1, 4, 1, 2 }, false }, // STARTA { STARTB, STARTB, STARTB, { 2, 1, 1, 2, 1, 4 }, false }, // STARTB { STARTC, STARTC, STARTC, { 2, 1, 1, 2, 3, 2 }, false }, // STARTC { '\0', '\0', '\0', { 0, 0, 0, 0, 0, 0 }, true } // null termininator of list }; // STOP CHARACTER { 2 3 3 1 1 1 2 } int code128IndexP(QChar code, int set) { + const char latin1Code = code.toLatin1(); for (int idx = 0; _128codes[idx]._null == false; idx++) { - if (set == SETA && _128codes[idx].codea == code.toAscii()) return idx; - if (set == SETB && _128codes[idx].codeb == code.toAscii()) return idx; - if (set == SETC && _128codes[idx].codec == code.toAscii()) return idx; + if (set == SETA && _128codes[idx].codea == latin1Code) return idx; + if (set == SETB && _128codes[idx].codeb == latin1Code) return idx; + if (set == SETC && _128codes[idx].codec == latin1Code) return idx; } return -1; // couldn't find it } void renderCode128(const QRect & r, const QString & _str, int align, QPainter * pPainter) { QVector str; int i = 0; // create the list.. if the list is empty then just set a start code and move on if (_str.isEmpty()) { str.push_back(104); } else { int rank_a = 0; int rank_b = 0; int rank_c = 0; QChar c; for (i = 0; i < _str.length(); ++i) { c = _str.at(i); rank_a += (code128IndexP(c, SETA) != -1 ? 1 : 0); rank_b += (code128IndexP(c, SETB) != -1 ? 1 : 0); rank_c += (c >= '0' && c <= '9' ? 1 : 0); } if (rank_c == _str.length() && ((rank_c % 2) == 0 || rank_c > 4)) { // every value in the is a digit so we are going to go with mode C // and we have an even number or we have more than 4 values i = 0; if ((rank_c % 2) == 1) { str.push_back(104); // START B c = _str.at(0); str.push_back(code128IndexP(c, SETB)); str.push_back(99); // MODE C i = 1; } else { str.push_back(105); // START C } for (i = i; i < _str.length(); i += 2) { char a, b; c = _str.at(i); - a = c.toAscii(); + a = c.toLatin1(); a -= 48; c = _str.at(i + 1); - b = c.toAscii(); + b = c.toLatin1(); b -= 48; str.push_back(int((a * 10) + b)); } } else { // start in the mode that had the higher number of hits and then // just shift into the opposite mode as needed int set = (rank_a > rank_b ? SETA : SETB); str.push_back((rank_a > rank_b ? 103 : 104)); int v = -1; for (i = 0; i < _str.length(); ++i) { c = _str.at(i); v = code128IndexP(c, set); if (v == -1) { v = code128IndexP(c, (set == SETA ? SETB : SETA)); if (v != -1) { str.push_back(98); // SHIFT str.push_back(v); } } else { str.push_back(v); } } } } // calculate and append the checksum value to the list int checksum = str.at(0); for (i = 1; i < str.size(); ++i) { checksum += (str.at(i) * i); } checksum = checksum % 103; str.push_back(checksum); // lets determine some core attributes about this barcode int bar_width = 1; // the width of the base unit bar // this is are mandatory minimum quiet zone int quiet_zone = bar_width * 10; if (quiet_zone < 10) quiet_zone = 10; // what kind of area do we have to work with int draw_width = r.width(); int draw_height = r.height(); // how long is the value we need to encode? int val_length = str.size() - 2; // we include start and checksum in are list so // subtract them out for our calculations // L = (11C + 35)X // L length of barcode (excluding quite zone) in units same as X and I // C the number of characters in the value excluding the start/stop and checksum characters // X the width of a bar (pixels in our case) int L; int C = val_length; int X = bar_width; L = (((11 * C) + 35) * X); // now we have the actual width the barcode will be so can determine the actual // size of the quiet zone (we assume we center the barcode in the given area // what should we do if the area is too small???? // At the moment the way the code is written is we will always start at the minimum // required quiet zone if we don't have enough space.... I guess we'll just have over-run // to the right // // calculate the starting position based on the alignment option // for left align we don't need to do anything as the values are already setup for it if (align == 1) { // center int nqz = (draw_width - L) / 2; if (nqz > quiet_zone) quiet_zone = nqz; } else if (align > 1) { // right quiet_zone = draw_width - (L + quiet_zone); } // else if(align < 1) {} // left : do nothing int pos = r.left() + quiet_zone; int top = r.top(); if (pPainter) { pPainter->save(); QPen oneWide(pPainter->pen()); oneWide.setWidth(1); #ifndef Q_WS_WIN32 oneWide.setJoinStyle(Qt::MiterJoin); #endif pPainter->setPen(oneWide); pPainter->setBrush(pPainter->pen().color()); } bool space = false; int idx = 0, b = 0, w = 0; for (i = 0; i < str.size(); ++i) { // loop through each value and render the barcode idx = str.at(i); if (idx < 0 || idx > 105) { qDebug("Encountered a non-compliant element while rendering a 3of9 barcode -- skipping"); continue; } space = false; for (b = 0; b < 6; b++, space = !space) { w = _128codes[idx].values[b] * bar_width; if (!space && pPainter) { pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color()); } pos += w; } } // we have to do the stop character separately like this because it has // 7 elements in it's bar sequence rather than 6 like the others int STOP_CHARACTER[] = { 2, 3, 3, 1, 1, 1, 2 }; space = false; for (b = 0; b < 7; b++, space = !space) { w = STOP_CHARACTER[b] * bar_width; if (!space && pPainter) { pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color()); } pos += w; } if (pPainter) { pPainter->restore(); } } diff --git a/plugins/reporting/barcode/ext3of9.cpp b/plugins/reporting/barcode/ext3of9.cpp index b6120956f6f..24402b50198 100644 --- a/plugins/reporting/barcode/ext3of9.cpp +++ b/plugins/reporting/barcode/ext3of9.cpp @@ -1,194 +1,195 @@ /* * OpenRPT report writer and rendering engine * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com) * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * This file contains the code that will render the extended 3of9 barcode. * This code will parse a string and build a compliant 3of9 string and then * call the 3of9 renderer to do the actual drawing. */ #include #include #include #include "barcodes.h" class _ext3of9map { public: _ext3of9map(const char c, const QString & s) { code = c; conversion = s; } char code; QString conversion; }; const _ext3of9map ext3of9map[] = { _ext3of9map('\0' , "%U"), // NUL _ext3of9map('\001' , "$A"), // SOH _ext3of9map('\002' , "$B"), // STX _ext3of9map('\003' , "$C"), // ETX _ext3of9map('\004' , "$D"), // EOT _ext3of9map('\005' , "$E"), // ENQ _ext3of9map('\006' , "$F"), // ACK _ext3of9map('\007' , "$G"), // BEL _ext3of9map('\010' , "$H"), // BS _ext3of9map('\011' , "$I"), // HT _ext3of9map('\012' , "$J"), // LF _ext3of9map('\013' , "$K"), // VT _ext3of9map('\014' , "$L"), // FF _ext3of9map('\015' , "$M"), // CR _ext3of9map('\016' , "$N"), // SO _ext3of9map('\017' , "$O"), // SI _ext3of9map('\020' , "$P"), // DLE _ext3of9map('\021' , "$Q"), // DC1 _ext3of9map('\022' , "$R"), // DC2 _ext3of9map('\023' , "$S"), // DC3 _ext3of9map('\024' , "$T"), // DC4 _ext3of9map('\025' , "$U"), // NAK _ext3of9map('\026' , "$V"), // SYN _ext3of9map('\027' , "$W"), // ETB _ext3of9map('\030' , "$X"), // CAN _ext3of9map('\031' , "$Y"), // EM _ext3of9map('\032' , "$Z"), // SUB _ext3of9map('\033' , "%A"), // ESC _ext3of9map('\034' , "%B"), // FS _ext3of9map('\035' , "%C"), // GS _ext3of9map('\036' , "%D"), // RS _ext3of9map('\037' , "%E"), // US _ext3of9map(' ' , " "), // SPACE _ext3of9map('!' , "/A"), _ext3of9map('"' , "/B"), _ext3of9map('#' , "/C"), _ext3of9map('$' , "/D"), _ext3of9map('%' , "/E"), _ext3of9map('&' , "/F"), _ext3of9map('\'' , "/G"), _ext3of9map('(' , "/H"), _ext3of9map(')' , "/I"), _ext3of9map('*' , "/J"), _ext3of9map('+' , "/K"), _ext3of9map(',' , "/L"), _ext3of9map('-' , "-"), // /M _ext3of9map('.' , "."), // /N _ext3of9map('/' , "/O"), _ext3of9map('0' , "0"), // /P _ext3of9map('1' , "1"), // /Q _ext3of9map('2' , "2"), // /R _ext3of9map('3' , "3"), // /S _ext3of9map('4' , "4"), // /T _ext3of9map('5' , "5"), // /U _ext3of9map('6' , "6"), // /V _ext3of9map('7' , "7"), // /W _ext3of9map('8' , "8"), // /X _ext3of9map('9' , "9"), // /Y _ext3of9map(':' , "/Z"), _ext3of9map(';' , "%F"), _ext3of9map('<' , "%G"), _ext3of9map('=' , "%H"), _ext3of9map('>' , "%I"), _ext3of9map('?' , "%J"), _ext3of9map('@' , "%V"), _ext3of9map('A' , "A"), _ext3of9map('B' , "B"), _ext3of9map('C' , "C"), _ext3of9map('D' , "D"), _ext3of9map('E' , "E"), _ext3of9map('F' , "F"), _ext3of9map('G' , "G"), _ext3of9map('H' , "H"), _ext3of9map('I' , "I"), _ext3of9map('J' , "J"), _ext3of9map('K' , "K"), _ext3of9map('L' , "L"), _ext3of9map('M' , "M"), _ext3of9map('N' , "N"), _ext3of9map('O' , "O"), _ext3of9map('P' , "P"), _ext3of9map('Q' , "Q"), _ext3of9map('R' , "R"), _ext3of9map('S' , "S"), _ext3of9map('T' , "T"), _ext3of9map('U' , "U"), _ext3of9map('V' , "V"), _ext3of9map('W' , "W"), _ext3of9map('X' , "X"), _ext3of9map('Y' , "Y"), _ext3of9map('Z' , "Z"), _ext3of9map('[' , "%K"), _ext3of9map('\\' , "%L"), _ext3of9map(']' , "%M"), _ext3of9map('^' , "%N"), _ext3of9map('_' , "%O"), _ext3of9map('`' , "%W"), _ext3of9map('a' , "+A"), _ext3of9map('b' , "+B"), _ext3of9map('c' , "+C"), _ext3of9map('d' , "+D"), _ext3of9map('e' , "+E"), _ext3of9map('f' , "+F"), _ext3of9map('g' , "+G"), _ext3of9map('h' , "+H"), _ext3of9map('i' , "+I"), _ext3of9map('j' , "+J"), _ext3of9map('k' , "+K"), _ext3of9map('l' , "+L"), _ext3of9map('m' , "+M"), _ext3of9map('n' , "+N"), _ext3of9map('o' , "+O"), _ext3of9map('p' , "+P"), _ext3of9map('q' , "+Q"), _ext3of9map('r' , "+R"), _ext3of9map('s' , "+S"), _ext3of9map('t' , "+T"), _ext3of9map('u' , "+U"), _ext3of9map('v' , "+V"), _ext3of9map('w' , "+W"), _ext3of9map('x' , "+X"), _ext3of9map('y' , "+Y"), _ext3of9map('z' , "+Z"), _ext3of9map('{' , "%P"), _ext3of9map('|' , "%Q"), _ext3of9map('}' , "%R"), _ext3of9map('~' , "%S"), _ext3of9map('\177' , "%T"), // DEL _ext3of9map(-1 , 0) }; QString convertTo3of9(QChar c) { + const char code = c.toLatin1(); for (int i = 0; !ext3of9map[i].conversion.isEmpty(); i++) - if (ext3of9map[i].code == c.toAscii()) + if (ext3of9map[i].code == code) return ext3of9map[i].conversion; return QString(); } void renderExtended3of9(OROPage * page, const QRectF & r, const QString & str, int align) { QString new_str; QChar c; for (int i = 0; i < str.length(); i++) { c = str.at(i); new_str += convertTo3of9(c); } render3of9(page, r, new_str, align); } diff --git a/plugins/reporting/barcode/ext3of9paint.cpp b/plugins/reporting/barcode/ext3of9paint.cpp index 2d3b3f03e95..9a30de99627 100644 --- a/plugins/reporting/barcode/ext3of9paint.cpp +++ b/plugins/reporting/barcode/ext3of9paint.cpp @@ -1,195 +1,196 @@ /* * OpenRPT report writer and rendering engine * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com) * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * This file contains the code that will render the extended 3of9 barcode. * This code will parse a string and build a compliant 3of9 string and then * call the 3of9 renderer to do the actual drawing. */ #include #include #include #include "barcodepaint.h" class _ext3of9map { public: _ext3of9map(const char c, const QString & s) { code = c; conversion = s; } char code; QString conversion; }; const _ext3of9map ext3of9map[] = { _ext3of9map('\0' , "%U"), // NUL _ext3of9map('\001' , "$A"), // SOH _ext3of9map('\002' , "$B"), // STX _ext3of9map('\003' , "$C"), // ETX _ext3of9map('\004' , "$D"), // EOT _ext3of9map('\005' , "$E"), // ENQ _ext3of9map('\006' , "$F"), // ACK _ext3of9map('\007' , "$G"), // BEL _ext3of9map('\010' , "$H"), // BS _ext3of9map('\011' , "$I"), // HT _ext3of9map('\012' , "$J"), // LF _ext3of9map('\013' , "$K"), // VT _ext3of9map('\014' , "$L"), // FF _ext3of9map('\015' , "$M"), // CR _ext3of9map('\016' , "$N"), // SO _ext3of9map('\017' , "$O"), // SI _ext3of9map('\020' , "$P"), // DLE _ext3of9map('\021' , "$Q"), // DC1 _ext3of9map('\022' , "$R"), // DC2 _ext3of9map('\023' , "$S"), // DC3 _ext3of9map('\024' , "$T"), // DC4 _ext3of9map('\025' , "$U"), // NAK _ext3of9map('\026' , "$V"), // SYN _ext3of9map('\027' , "$W"), // ETB _ext3of9map('\030' , "$X"), // CAN _ext3of9map('\031' , "$Y"), // EM _ext3of9map('\032' , "$Z"), // SUB _ext3of9map('\033' , "%A"), // ESC _ext3of9map('\034' , "%B"), // FS _ext3of9map('\035' , "%C"), // GS _ext3of9map('\036' , "%D"), // RS _ext3of9map('\037' , "%E"), // US _ext3of9map(' ' , " "), // SPACE _ext3of9map('!' , "/A"), _ext3of9map('"' , "/B"), _ext3of9map('#' , "/C"), _ext3of9map('$' , "/D"), _ext3of9map('%' , "/E"), _ext3of9map('&' , "/F"), _ext3of9map('\'' , "/G"), _ext3of9map('(' , "/H"), _ext3of9map(')' , "/I"), _ext3of9map('*' , "/J"), _ext3of9map('+' , "/K"), _ext3of9map(',' , "/L"), _ext3of9map('-' , "-"), // /M _ext3of9map('.' , "."), // /N _ext3of9map('/' , "/O"), _ext3of9map('0' , "0"), // /P _ext3of9map('1' , "1"), // /Q _ext3of9map('2' , "2"), // /R _ext3of9map('3' , "3"), // /S _ext3of9map('4' , "4"), // /T _ext3of9map('5' , "5"), // /U _ext3of9map('6' , "6"), // /V _ext3of9map('7' , "7"), // /W _ext3of9map('8' , "8"), // /X _ext3of9map('9' , "9"), // /Y _ext3of9map(':' , "/Z"), _ext3of9map(';' , "%F"), _ext3of9map('<' , "%G"), _ext3of9map('=' , "%H"), _ext3of9map('>' , "%I"), _ext3of9map('?' , "%J"), _ext3of9map('@' , "%V"), _ext3of9map('A' , "A"), _ext3of9map('B' , "B"), _ext3of9map('C' , "C"), _ext3of9map('D' , "D"), _ext3of9map('E' , "E"), _ext3of9map('F' , "F"), _ext3of9map('G' , "G"), _ext3of9map('H' , "H"), _ext3of9map('I' , "I"), _ext3of9map('J' , "J"), _ext3of9map('K' , "K"), _ext3of9map('L' , "L"), _ext3of9map('M' , "M"), _ext3of9map('N' , "N"), _ext3of9map('O' , "O"), _ext3of9map('P' , "P"), _ext3of9map('Q' , "Q"), _ext3of9map('R' , "R"), _ext3of9map('S' , "S"), _ext3of9map('T' , "T"), _ext3of9map('U' , "U"), _ext3of9map('V' , "V"), _ext3of9map('W' , "W"), _ext3of9map('X' , "X"), _ext3of9map('Y' , "Y"), _ext3of9map('Z' , "Z"), _ext3of9map('[' , "%K"), _ext3of9map('\\' , "%L"), _ext3of9map(']' , "%M"), _ext3of9map('^' , "%N"), _ext3of9map('_' , "%O"), _ext3of9map('`' , "%W"), _ext3of9map('a' , "+A"), _ext3of9map('b' , "+B"), _ext3of9map('c' , "+C"), _ext3of9map('d' , "+D"), _ext3of9map('e' , "+E"), _ext3of9map('f' , "+F"), _ext3of9map('g' , "+G"), _ext3of9map('h' , "+H"), _ext3of9map('i' , "+I"), _ext3of9map('j' , "+J"), _ext3of9map('k' , "+K"), _ext3of9map('l' , "+L"), _ext3of9map('m' , "+M"), _ext3of9map('n' , "+N"), _ext3of9map('o' , "+O"), _ext3of9map('p' , "+P"), _ext3of9map('q' , "+Q"), _ext3of9map('r' , "+R"), _ext3of9map('s' , "+S"), _ext3of9map('t' , "+T"), _ext3of9map('u' , "+U"), _ext3of9map('v' , "+V"), _ext3of9map('w' , "+W"), _ext3of9map('x' , "+X"), _ext3of9map('y' , "+Y"), _ext3of9map('z' , "+Z"), _ext3of9map('{' , "%P"), _ext3of9map('|' , "%Q"), _ext3of9map('}' , "%R"), _ext3of9map('~' , "%S"), _ext3of9map('\177' , "%T"), // DEL _ext3of9map(-1 , 0) }; QString convertTo3of9P(QChar c) { + const char code = c.toLatin1(); for (int i = 0; !ext3of9map[i].conversion.isEmpty(); i++) - if (ext3of9map[i].code == c.toAscii()) + if (ext3of9map[i].code == code) return ext3of9map[i].conversion; return QString(); } void renderExtended3of9(const QRect & r, const QString & str, int align, QPainter * pPainter) { QString new_str; QChar c; for (int i = 0; i < str.length(); i++) { c = str.at(i); new_str += convertTo3of9P(c); } render3of9(r, new_str, align, pPainter); } diff --git a/sheets/Global.h b/sheets/Global.h index 1759095c147..ac69b06e59f 100644 --- a/sheets/Global.h +++ b/sheets/Global.h @@ -1,125 +1,125 @@ /* This file is part of the KDE project Copyright (C) 2005-2006 Stefan Nikolaus (C) 2006 Fredrik Edemar (C) 2005-2006 Raphael Langerhorst (C) 2004 Tomas Mecir (C) 2003 Norbert Andres (C) 2002 Philipp Mueller (C) 2000 David Faure (C) 2000 Werner Trobin (C) 2000-2006 Laurent Montel (C) 1999, 2000 Torben Weis (C) 1999 Stephan Kulow 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. */ #ifndef KSPREAD_GLOBAL_H #define KSPREAD_GLOBAL_H #include #include namespace Calligra { namespace Sheets { class ElapsedTime { public: enum OutputMode { Default, PrintOnlyTime }; #ifdef NDEBUG ElapsedTime() {} explicit ElapsedTime(QString const& , OutputMode = Default) {} #else // NDEBUG ElapsedTime() { m_time.start(); } explicit ElapsedTime(QString const & name, OutputMode mode = Default) : m_name(name) { m_time.start(); if (mode != PrintOnlyTime) { - kDebug(36001) << QString("*** (" + name + ")... Starting measuring...").toLatin1().data(); + kDebug(36001) << QString("*** (" + name + ")... Starting measuring..."); } } ~ElapsedTime() { uint milliSec = m_time.elapsed(); uint min = (uint)(milliSec / (1000 * 60)); milliSec -= (min * 60 * 1000); uint sec = (uint)(milliSec / 1000); milliSec -= sec * 1000; if (m_name.isNull()) - kDebug(36001) << QString("*** Elapsed time: %1 min %2 sec %3 msec").arg(min).arg(sec).arg(milliSec).toLatin1().data(); + kDebug(36001) << QString("*** Elapsed time: %1 min %2 sec %3 msec").arg(min).arg(sec).arg(milliSec); else - kDebug(36001) << QString("*** (%1) Elapsed time: %2 min %3 sec %4 msec").arg(m_name).arg(min).arg(sec).arg(milliSec).toLatin1().data(); + kDebug(36001) << QString("*** (%1) Elapsed time: %2 min %3 sec %4 msec").arg(m_name).arg(min).arg(sec).arg(milliSec); } private: QTime m_time; QString m_name; #endif // NDEBUG }; /** * This namespace collects enumerations related to * pasting operations. */ namespace Paste { /** * The pasted content */ enum Mode { Normal /** Everything */, Text /** Text only */, Format /** Format only */, NoBorder /** not the borders */, Comment /** Comment only */, Result /** Result only, no formula */, NormalAndTranspose /** */, TextAndTranspose /** */, FormatAndTranspose /** */, NoBorderAndTranspose /** */ }; /** * The current cell value treatment. */ enum Operation { OverWrite /** Overwrite */, Add /** Add */, Mul /** Multiply */, Sub /** Subtract */, Div /** Divide */ }; } // namespace Paste // necessary due to QDock* enums (Werner) enum MoveTo { Bottom, Left, Top, Right, BottomFirst, NoMovement }; enum MethodOfCalc { SumOfNumber, Min, Max, Average, Count, NoneCalc, CountA }; } // namespace Sheets } // namespace Calligra #endif