diff --git a/libs/flake/KoShapeSavingContext.cpp b/libs/flake/KoShapeSavingContext.cpp index fa8a8f4f3b..caa3122743 100644 --- a/libs/flake/KoShapeSavingContext.cpp +++ b/libs/flake/KoShapeSavingContext.cpp @@ -1,346 +1,346 @@ /* This file is part of the KDE project Copyright (C) 2004-2006 David Faure Copyright (C) 2007-2009, 2011 Thorsten Zachmann Copyright (C) 2007 Jan Hambrecht 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 "KoShapeSavingContext.h" #include "KoDataCenterBase.h" #include "KoShapeLayer.h" #include "KoImageData.h" #include "KoMarker.h" #include #include #include #include #include #include #include #include #include class KoShapeSavingContextPrivate { public: KoShapeSavingContextPrivate(KoXmlWriter&, KoGenStyles&, KoEmbeddedDocumentSaver&); ~KoShapeSavingContextPrivate(); KoXmlWriter *xmlWriter; KoShapeSavingContext::ShapeSavingOptions savingOptions; QList layers; QSet dataCenters; QMap sharedData; QMap imageNames; int imageId; QMap images; QHash shapeOffsets; QMap markerRefs; KoGenStyles& mainStyles; KoEmbeddedDocumentSaver& embeddedSaver; QMap references; QMap referenceCounters; QMap > prefixedReferences; }; KoShapeSavingContextPrivate::KoShapeSavingContextPrivate(KoXmlWriter &w, KoGenStyles &s, KoEmbeddedDocumentSaver &e) : xmlWriter(&w), savingOptions(0), imageId(0), mainStyles(s), embeddedSaver(e) { } KoShapeSavingContextPrivate::~KoShapeSavingContextPrivate() { Q_FOREACH (KoSharedSavingData * data, sharedData) { delete data; } } KoShapeSavingContext::KoShapeSavingContext(KoXmlWriter &xmlWriter, KoGenStyles &mainStyles, KoEmbeddedDocumentSaver &embeddedSaver) : d(new KoShapeSavingContextPrivate(xmlWriter, mainStyles, embeddedSaver)) { // by default allow saving of draw:id + xml:id addOption(KoShapeSavingContext::DrawId); } KoShapeSavingContext::~KoShapeSavingContext() { delete d; } KoXmlWriter & KoShapeSavingContext::xmlWriter() { return *d->xmlWriter; } void KoShapeSavingContext::setXmlWriter(KoXmlWriter &xmlWriter) { d->xmlWriter = &xmlWriter; } KoGenStyles & KoShapeSavingContext::mainStyles() { return d->mainStyles; } KoEmbeddedDocumentSaver &KoShapeSavingContext::embeddedSaver() { return d->embeddedSaver; } bool KoShapeSavingContext::isSet(ShapeSavingOption option) const { return d->savingOptions & option; } void KoShapeSavingContext::setOptions(ShapeSavingOptions options) { d->savingOptions = options; } KoShapeSavingContext::ShapeSavingOptions KoShapeSavingContext::options() const { return d->savingOptions; } void KoShapeSavingContext::addOption(ShapeSavingOption option) { d->savingOptions = d->savingOptions | option; } void KoShapeSavingContext::removeOption(ShapeSavingOption option) { if (isSet(option)) d->savingOptions = d->savingOptions ^ option; // xor to remove it. } KoElementReference KoShapeSavingContext::xmlid(const void *referent, const QString& prefix, KoElementReference::GenerationOption counter) { Q_ASSERT(counter == KoElementReference::UUID || (counter == KoElementReference::Counter && !prefix.isEmpty())); if (d->references.contains(referent)) { return d->references[referent]; } KoElementReference ref; if (counter == KoElementReference::Counter) { int referenceCounter = d->referenceCounters[prefix]; referenceCounter++; ref = KoElementReference(prefix, referenceCounter); d->references.insert(referent, ref); d->referenceCounters[prefix] = referenceCounter; } else { if (!prefix.isEmpty()) { ref = KoElementReference(prefix); d->references.insert(referent, ref); } else { d->references.insert(referent, ref); } } if (!prefix.isNull()) { d->prefixedReferences[prefix].append(referent); } return ref; } KoElementReference KoShapeSavingContext::existingXmlid(const void *referent) { if (d->references.contains(referent)) { return d->references[referent]; } else { KoElementReference ref; ref.invalidate(); return ref; } } void KoShapeSavingContext::clearXmlIds(const QString &prefix) { if (d->prefixedReferences.contains(prefix)) { Q_FOREACH (const void* ptr, d->prefixedReferences[prefix]) { d->references.remove(ptr); } d->prefixedReferences.remove(prefix); } if (d->referenceCounters.contains(prefix)) { d->referenceCounters[prefix] = 0; } } void KoShapeSavingContext::addLayerForSaving(const KoShapeLayer *layer) { if (layer && ! d->layers.contains(layer)) d->layers.append(layer); } void KoShapeSavingContext::saveLayerSet(KoXmlWriter &xmlWriter) const { xmlWriter.startElement("draw:layer-set"); Q_FOREACH (const KoShapeLayer * layer, d->layers) { xmlWriter.startElement("draw:layer"); xmlWriter.addAttribute("draw:name", layer->name()); if (layer->isGeometryProtected()) xmlWriter.addAttribute("draw:protected", "true"); if (! layer->isVisible()) xmlWriter.addAttribute("draw:display", "none"); xmlWriter.endElement(); // draw:layer } xmlWriter.endElement(); // draw:layer-set } void KoShapeSavingContext::clearLayers() { d->layers.clear(); } QString KoShapeSavingContext::imageHref(const KoImageData *image) { QMap::iterator it(d->imageNames.find(image->key())); if (it == d->imageNames.end()) { QString suffix = image->suffix(); if (suffix.isEmpty()) { it = d->imageNames.insert(image->key(), QString("Pictures/image%1").arg(++d->imageId)); } else { it = d->imageNames.insert(image->key(), QString("Pictures/image%1.%2").arg(++d->imageId).arg(suffix)); } } return it.value(); } QString KoShapeSavingContext::imageHref(const QImage &image) { // TODO this can be optimized to recognize images which have the same content // Also this can use quite a lot of memeory as the qimage are all kept until // they are saved to the store in memory QString href = QString("Pictures/image%1.png").arg(++d->imageId); d->images.insert(href, image); return href; } QMap KoShapeSavingContext::imagesToSave() { return d->imageNames; } -QString KoShapeSavingContext::markerRef(const KoMarker *marker) +QString KoShapeSavingContext::markerRef(const KoMarker */*marker*/) { // QMap::iterator it = d->markerRefs.find(marker); // if (it == d->markerRefs.end()) { // it = d->markerRefs.insert(marker, marker->saveOdf(*this)); // } // return it.value(); return QString(); } void KoShapeSavingContext::addDataCenter(KoDataCenterBase * dataCenter) { if (dataCenter) { d->dataCenters.insert(dataCenter); } } bool KoShapeSavingContext::saveDataCenter(KoStore *store, KoXmlWriter* manifestWriter) { bool ok = true; Q_FOREACH (KoDataCenterBase *dataCenter, d->dataCenters) { ok = ok && dataCenter->completeSaving(store, manifestWriter, this); //debugFlake << "ok" << ok; } // Save images for (QMap::iterator it(d->images.begin()); it != d->images.end(); ++it) { if (store->open(it.key())) { KoStoreDevice device(store); ok = ok && it.value().save(&device, "PNG"); store->close(); // TODO error handling if (ok) { const QString mimetype = KisMimeDatabase::mimeTypeForFile(it.key()); manifestWriter->addManifestEntry(it.key(), mimetype); } else { warnFlake << "saving image failed"; } } else { ok = false; warnFlake << "saving image failed: open store failed"; } } return ok; } void KoShapeSavingContext::addSharedData(const QString &id, KoSharedSavingData * data) { QMap::iterator it(d->sharedData.find(id)); // data will not be overwritten if (it == d->sharedData.end()) { d->sharedData.insert(id, data); } else { warnFlake << "The id" << id << "is already registered. Data not inserted"; Q_ASSERT(it == d->sharedData.end()); } } KoSharedSavingData * KoShapeSavingContext::sharedData(const QString &id) const { KoSharedSavingData * data = 0; QMap::const_iterator it(d->sharedData.constFind(id)); if (it != d->sharedData.constEnd()) { data = it.value(); } return data; } void KoShapeSavingContext::addShapeOffset(const KoShape *shape, const QTransform &m) { d->shapeOffsets.insert(shape, m); } void KoShapeSavingContext::removeShapeOffset(const KoShape *shape) { d->shapeOffsets.remove(shape); } QTransform KoShapeSavingContext::shapeOffset(const KoShape *shape) const { return d->shapeOffsets.value(shape, QTransform()); } diff --git a/libs/flake/KoUnavailShape.cpp b/libs/flake/KoUnavailShape.cpp index 3e407cb220..1c85d0f7b2 100644 --- a/libs/flake/KoUnavailShape.cpp +++ b/libs/flake/KoUnavailShape.cpp @@ -1,657 +1,628 @@ /* 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 #include // Calligra #include #include #include #include #include #include #include #include #include "KoShapeLoadingContext.h" #include "KoShapeSavingContext.h" #include "SimpleShapeContainerModel.h" #include "KoShapeBackground.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(":/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 SimpleShapeContainerModel()) , 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. debugFlake << "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(":/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), 0)); 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 { debugFlake << "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; debugFlake << "Object name: " << objectName << "newName: " << newName << "filename: " << fileName << "isDir: " << entry->isDir; fileName.replace(objectName, newName); fileName.prepend("./"); debugFlake << "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) { debugFlake << "START LOADING ##################################################"; //debugFlake << "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. debugFlake << "MANIFEST: "; foreach (KoOdfManifestEntry *entry, manifest) { debugFlake << 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 debugFlake << "----------------------------------------------------------------"; debugFlake << "After storeObjects():"; foreach (ObjectEntry *object, d->objectEntries) { debugFlake << "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; debugFlake << "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) { debugFlake << "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()) { debugFlake << "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()) { debugFlake << "Found pixel based preview image!"; foundPreview = true; } } } #if 0 // Enable to get more detailed debug messages debugFlake << "Object manifest entries:"; for (int i = 0; i < d->manifestEntries.size(); ++i) { KoOdfManifestEntry *entry = d->manifestEntries.value(i); debugFlake << i << ":" << entry; if (entry) debugFlake << entry->fullPath() << entry->mediaType() << entry->version(); else debugFlake << "--"; } debugFlake << "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()) { debugFlake << "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(QLatin1String("./"))) name.remove(0, 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().toLatin1()); writer.startElement(name.constData()); -#ifndef KOXML_USE_QDOM - // 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.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.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); - } - QString name = QString("xmlns:") + nsShort; - writer.addAttribute(name.toLatin1(), attrPair.first.toLatin1()); - } - QString attr(nsShort + ':' + attrPair.second); - writer.addAttribute(attr.toLatin1(), el.attributeNS(attrPair.first, - attrPair.second)); - } - } -#endif // 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) { debugFlake << "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(QLatin1String("./"))) entry->path.remove(0, 2); entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path); entry->isDir = false; entry->contents = fileContent; embeddedFiles.append(entry); debugFlake << "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(); //debugFlake << "File content: " << fileContent; return fileContent; } diff --git a/libs/libkis/Document.cpp b/libs/libkis/Document.cpp index 568cf65632..9368f8c8e8 100644 --- a/libs/libkis/Document.cpp +++ b/libs/libkis/Document.cpp @@ -1,565 +1,565 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * 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 "Document.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct Document::Private { Private() {} QPointer document; }; Document::Document(KisDocument *document, QObject *parent) : QObject(parent) , d(new Private) { d->document = document; } Document::~Document() { delete d; } bool Document::operator==(const Document &other) const { return (d->document == other.d->document); } bool Document::operator!=(const Document &other) const { return !(operator==(other)); } bool Document::batchmode() const { if (!d->document) return false; return d->document->fileBatchMode(); } void Document::setBatchmode(bool value) { if (!d->document) return; d->document->setFileBatchMode(value); } Node *Document::activeNode() const { QList activeNodes; Q_FOREACH(QPointer view, KisPart::instance()->views()) { if (view && view->document() == d->document) { activeNodes << view->currentNode(); } } if (activeNodes.size() > 0) { return new Node(d->document->image(), activeNodes.first()); } return new Node(d->document->image(), d->document->image()->root()->firstChild()); } void Document::setActiveNode(Node* value) { if (!value->node()) return; KisMainWindow *mainWin = KisPart::instance()->currentMainwindow(); if (!mainWin) return; KisViewManager *viewManager = mainWin->viewManager(); if (!viewManager) return; if (viewManager->document() != d->document) return; KisNodeManager *nodeManager = viewManager->nodeManager(); if (!nodeManager) return; KisNodeSelectionAdapter *selectionAdapter = nodeManager->nodeSelectionAdapter(); if (!selectionAdapter) return; selectionAdapter->setActiveNode(value->node()); } QList Document::topLevelNodes() const { if (!d->document) return QList(); Node n(d->document->image(), d->document->image()->rootLayer()); return n.childNodes(); } Node *Document::nodeByName(const QString &name) const { if (!d->document) return 0; KisNodeSP node = d->document->image()->rootLayer()->findChildByName(name); return new Node(d->document->image(), node); } QString Document::colorDepth() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorDepthId().id(); } QString Document::colorModel() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorModelId().id(); } QString Document::colorProfile() const { if (!d->document) return ""; return d->document->image()->colorSpace()->profile()->name(); } bool Document::setColorProfile(const QString &value) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(value); if (!profile) return false; bool retval = d->document->image()->assignImageProfile(profile); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return retval; } bool Document::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, colorProfile); if (!colorSpace) return false; d->document->image()->convertImageColorSpace(colorSpace, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::HighQuality | KoColorConversionTransformation::NoOptimization); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return true; } QString Document::documentInfo() const { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = d->document->documentInfo()->save(doc); return doc.toString(); } void Document::setDocumentInfo(const QString &document) { - KoXmlDocument doc = KoXmlDocument(true); + KoXmlDocument doc; QString errorMsg; int errorLine, errorColumn; doc.setContent(document, &errorMsg, &errorLine, &errorColumn); d->document->documentInfo()->load(doc); } QString Document::fileName() const { if (!d->document) return QString::null; return d->document->url().toLocalFile(); } void Document::setFileName(QString value) { if (!d->document) return; d->document->setUrl(QUrl::fromLocalFile(value)); } int Document::height() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->height(); } void Document::setHeight(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->width(), value); } QString Document::name() const { if (!d->document) return ""; return d->document->documentInfo()->aboutInfo("title"); } void Document::setName(QString value) { if (!d->document) return; d->document->documentInfo()->setAboutInfo("title", value); } int Document::resolution() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return qRound(d->document->image()->xRes() * 72); } void Document::setResolution(int value) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; d->document->image()->setResolution(value / 72.0, value / 72.0); } Node *Document::rootNode() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return new Node(image, image->root()); } Selection *Document::selection() const { if (!d->document) return 0; if (!d->document->image()) return 0; if (!d->document->image()->globalSelection()) return 0; return new Selection(d->document->image()->globalSelection()); } void Document::setSelection(Selection* value) { if (!d->document) return; if (!d->document->image()) return; if (value) { d->document->image()->setGlobalSelection(value->selection()); } else { d->document->image()->setGlobalSelection(0); } } int Document::width() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->width(); } void Document::setWidth(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(value, d->document->image()->height()); } double Document::xRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->xRes(); } void Document::setXRes(double xRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(xRes, d->document->image()->yRes()); } double Document::yRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->yRes(); } void Document::setYRes(double yRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(d->document->image()->xRes(), yRes); } QByteArray Document::pixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->document) return ba; KisImageSP image = d->document->image(); if (!image) return ba; KisPaintDeviceSP dev = image->projection(); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } bool Document::close() { bool retval = d->document->closeUrl(false); Q_FOREACH(KisView *view, KisPart::instance()->views()) { if (view->document() == d->document) { view->close(); view->deleteLater(); } } d->document->deleteLater(); d->document = 0; return retval; } void Document::crop(int x, int y, int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc(x, y, w, h); image->cropImage(rc); } bool Document::exportImage(const QString &filename, const InfoObject &exportConfiguration) { if (!d->document) return false; QString mimeType = KisMimeDatabase::mimeTypeForFile(filename); d->document->setOutputMimeType(mimeType.toLatin1()); return d->document->exportDocument(QUrl::fromLocalFile(filename), exportConfiguration.configuration()); } void Document::flatten() { if (!d->document) return; if (!d->document->image()) return; d->document->image()->flatten(); } void Document::resizeImage(int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc = image->bounds(); rc.setWidth(w); rc.setHeight(h); image->resizeImage(rc); } void Document::scaleImage(int w, int h, int xres, int yres, QString strategy) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc = image->bounds(); rc.setWidth(w); rc.setHeight(h); - KisFilterStrategy *actualStrategy; + KisFilterStrategy *actualStrategy = 0; if (strategy == "hermite") { actualStrategy = new KisHermiteFilterStrategy(); } else if (strategy == "bicubic") { actualStrategy = new KisBicubicFilterStrategy(); } else if (strategy == "box") { actualStrategy = new KisBoxFilterStrategy(); } else if (strategy == "bilinear") { actualStrategy = new KisBilinearFilterStrategy(); } else if (strategy == "bell") { actualStrategy = new KisBellFilterStrategy(); } else if (strategy == "bspline") { actualStrategy = new KisBSplineFilterStrategy(); } else if (strategy == "lanczos3") { actualStrategy = new KisLanczos3FilterStrategy(); } else if (strategy == "mitchell") { actualStrategy = new KisMitchellFilterStrategy(); } image->scaleImage(rc.size(), xres, yres, actualStrategy); } void Document::rotateImage(double radians) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->rotateImage(radians); } void Document::shearImage(double angleX, double angleY) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->shear(angleX, angleY); } bool Document::save() { if (!d->document) return false; return d->document->save(); } bool Document::saveAs(const QString &filename) { if (!d->document) return false; return d->document->saveAs(QUrl::fromLocalFile(filename)); } Node* Document::createNode(const QString &name, const QString &nodeType) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); Node *node = 0; if (nodeType == "paintlayer") { node = new Node(image, new KisPaintLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "grouplayer") { node = new Node(image, new KisGroupLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "filelayer") { node = new Node(image, new KisFileLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "filterlayer") { node = new Node(image, new KisAdjustmentLayer(image, name, 0, 0)); } else if (nodeType == "filllayer") { node = new Node(image, new KisGeneratorLayer(image, name, 0, 0)); } else if (nodeType == "clonelayer") { node = new Node(image, new KisCloneLayer(0, image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "vectorlayer") { node = new Node(image, new KisShapeLayer(d->document->shapeController(), image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "transparencymask") { node = new Node(image, new KisTransparencyMask()); } else if (nodeType == "filtermask") { node = new Node(image, new KisFilterMask()); } else if (nodeType == "transformmask") { node = new Node(image, new KisTransformMask()); } else if (nodeType == "selectionmask") { node = new Node(image, new KisSelectionMask(image)); } return node; } QImage Document::projection(int x, int y, int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->image()->convertToQImage(x, y, w, h, 0); } QImage Document::thumbnail(int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->generatePreview(QSize(w, h)).toImage(); } void Document::lock() { if (!d->document || !d->document->image()) return; d->document->image()->barrierLock(); } void Document::unlock() { if (!d->document || !d->document->image()) return; d->document->image()->unlock(); } void Document::waitForDone() { if (!d->document || !d->document->image()) return; d->document->image()->waitForDone(); } bool Document::tryBarrierLock() { if (!d->document || !d->document->image()) return false; return d->document->image()->tryBarrierLock(); } bool Document::isIdle() { if (!d->document || !d->document->image()) return false; return d->document->image()->isIdle(); } void Document::refreshProjection() { if (!d->document || !d->document->image()) return; d->document->image()->refreshGraph(); } QPointer Document::document() const { return d->document; } diff --git a/libs/odf/tests/CMakeLists.txt b/libs/odf/tests/CMakeLists.txt index 2c81799249..9dbf046647 100644 --- a/libs/odf/tests/CMakeLists.txt +++ b/libs/odf/tests/CMakeLists.txt @@ -1,23 +1,22 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include(ECMAddTests) include(KritaAddBrokenUnitTest) ecm_add_tests( TestKoGenStyles.cpp TestOdfSettings.cpp TestKoOdfLoadingContext.cpp TestStorage.cpp NAME_PREFIX "libs-odf-" LINK_LIBRARIES kritaodf KF5::I18n Qt5::Test) ecm_add_tests( TestXmlWriter.cpp TestXmlReader.cpp - TestXmlReaderWithoutSpaces.cpp kodomtest.cpp TestKoUnit.cpp TestNumberStyle.cpp TestKoElementReference.cpp NAME_PREFIX "libs-odf-" LINK_LIBRARIES kritaodf Qt5::Test) diff --git a/libs/odf/tests/TestXmlReaderWithoutSpaces.cpp b/libs/odf/tests/TestXmlReaderWithoutSpaces.cpp deleted file mode 100644 index e0b7107746..0000000000 --- a/libs/odf/tests/TestXmlReaderWithoutSpaces.cpp +++ /dev/null @@ -1,2700 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - - -class TestXmlReaderWithoutSpaces : public QObject -{ - Q_OBJECT -private Q_SLOTS: -#ifndef KOXML_USE_QDOM - void testNode(); - void testElement(); - void testAttributes(); - void testText(); - void testCDATA(); - void testDocument(); - void testDocumentType(); - void testNamespace(); - void testParseQString(); - void testUnload(); - void testSimpleXML(); - void testRootError(); - void testMismatchedTag(); - void testConvertQDomDocument(); - void testConvertQDomElement(); - void testSimpleOpenDocumentText(); - void testWhitespace(); - void testSimpleOpenDocumentSpreadsheet(); - void testSimpleOpenDocumentPresentation(); - void testSimpleOpenDocumentFormula(); - void testLargeOpenDocumentSpreadsheet(); - void testExternalOpenDocumentSpreadsheet(const QString& filename); -#endif -}; - -#ifndef KOXML_USE_QDOM -void TestXmlReaderWithoutSpaces::testNode() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // null node - KoXmlNode node1; - QCOMPARE(node1.nodeName(), QString()); - QCOMPARE(node1.isNull(), true); - QCOMPARE(node1.isElement(), false); - QCOMPARE(node1.isDocument(), false); - QCOMPARE(node1.ownerDocument().isNull(), true); - QCOMPARE(node1.parentNode().isNull(), true); - QCOMPARE(node1.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(node1), 0); - QCOMPARE(node1.firstChild().isNull(), true); - QCOMPARE(node1.lastChild().isNull(), true); - QCOMPARE(node1.previousSibling().isNull(), true); - QCOMPARE(node1.nextSibling().isNull(), true); - - // compare with another null node - KoXmlNode node2; - QCOMPARE(node2.isNull(), true); - QCOMPARE(node1 == node2, true); - QCOMPARE(node1 != node2, false); - - // a node which is a document - KoXmlNode node3 = doc; - QCOMPARE(node3.nodeName(), QString("#document")); - QCOMPARE(node3.isNull(), false); - QCOMPARE(node3.isElement(), false); - QCOMPARE(node3.isText(), false); - QCOMPARE(node3.isDocument(), true); - QCOMPARE(node3.ownerDocument().isNull(), false); - QCOMPARE(node3.ownerDocument() == doc, true); - QCOMPARE(node3.toDocument() == doc, true); - QCOMPARE(KoXml::childNodesCount(node3), 1); - - // convert to document and the compare - KoXmlDocument doc2 = node3.toDocument(); - QCOMPARE(doc2.nodeName(), QString("#document")); - QCOMPARE(doc2.isNull(), false); - QCOMPARE(doc2.isDocument(), true); - QCOMPARE(node3 == doc2, true); - QCOMPARE(KoXml::childNodesCount(doc2), 1); - - // a document is of course can't be converted to element - KoXmlElement invalidElement = node3.toElement(); - QCOMPARE(invalidElement.nodeName(), QString()); - QCOMPARE(invalidElement.isNull(), true); - QCOMPARE(invalidElement.isElement(), false); - QCOMPARE(invalidElement.isText(), false); - QCOMPARE(invalidElement.isDocument(), false); - - // clear() makes it a null node again - node3.clear(); - QCOMPARE(node3.isNull(), true); - QCOMPARE(node3.nodeName(), QString()); - QCOMPARE(node3.isElement(), false); - QCOMPARE(node3.isText(), false); - QCOMPARE(node3.isDocument(), false); - QCOMPARE(node3.ownerDocument().isNull(), true); - QCOMPARE(node1 == node3, true); - QCOMPARE(node1 != node3, false); - - // a node which is an element for - KoXmlNode node4 = doc.firstChild(); - QCOMPARE(node4.isNull(), false); - QCOMPARE(node4.isElement(), true); - QCOMPARE(node4.isText(), false); - QCOMPARE(node4.isDocument(), false); - QCOMPARE(node4.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(node4), 2); - QCOMPARE(node4.ownerDocument() == doc, true); - QCOMPARE(node4.toElement() == doc.firstChild().toElement(), true); - - // clear() makes it a null node again - node4.clear(); - QCOMPARE(node4.isNull(), true); - QCOMPARE(node4.isElement(), false); - QCOMPARE(node4.isText(), false); - QCOMPARE(node4.isDocument(), false); - QCOMPARE(node4 == node1, true); - QCOMPARE(node4 != node1, false); - QCOMPARE(KoXml::childNodesCount(node4), 0); - - // a node which is an element for - KoXmlNode node5 = doc.firstChild().firstChild(); - QCOMPARE(node5.nodeName(), QString("continents")); - QCOMPARE(node5.isNull(), false); - QCOMPARE(node5.isElement(), true); - QCOMPARE(node5.isText(), false); - QCOMPARE(node5.isDocument(), false); - QCOMPARE(node5.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(node5), 6); - QCOMPARE(node5.ownerDocument() == doc, true); - - // convert to element and the compare - KoXmlElement continentsElement = node5.toElement(); - QCOMPARE(node5 == continentsElement, true); - QCOMPARE(continentsElement.isNull(), false); - QCOMPARE(continentsElement.isElement(), true); - QCOMPARE(continentsElement.isText(), false); - QCOMPARE(continentsElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(continentsElement), 6); - QCOMPARE(continentsElement.ownerDocument() == doc, true); - - // and it doesn't make sense to convert that node to document - KoXmlDocument invalidDoc = node5.toDocument(); - QCOMPARE(invalidDoc.isNull(), true); - QCOMPARE(invalidDoc.isElement(), false); - QCOMPARE(invalidDoc.isText(), false); - QCOMPARE(invalidDoc.isDocument(), false); - - // node for using namedItem() function - KoXmlNode europeNode = continentsElement.namedItem(QString("europe")); - QCOMPARE(europeNode.nodeName(), QString("europe")); - QCOMPARE(europeNode.isNull(), false); - QCOMPARE(europeNode.isElement(), true); - QCOMPARE(europeNode.isText(), false); - QCOMPARE(europeNode.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(europeNode), 0); - QCOMPARE(europeNode.ownerDocument() == doc, true); - - // search non-existing node - KoXmlNode fooNode = continentsElement.namedItem(QString("foobar")); - QCOMPARE(fooNode.isNull(), true); - QCOMPARE(fooNode.isElement(), false); - QCOMPARE(fooNode.isText(), false); - QCOMPARE(fooNode.isCDATASection(), false); - QCOMPARE(KoXml::childNodesCount(fooNode), 0); -} - -void TestXmlReaderWithoutSpaces::testElement() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << ""; - xmlstream << ""; - xmlstream << "

"; - xmlstream << "Hello, world!"; - xmlstream << "

"; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // element for - KoXmlElement rootElement; - rootElement = doc.documentElement(); - QCOMPARE(rootElement.nodeName(), QString("html")); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.isDocument(), false); - QCOMPARE(rootElement.ownerDocument().isNull(), false); - QCOMPARE(rootElement.ownerDocument() == doc, true); - QCOMPARE(rootElement.parentNode().isNull(), false); - QCOMPARE(rootElement.parentNode().toDocument() == doc, true); - QCOMPARE(rootElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(rootElement), 1); - QCOMPARE(rootElement.tagName(), QString("html")); - QCOMPARE(rootElement.prefix().isNull(), true); - - // element for - KoXmlElement bodyElement; - bodyElement = rootElement.firstChild().toElement(); - QCOMPARE(bodyElement.nodeName(), QString("body")); - QCOMPARE(bodyElement.isNull(), false); - QCOMPARE(bodyElement.isElement(), true); - QCOMPARE(bodyElement.isDocument(), false); - QCOMPARE(bodyElement.ownerDocument().isNull(), false); - QCOMPARE(bodyElement.ownerDocument() == doc, true); - QCOMPARE(bodyElement.parentNode().isNull(), false); - QCOMPARE(bodyElement.parentNode() == rootElement, true); - QCOMPARE(bodyElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(bodyElement), 1); - QCOMPARE(bodyElement.tagName(), QString("body")); - QCOMPARE(bodyElement.prefix().isNull(), true); - QCOMPARE(bodyElement.hasAttribute("bgcolor"), true); - QCOMPARE(bodyElement.attribute("bgcolor"), QString("#000")); - - // a shared copy of , will still have access to attribute bgcolor - KoXmlElement body2Element; - body2Element = bodyElement; - QCOMPARE(body2Element.nodeName(), QString("body")); - QCOMPARE(body2Element.isNull(), false); - QCOMPARE(body2Element.isElement(), true); - QCOMPARE(body2Element.isDocument(), false); - QCOMPARE(body2Element.ownerDocument().isNull(), false); - QCOMPARE(body2Element.ownerDocument() == doc, true); - QCOMPARE(body2Element == bodyElement, true); - QCOMPARE(body2Element != bodyElement, false); - QCOMPARE(body2Element.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(body2Element), 1); - QCOMPARE(body2Element.tagName(), QString("body")); - QCOMPARE(body2Element.prefix().isNull(), true); - QCOMPARE(body2Element.hasAttribute("bgcolor"), true); - QCOMPARE(body2Element.attribute("bgcolor"), QString("#000")); - - // empty element, by default constructor - KoXmlElement testElement; - QCOMPARE(testElement.nodeName(), QString()); - QCOMPARE(testElement.isNull(), true); - QCOMPARE(testElement.isElement(), false); - QCOMPARE(testElement.isDocument(), false); - QCOMPARE(testElement.ownerDocument().isNull(), true); - QCOMPARE(testElement.ownerDocument() != doc, true); - QCOMPARE(testElement == rootElement, false); - QCOMPARE(testElement != rootElement, true); - QCOMPARE(testElement.parentNode().isNull(), true); - QCOMPARE(testElement.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(testElement), 0); - - // check assignment operator - testElement = rootElement; - QCOMPARE(testElement.nodeName(), QString("html")); - QCOMPARE(testElement.isNull(), false); - QCOMPARE(testElement.isElement(), true); - QCOMPARE(testElement.isDocument(), false); - QCOMPARE(testElement == rootElement, true); - QCOMPARE(testElement != rootElement, false); - QCOMPARE(testElement.parentNode().isNull(), false); - QCOMPARE(testElement.parentNode().toDocument() == doc, true); - QCOMPARE(testElement.tagName(), QString("html")); - QCOMPARE(testElement.prefix().isNull(), true); - QCOMPARE(KoXml::childNodesCount(testElement), 1); - - // assigned from another empty element - testElement = KoXmlElement(); - QCOMPARE(testElement.isNull(), true); - QCOMPARE(testElement != rootElement, true); - - // assigned from - testElement = bodyElement; - QCOMPARE(testElement.isNull(), false); - QCOMPARE(testElement.isElement(), true); - QCOMPARE(testElement.isDocument(), false); - QCOMPARE(testElement.ownerDocument().isNull(), false); - QCOMPARE(testElement.ownerDocument() == doc, true); - QCOMPARE(testElement == bodyElement, true); - QCOMPARE(testElement.parentNode().isNull(), false); - QCOMPARE(testElement.tagName(), QString("body")); - QCOMPARE(testElement.prefix().isNull(), true); - QCOMPARE(testElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(testElement), 1); - - // copy constructor - KoXmlElement dummyElement(rootElement); - QCOMPARE(dummyElement.isNull(), false); - QCOMPARE(dummyElement.isElement(), true); - QCOMPARE(dummyElement.isDocument(), false); - QCOMPARE(dummyElement.ownerDocument().isNull(), false); - QCOMPARE(dummyElement.ownerDocument() == doc, true); - QCOMPARE(dummyElement == rootElement, true); - QCOMPARE(dummyElement.parentNode().isNull(), false); - QCOMPARE(dummyElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(dummyElement), 1); - QCOMPARE(dummyElement.tagName(), QString("html")); - QCOMPARE(dummyElement.prefix().isNull(), true); - - // clear() turns element to null node - dummyElement.clear(); - QCOMPARE(dummyElement.isNull(), true); - QCOMPARE(dummyElement.isElement(), false); - QCOMPARE(dummyElement.isDocument(), false); - QCOMPARE(dummyElement.ownerDocument().isNull(), true); - QCOMPARE(dummyElement.ownerDocument() == doc, false); - QCOMPARE(dummyElement.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(dummyElement), 0); - QCOMPARE(dummyElement == rootElement, false); - QCOMPARE(dummyElement != rootElement, true); - - // check for plain null node converted to element - KoXmlNode dummyNode; - dummyElement = dummyNode.toElement(); - QCOMPARE(dummyElement.isNull(), true); - QCOMPARE(dummyElement.isElement(), false); - QCOMPARE(dummyElement.isDocument(), false); - QCOMPARE(dummyElement.ownerDocument().isNull(), true); - QCOMPARE(dummyElement.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(dummyElement), 0); - QCOMPARE(dummyElement.ownerDocument() == doc, false); -} - -void TestXmlReaderWithoutSpaces::testAttributes() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << "

"; - xmlstream << ""; - xmlstream << "

"; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - KoXmlElement rootElement; - rootElement = doc.documentElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.parentNode().isNull(), false); - QCOMPARE(rootElement.parentNode().toDocument() == doc, true); - QCOMPARE(rootElement.tagName(), QString("p")); - QCOMPARE(rootElement.prefix().isNull(), true); - QCOMPARE(KoXml::childNodesCount(rootElement), 1); - - KoXmlElement imgElement; - imgElement = rootElement.firstChild().toElement(); - QCOMPARE(imgElement.isNull(), false); - QCOMPARE(imgElement.isElement(), true); - QCOMPARE(imgElement.tagName(), QString("img")); - QCOMPARE(imgElement.prefix().isNull(), true); - QCOMPARE(KoXml::childNodesCount(imgElement), 0); - QCOMPARE(imgElement.hasAttribute("src"), true); - QCOMPARE(imgElement.hasAttribute("width"), true); - QCOMPARE(imgElement.hasAttribute("height"), true); - QCOMPARE(imgElement.hasAttribute("non-exist"), false); - QCOMPARE(imgElement.hasAttribute("SRC"), false); - QCOMPARE(imgElement.attribute("src"), QString("foo.png")); - QCOMPARE(imgElement.attribute("width"), QString("300")); - QCOMPARE(imgElement.attribute("width").toInt(), 300); - QCOMPARE(imgElement.attribute("height"), QString("150")); - QCOMPARE(imgElement.attribute("height").toInt(), 150); - QCOMPARE(imgElement.attribute("border").isEmpty(), true); - QCOMPARE(imgElement.attribute("border", "0").toInt(), 0); - QCOMPARE(imgElement.attribute("border", "-1").toInt(), -1); - - QStringList list = KoXml::attributeNames(imgElement); - QCOMPARE(list.count(), 3); - QVERIFY(list.contains("src")); - QVERIFY(list.contains("width")); - QVERIFY(list.contains("height")); - QVERIFY(! list.contains("border")); - Q_FOREACH (QString a, list) { - QCOMPARE(imgElement.hasAttribute(a), true); - QCOMPARE(imgElement.attribute(a).isEmpty(), false); - } -} - -void TestXmlReaderWithoutSpaces::testText() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << "

"; - xmlstream << "Hello "; - xmlstream << "world"; - xmlstream << "

"; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // element for

- KoXmlElement parElement; - parElement = doc.documentElement(); - QCOMPARE(parElement.isNull(), false); - QCOMPARE(parElement.isElement(), true); - QCOMPARE(parElement.isText(), false); - QCOMPARE(parElement.isDocument(), false); - QCOMPARE(parElement.ownerDocument().isNull(), false); - QCOMPARE(parElement.ownerDocument() == doc, true); - QCOMPARE(parElement.parentNode().isNull(), false); - QCOMPARE(parElement.parentNode().toDocument() == doc, true); - QCOMPARE(parElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(parElement), 2); // and text node "Hello " - QCOMPARE(parElement.tagName(), QString("p")); - QCOMPARE(parElement.prefix().isNull(), true); - QCOMPARE(parElement.text(), QString("Hello world")); - - // node for "Hello" - KoXmlNode helloNode; - helloNode = parElement.firstChild(); - QCOMPARE(helloNode.nodeName(), QString("#text")); - QCOMPARE(helloNode.isNull(), false); - QCOMPARE(helloNode.isElement(), false); - QCOMPARE(helloNode.isText(), true); - QCOMPARE(helloNode.isDocument(), false); - QCOMPARE(helloNode.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(helloNode), 0); - - // "Hello" text - KoXmlText helloText; - helloText = helloNode.toText(); - QCOMPARE(helloText.nodeName(), QString("#text")); - QCOMPARE(helloText.isNull(), false); - QCOMPARE(helloText.isElement(), false); - QCOMPARE(helloText.isText(), true); - QCOMPARE(helloText.isDocument(), false); - QCOMPARE(helloText.data(), QString("Hello ")); - QCOMPARE(KoXml::childNodesCount(helloText), 0); - - // shared copy of the text - KoXmlText hello2Text; - hello2Text = helloText; - QCOMPARE(hello2Text.isNull(), false); - QCOMPARE(hello2Text.isElement(), false); - QCOMPARE(hello2Text.isText(), true); - QCOMPARE(hello2Text.isDocument(), false); - QCOMPARE(hello2Text.data(), QString("Hello ")); - QCOMPARE(KoXml::childNodesCount(hello2Text), 0); - - // element for - KoXmlElement boldElement; - boldElement = helloNode.nextSibling().toElement(); - QCOMPARE(boldElement.isNull(), false); - QCOMPARE(boldElement.isElement(), true); - QCOMPARE(boldElement.isText(), false); - QCOMPARE(boldElement.isDocument(), false); - QCOMPARE(boldElement.ownerDocument().isNull(), false); - QCOMPARE(boldElement.ownerDocument() == doc, true); - QCOMPARE(boldElement.parentNode().isNull(), false); - QCOMPARE(boldElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(boldElement), 1); // text node "world" - QCOMPARE(boldElement.tagName(), QString("b")); - QCOMPARE(boldElement.prefix().isNull(), true); - - // "world" text - KoXmlText worldText; - worldText = boldElement.firstChild().toText(); - QCOMPARE(worldText.isNull(), false); - QCOMPARE(worldText.isElement(), false); - QCOMPARE(worldText.isText(), true); - QCOMPARE(worldText.isDocument(), false); - QCOMPARE(worldText.data(), QString("world")); - QCOMPARE(KoXml::childNodesCount(worldText), 0); -} - -void TestXmlReaderWithoutSpaces::testCDATA() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << "

"; - xmlstream << "Hello "; - xmlstream << ""; - xmlstream << "

"; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // element for

- KoXmlElement parElement; - parElement = doc.documentElement(); - QCOMPARE(parElement.isNull(), false); - QCOMPARE(parElement.isElement(), true); - QCOMPARE(parElement.isText(), false); - QCOMPARE(parElement.isDocument(), false); - QCOMPARE(parElement.ownerDocument().isNull(), false); - QCOMPARE(parElement.ownerDocument() == doc, true); - QCOMPARE(parElement.parentNode().isNull(), false); - QCOMPARE(parElement.parentNode().toDocument() == doc, true); - QCOMPARE(parElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(parElement), 2); - QCOMPARE(parElement.tagName(), QString("p")); - QCOMPARE(parElement.prefix().isNull(), true); - QCOMPARE(parElement.text(), QString("Hello world")); - - // node for "Hello" - KoXmlNode helloNode; - helloNode = parElement.firstChild(); - QCOMPARE(helloNode.isNull(), false); - QCOMPARE(helloNode.isElement(), false); - QCOMPARE(helloNode.isText(), true); - QCOMPARE(helloNode.isDocument(), false); - - // "Hello" text - KoXmlText helloText; - helloText = helloNode.toText(); - QCOMPARE(helloText.isNull(), false); - QCOMPARE(helloText.isElement(), false); - QCOMPARE(helloText.isText(), true); - QCOMPARE(helloText.isDocument(), false); - QCOMPARE(helloText.data(), QString("Hello ")); - - // node for CDATA "world!" - // Note: isText() is also true for CDATA - KoXmlNode worldNode; - worldNode = helloNode.nextSibling(); - QCOMPARE(worldNode.nodeName(), QString("#cdata-section")); - QCOMPARE(worldNode.isNull(), false); - QCOMPARE(worldNode.isElement(), false); - QCOMPARE(worldNode.isText(), true); - QCOMPARE(worldNode.isCDATASection(), true); - QCOMPARE(worldNode.isDocument(), false); - - // CDATA section for "world!" - // Note: isText() is also true for CDATA - KoXmlCDATASection worldCDATA; - worldCDATA = worldNode.toCDATASection(); - QCOMPARE(worldCDATA.nodeName(), QString("#cdata-section")); - QCOMPARE(worldCDATA.isNull(), false); - QCOMPARE(worldCDATA.isElement(), false); - QCOMPARE(worldCDATA.isText(), true); - QCOMPARE(worldCDATA.isCDATASection(), true); - QCOMPARE(worldCDATA.isDocument(), false); - QCOMPARE(worldCDATA.data(), QString("world")); -} - -void TestXmlReaderWithoutSpaces::testDocument() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << ""; - xmlstream << "\n"; - xmlstream << "\n"; - xmlstream << "\n"; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - - // empty document - QCOMPARE(doc.nodeName(), QString()); - QCOMPARE(doc.isNull(), true); - QCOMPARE(doc.isElement(), false); - QCOMPARE(doc.isDocument(), false); - QCOMPARE(doc.parentNode().isNull(), true); - QCOMPARE(doc.firstChild().isNull(), true); - QCOMPARE(doc.lastChild().isNull(), true); - QCOMPARE(doc.previousSibling().isNull(), true); - QCOMPARE(doc.nextSibling().isNull(), true); - - // now give something as the content - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // this document has something already - QCOMPARE(doc.nodeName(), QString("#document")); - QCOMPARE(doc.isNull(), false); - QCOMPARE(doc.isElement(), false); - QCOMPARE(doc.isDocument(), true); - QCOMPARE(doc.parentNode().isNull(), true); - QCOMPARE(doc.firstChild().isNull(), false); - QCOMPARE(doc.lastChild().isNull(), false); - QCOMPARE(doc.previousSibling().isNull(), true); - QCOMPARE(doc.nextSibling().isNull(), true); - - // make sure its children are fine - KoXmlElement rootElement; - rootElement = doc.firstChild().toElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.isDocument(), false); - QCOMPARE(rootElement.parentNode().isNull(), false); - QCOMPARE(rootElement.parentNode().toDocument() == doc, true); - rootElement = doc.lastChild().toElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.isDocument(), false); - QCOMPARE(rootElement.parentNode().isNull(), false); - QCOMPARE(rootElement.parentNode().toDocument() == doc, true); - - // clear() converts it into null node - doc.clear(); - QCOMPARE(doc.nodeName(), QString()); - QCOMPARE(doc.isNull(), true); - QCOMPARE(doc.isElement(), false); - QCOMPARE(doc.isDocument(), false); - QCOMPARE(doc.parentNode().isNull(), true); - QCOMPARE(doc.firstChild().isNull(), true); - QCOMPARE(doc.lastChild().isNull(), true); - QCOMPARE(doc.previousSibling().isNull(), true); - QCOMPARE(doc.nextSibling().isNull(), true); - - // assigned from another empty document - doc = KoXmlDocument(); - QCOMPARE(doc.nodeName(), QString()); - QCOMPARE(doc.nodeName().isEmpty(), true); - QCOMPARE(doc.isNull(), true); - QCOMPARE(doc.isElement(), false); - QCOMPARE(doc.isDocument(), false); - QCOMPARE(doc.parentNode().isNull(), true); -} - -void TestXmlReaderWithoutSpaces::testDocumentType() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << "

"; - xmlstream << "

"; - xmlstream << ""; - xmldevice.close(); - - // empty document - KoXmlDocument doc(false); - QCOMPARE(doc.nodeName(), QString()); - QCOMPARE(doc.isNull(), true); - QCOMPARE(doc.isElement(), false); - QCOMPARE(doc.isDocument(), false); - QCOMPARE(doc.parentNode().isNull(), true); - QCOMPARE(doc.firstChild().isNull(), true); - QCOMPARE(doc.lastChild().isNull(), true); - QCOMPARE(doc.previousSibling().isNull(), true); - QCOMPARE(doc.nextSibling().isNull(), true); - - // has empty doctype - KoXmlDocumentType doctype = doc.doctype(); - QCOMPARE(doctype.nodeName(), QString()); - QCOMPARE(doctype.isNull(), true); - QCOMPARE(doctype.isElement(), false); - QCOMPARE(doctype.isDocument(), false); - QCOMPARE(doctype.isDocumentType(), false); - QCOMPARE(doctype.parentNode().isNull(), true); - QCOMPARE(doctype.firstChild().isNull(), true); - QCOMPARE(doctype.lastChild().isNull(), true); - QCOMPARE(doctype.previousSibling().isNull(), true); - QCOMPARE(doctype.nextSibling().isNull(), true); - - // now give something as the content - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // this document has something already - QCOMPARE(doc.nodeName(), QString("#document")); - QCOMPARE(doc.isNull(), false); - QCOMPARE(doc.isElement(), false); - QCOMPARE(doc.isDocument(), true); - QCOMPARE(doc.parentNode().isNull(), true); - QCOMPARE(doc.firstChild().isNull(), false); - QCOMPARE(doc.lastChild().isNull(), false); - QCOMPARE(doc.previousSibling().isNull(), true); - QCOMPARE(doc.nextSibling().isNull(), true); - - // the doctype becomes a valid one - doctype = doc.doctype(); - QCOMPARE(doctype.nodeName(), QString("html")); - QCOMPARE(doctype.name(), QString("html")); - QCOMPARE(doctype.isNull(), false); - QCOMPARE(doctype.isElement(), false); - QCOMPARE(doctype.isDocument(), false); - QCOMPARE(doctype.isDocumentType(), true); - QCOMPARE(doctype.parentNode().isNull(), false); - QCOMPARE(doctype.parentNode() == doc, true); - QCOMPARE(doctype.firstChild().isNull(), true); - QCOMPARE(doctype.lastChild().isNull(), true); - QCOMPARE(doctype.previousSibling().isNull(), true); - QCOMPARE(doctype.nextSibling().isNull(), true); -} - -void TestXmlReaderWithoutSpaces::testNamespace() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - // taken from example in Qt documentation (xml.html) - xmlstream << ""; - xmlstream << ""; - xmlstream << "Practical XML"; - xmlstream << ""; - xmlstream << ""; - xmlstream << "A Namespace Called fnord"; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - KoXmlElement rootElement; - KoXmlElement bookElement; - KoXmlElement bookTitleElement; - KoXmlElement bookAuthorElement; - - // ------------- first without any namespace processing ----------- - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - rootElement = doc.documentElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.tagName(), QString("document")); - QCOMPARE(rootElement.prefix().isNull(), true); - - bookElement = rootElement.firstChild().toElement(); - QCOMPARE(bookElement.isNull(), false); - QCOMPARE(bookElement.isElement(), true); - QCOMPARE(bookElement.tagName(), QString("book")); - QCOMPARE(bookElement.prefix().isNull(), true); - QCOMPARE(bookElement.localName(), QString()); - - bookTitleElement = bookElement.firstChild().toElement(); - QCOMPARE(bookTitleElement.isNull(), false); - QCOMPARE(bookTitleElement.isElement(), true); - QCOMPARE(bookTitleElement.tagName(), QString("book:title")); - QCOMPARE(bookTitleElement.prefix().isNull(), true); - QCOMPARE(bookTitleElement.localName(), QString()); - - bookAuthorElement = bookTitleElement.nextSibling().toElement(); - QCOMPARE(bookAuthorElement.isNull(), false); - QCOMPARE(bookAuthorElement.isElement(), true); - QCOMPARE(bookAuthorElement.tagName(), QString("book:author")); - QCOMPARE(bookAuthorElement.prefix().isNull(), true); - QCOMPARE(bookAuthorElement.attribute("title"), QString("Ms")); - QCOMPARE(bookAuthorElement.attribute("fnord:title"), QString("Goddess")); - QCOMPARE(bookAuthorElement.attribute("name"), QString("Eris Kallisti")); - - // ------------- now with namespace processing ----------- - xmldevice.seek(0); // just to rewind - - QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - const char* defaultNS = "http://trolltech.com/fnord/"; - const char* bookNS = "http://trolltech.com/fnord/book/"; - const char* fnordNS = "http://trolltech.com/fnord/"; - - // - rootElement = doc.documentElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.tagName(), QString("document")); - QCOMPARE(rootElement.prefix().isEmpty(), true); - QCOMPARE(rootElement.namespaceURI(), QString(defaultNS)); - QCOMPARE(rootElement.localName(), QString("document")); - - // - bookElement = rootElement.firstChild().toElement(); - QCOMPARE(bookElement.isNull(), false); - QCOMPARE(bookElement.isElement(), true); - QCOMPARE(bookElement.tagName(), QString("book")); - QCOMPARE(bookElement.prefix().isEmpty(), true); - QCOMPARE(bookElement.namespaceURI(), QString(defaultNS)); - QCOMPARE(bookElement.localName(), QString("book")); - - // - bookTitleElement = bookElement.firstChild().toElement(); - QCOMPARE(bookTitleElement.isNull(), false); - QCOMPARE(bookTitleElement.isElement(), true); - QCOMPARE(bookTitleElement.tagName(), QString("title")); - QCOMPARE(bookTitleElement.prefix(), QString("book")); - QCOMPARE(bookTitleElement.namespaceURI(), QString(bookNS)); - QCOMPARE(bookTitleElement.localName(), QString("title")); - - // another way, find it using namedItemNS() - KoXmlElement book2TitleElement; - book2TitleElement = KoXml::namedItemNS(rootElement.firstChild(), bookNS, "title"); - //book2TitleElement = bookElement.namedItemNS( bookNS, "title" ).toElement(); - QCOMPARE(book2TitleElement == bookTitleElement, true); - QCOMPARE(book2TitleElement.isNull(), false); - QCOMPARE(book2TitleElement.isElement(), true); - QCOMPARE(book2TitleElement.tagName(), QString("title")); - - // - bookAuthorElement = bookTitleElement.nextSibling().toElement(); - QCOMPARE(bookAuthorElement.isNull(), false); - QCOMPARE(bookAuthorElement.isElement(), true); - QCOMPARE(bookAuthorElement.tagName(), QString("author")); - QCOMPARE(bookAuthorElement.prefix(), QString("book")); - QCOMPARE(bookAuthorElement.namespaceURI(), QString(bookNS)); - QCOMPARE(bookAuthorElement.localName(), QString("author")); - - // another way, find it using namedItemNS() - KoXmlElement book2AuthorElement; - book2AuthorElement = KoXml::namedItemNS(bookElement, bookNS, "author"); - //book2AuthorElement = bookElement.namedItemNS( bookNS, "author" ).toElement(); - QCOMPARE(book2AuthorElement == bookAuthorElement, true); - QCOMPARE(book2AuthorElement.isNull(), false); - QCOMPARE(book2AuthorElement.isElement(), true); - QCOMPARE(book2AuthorElement.tagName(), QString("author")); - - // attributes in - // Note: with namespace processing, attribute's prefix is taken out and - // hence "fnord:title" will simply override "title" - // and searching attribute with prefix will give no result - QCOMPARE(bookAuthorElement.hasAttribute("title"), true); - QCOMPARE(bookAuthorElement.hasAttribute("fnord:title"), false); - QCOMPARE(bookAuthorElement.hasAttribute("name"), true); - QCOMPARE(bookAuthorElement.attribute("title"), QString("Goddess")); - QCOMPARE(bookAuthorElement.attribute("fnord:title").isEmpty(), true); - QCOMPARE(bookAuthorElement.attribute("name"), QString("Eris Kallisti")); - - // attributes in , with NS family of functions - // those without prefix are not accessible at all, because they do not belong - // to any namespace at all. - // Note: default namespace does not apply to attribute names! - QCOMPARE(bookAuthorElement.hasAttributeNS(defaultNS, "title"), true); - QCOMPARE(bookAuthorElement.hasAttributeNS(bookNS, "title"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(fnordNS, "title"), true); - - QCOMPARE(bookAuthorElement.attributeNS(defaultNS, "title", ""), QString("Goddess")); - QCOMPARE(bookAuthorElement.attributeNS(bookNS, "title", ""), QString()); - QCOMPARE(bookAuthorElement.attributeNS(fnordNS, "title", ""), QString("Goddess")); - - QCOMPARE(bookAuthorElement.hasAttributeNS(defaultNS, "fnord:title"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(bookNS, "fnord:title"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(fnordNS, "fnord:title"), false); - - QCOMPARE(bookAuthorElement.hasAttributeNS(defaultNS, "name"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(bookNS, "name"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(fnordNS, "name"), false); - - QCOMPARE(bookAuthorElement.attributeNS(defaultNS, "name", QString()).isEmpty(), true); - QCOMPARE(bookAuthorElement.attributeNS(bookNS, "name", QString()).isEmpty(), true); - QCOMPARE(bookAuthorElement.attributeNS(fnordNS, "name", QString()).isEmpty(), true); -} - -// mostly similar to testNamespace above, but parse from a QString -void TestXmlReaderWithoutSpaces::testParseQString() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QString xmlText; - xmlText += ""; - xmlText += ""; - xmlText += "Practical XML"; - xmlText += ""; - xmlText += ""; - xmlText += "A Namespace Called fnord"; - xmlText += ""; - xmlText += ""; - xmlText += ""; - - KoXmlDocument doc(false); - KoXmlElement rootElement; - KoXmlElement bookElement; - KoXmlElement bookTitleElement; - KoXmlElement bookAuthorElement; - - QCOMPARE(doc.setContent(xmlText, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - const char* defaultNS = "http://trolltech.com/fnord/"; - const char* bookNS = "http://trolltech.com/fnord/book/"; - const char* fnordNS = "http://trolltech.com/fnord/"; - - // - rootElement = doc.documentElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.tagName(), QString("document")); - QCOMPARE(rootElement.prefix().isEmpty(), true); - QCOMPARE(rootElement.namespaceURI(), QString(defaultNS)); - QCOMPARE(rootElement.localName(), QString("document")); - - // - bookElement = rootElement.firstChild().toElement(); - QCOMPARE(bookElement.isNull(), false); - QCOMPARE(bookElement.isElement(), true); - QCOMPARE(bookElement.tagName(), QString("book")); - QCOMPARE(bookElement.prefix().isEmpty(), true); - QCOMPARE(bookElement.namespaceURI(), QString(defaultNS)); - QCOMPARE(bookElement.localName(), QString("book")); - - // - bookTitleElement = bookElement.firstChild().toElement(); - QCOMPARE(bookTitleElement.isNull(), false); - QCOMPARE(bookTitleElement.isElement(), true); - QCOMPARE(bookTitleElement.tagName(), QString("title")); - QCOMPARE(bookTitleElement.prefix(), QString("book")); - QCOMPARE(bookTitleElement.namespaceURI(), QString(bookNS)); - QCOMPARE(bookTitleElement.localName(), QString("title")); - - // another way, find it using namedItemNS() - KoXmlElement book2TitleElement; - book2TitleElement = KoXml::namedItemNS(rootElement.firstChild(), bookNS, "title"); - //book2TitleElement = bookElement.namedItemNS( bookNS, "title" ).toElement(); - QCOMPARE(book2TitleElement == bookTitleElement, true); - QCOMPARE(book2TitleElement.isNull(), false); - QCOMPARE(book2TitleElement.isElement(), true); - QCOMPARE(book2TitleElement.tagName(), QString("title")); - - // - bookAuthorElement = bookTitleElement.nextSibling().toElement(); - QCOMPARE(bookAuthorElement.isNull(), false); - QCOMPARE(bookAuthorElement.isElement(), true); - QCOMPARE(bookAuthorElement.tagName(), QString("author")); - QCOMPARE(bookAuthorElement.prefix(), QString("book")); - QCOMPARE(bookAuthorElement.namespaceURI(), QString(bookNS)); - QCOMPARE(bookAuthorElement.localName(), QString("author")); - - // another way, find it using namedItemNS() - KoXmlElement book2AuthorElement; - book2AuthorElement = KoXml::namedItemNS(bookElement, bookNS, "author"); - //book2AuthorElement = bookElement.namedItemNS( bookNS, "author" ).toElement(); - QCOMPARE(book2AuthorElement == bookAuthorElement, true); - QCOMPARE(book2AuthorElement.isNull(), false); - QCOMPARE(book2AuthorElement.isElement(), true); - QCOMPARE(book2AuthorElement.tagName(), QString("author")); - - // attributes in - // Note: with namespace processing, attribute's prefix is taken out and - // hence "fnord:title" will simply override "title" - // and searching attribute with prefix will give no result - QCOMPARE(bookAuthorElement.hasAttribute("title"), true); - QCOMPARE(bookAuthorElement.hasAttribute("fnord:title"), false); - QCOMPARE(bookAuthorElement.hasAttribute("name"), true); - QCOMPARE(bookAuthorElement.attribute("title"), QString("Goddess")); - QCOMPARE(bookAuthorElement.attribute("fnord:title").isEmpty(), true); - QCOMPARE(bookAuthorElement.attribute("name"), QString("Eris Kallisti")); - - // attributes in , with NS family of functions - // those without prefix are not accessible at all, because they do not belong - // to any namespace at all. - // Note: default namespace does not apply to attribute names! - QCOMPARE(bookAuthorElement.hasAttributeNS(defaultNS, "title"), true); - QCOMPARE(bookAuthorElement.hasAttributeNS(bookNS, "title"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(fnordNS, "title"), true); - - QCOMPARE(bookAuthorElement.attributeNS(defaultNS, "title", ""), QString("Goddess")); - QCOMPARE(bookAuthorElement.attributeNS(bookNS, "title", ""), QString()); - QCOMPARE(bookAuthorElement.attributeNS(fnordNS, "title", ""), QString("Goddess")); - - QCOMPARE(bookAuthorElement.hasAttributeNS(defaultNS, "fnord:title"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(bookNS, "fnord:title"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(fnordNS, "fnord:title"), false); - - QCOMPARE(bookAuthorElement.hasAttributeNS(defaultNS, "name"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(bookNS, "name"), false); - QCOMPARE(bookAuthorElement.hasAttributeNS(fnordNS, "name"), false); - - QCOMPARE(bookAuthorElement.attributeNS(defaultNS, "name", QString()).isEmpty(), true); - QCOMPARE(bookAuthorElement.attributeNS(bookNS, "name", QString()).isEmpty(), true); - QCOMPARE(bookAuthorElement.attributeNS(fnordNS, "name", QString()).isEmpty(), true); -} - -void TestXmlReaderWithoutSpaces::testUnload() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - KoXmlElement earthElement; - earthElement = doc.documentElement().toElement(); - QCOMPARE(earthElement.isNull(), false); - QCOMPARE(earthElement.isElement(), true); - QCOMPARE(earthElement.parentNode().isNull(), false); - QCOMPARE(earthElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(earthElement), 2); - QCOMPARE(earthElement.tagName(), QString("earth")); - QCOMPARE(earthElement.prefix().isNull(), true); - - // this ensures that all child nodes of are loaded - earthElement.firstChild(); - - // explicitly unload all child nodes of - KoXml::unload(earthElement); - - // we should get the correct first child - KoXmlElement continentsElement = earthElement.firstChild().toElement(); - QCOMPARE(continentsElement.nodeName(), QString("continents")); - QCOMPARE(continentsElement.isNull(), false); - QCOMPARE(continentsElement.isElement(), true); - QCOMPARE(continentsElement.isText(), false); - QCOMPARE(continentsElement.isDocument(), false); - QCOMPARE(continentsElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(continentsElement), 6); - - // let us unload everything again - KoXml::unload(earthElement); - - // we should get the correct last child - KoXmlElement oceansElement = earthElement.lastChild().toElement(); - QCOMPARE(oceansElement.nodeName(), QString("oceans")); - QCOMPARE(oceansElement.isNull(), false); - QCOMPARE(oceansElement.isElement(), true); - QCOMPARE(oceansElement.isText(), false); - QCOMPARE(oceansElement.isDocument(), false); - QCOMPARE(oceansElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(continentsElement), 6); -} - -void TestXmlReaderWithoutSpaces::testSimpleXML() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // - KoXmlElement rootElement; - rootElement = doc.documentElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.parentNode().isNull(), false); - QCOMPARE(rootElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(rootElement), 5); - QCOMPARE(rootElement.tagName(), QString("solarsystem")); - QCOMPARE(rootElement.prefix().isNull(), true); - - // node - KoXmlNode firstPlanetNode; - firstPlanetNode = rootElement.firstChild(); - QCOMPARE(firstPlanetNode.isNull(), false); - QCOMPARE(firstPlanetNode.isElement(), true); - QCOMPARE(firstPlanetNode.nextSibling().isNull(), false); - QCOMPARE(firstPlanetNode.previousSibling().isNull(), true); - QCOMPARE(firstPlanetNode.parentNode().isNull(), false); - QCOMPARE(firstPlanetNode.parentNode() == rootElement, true); - QCOMPARE(firstPlanetNode.parentNode() != rootElement, false); - QCOMPARE(firstPlanetNode.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(firstPlanetNode), 0); - QCOMPARE(firstPlanetNode.firstChild().isNull(), true); - QCOMPARE(firstPlanetNode.lastChild().isNull(), true); - - // element - KoXmlElement firstPlanetElement; - firstPlanetElement = firstPlanetNode.toElement(); - QCOMPARE(firstPlanetElement.isNull(), false); - QCOMPARE(firstPlanetElement.isElement(), true); - QCOMPARE(firstPlanetElement.parentNode().isNull(), false); - QCOMPARE(firstPlanetElement.parentNode() == rootElement, true); - QCOMPARE(firstPlanetElement.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(firstPlanetNode), 0); - QCOMPARE(firstPlanetElement.firstChild().isNull(), true); - QCOMPARE(firstPlanetElement.lastChild().isNull(), true); - QCOMPARE(firstPlanetElement.tagName(), QString("mercurius")); - QCOMPARE(firstPlanetElement.prefix().isNull(), true); - - // node - KoXmlNode secondPlanetNode; - secondPlanetNode = firstPlanetNode.nextSibling(); - QCOMPARE(secondPlanetNode.isNull(), false); - QCOMPARE(secondPlanetNode.isElement(), true); - QCOMPARE(secondPlanetNode.nextSibling().isNull(), false); - QCOMPARE(secondPlanetNode.previousSibling().isNull(), false); - QCOMPARE(secondPlanetNode.previousSibling() == firstPlanetNode, true); - QCOMPARE(secondPlanetNode.previousSibling() == firstPlanetElement, true); - QCOMPARE(secondPlanetNode.parentNode().isNull(), false); - QCOMPARE(secondPlanetNode.parentNode() == rootElement, true); - QCOMPARE(secondPlanetNode.parentNode() != rootElement, false); - QCOMPARE(secondPlanetNode.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(secondPlanetNode), 0); - QCOMPARE(secondPlanetNode.firstChild().isNull(), true); - QCOMPARE(secondPlanetNode.lastChild().isNull(), true); - - // element - KoXmlElement secondPlanetElement; - secondPlanetElement = secondPlanetNode.toElement(); - QCOMPARE(secondPlanetElement.isNull(), false); - QCOMPARE(secondPlanetElement.isElement(), true); - QCOMPARE(secondPlanetElement.nextSibling().isNull(), false); - QCOMPARE(secondPlanetElement.previousSibling().isNull(), false); - QCOMPARE(secondPlanetElement.previousSibling() == firstPlanetNode, true); - QCOMPARE(secondPlanetElement.previousSibling() == firstPlanetElement, true); - QCOMPARE(secondPlanetElement.parentNode().isNull(), false); - QCOMPARE(secondPlanetElement.parentNode() == rootElement, true); - QCOMPARE(secondPlanetElement.parentNode() != rootElement, false); - QCOMPARE(secondPlanetElement.hasChildNodes(), false); - QCOMPARE(KoXml::childNodesCount(secondPlanetNode), 0); - QCOMPARE(secondPlanetElement.firstChild().isNull(), true); - QCOMPARE(secondPlanetElement.lastChild().isNull(), true); - QCOMPARE(secondPlanetElement.tagName(), QString("venus")); - QCOMPARE(secondPlanetElement.prefix().isNull(), true); -} - -void TestXmlReaderWithoutSpaces::testRootError() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - // multiple root nodes are not valid ! - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), false); - QCOMPARE(errorMsg.isEmpty(), false); - QCOMPARE(errorMsg, QString("Extra content at end of document.")); - QCOMPARE(errorLine, 1); - QCOMPARE(errorColumn, 21); -} - -void TestXmlReaderWithoutSpaces::testMismatchedTag() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), false); - QCOMPARE(errorMsg.isEmpty(), false); - QCOMPARE(errorMsg, QString("Opening and ending tag mismatch.")); - QCOMPARE(errorLine, 1); - QCOMPARE(errorColumn, 11); -} - -static void dumpNodes(const KoXmlNode &node, int level=0) -{ - QString indent = QString("%1").arg("", level*3); - if (node.isNull()) { - qDebug()<"; - xmlstream << ""; - xmlstream << ""; - xmlstream << "

The best place

"; - xmlstream << ""; - xmlstream << "
"; - xmlstream << ""; - xmlstream << ""; - xmlstream << "
"; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - dumpNodes(doc); - // - KoXmlElement rootElement; - rootElement = doc.documentElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.parentNode().isNull(), false); - QCOMPARE(rootElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(rootElement), 5); - QCOMPARE(rootElement.tagName(), QString("solarsystem")); - QCOMPARE(rootElement.prefix().isNull(), true); - - // now test converting KoXmlDocument to QDomDocument - QDomDocument universeDoc = KoXml::asQDomDocument(doc); - - // - QDomElement solarSystemElement = universeDoc.documentElement(); - QCOMPARE(solarSystemElement.isNull(), false); - QCOMPARE(solarSystemElement.isElement(), true); - QCOMPARE(solarSystemElement.parentNode().isNull(), false); - QCOMPARE(solarSystemElement.hasChildNodes(), true); - QCOMPARE(solarSystemElement.tagName(), QString("solarsystem")); - QCOMPARE(solarSystemElement.prefix().isNull(), true); - - // - QDomElement earthElement = solarSystemElement.namedItem("earth").toElement(); - QCOMPARE(earthElement.isNull(), false); - QCOMPARE(earthElement.isElement(), true); - QCOMPARE(earthElement.parentNode().isNull(), false); - QCOMPARE(earthElement.hasAttribute("habitable"), true); - QCOMPARE(earthElement.hasChildNodes(), true); - QCOMPARE(earthElement.tagName(), QString("earth")); - QCOMPARE(earthElement.prefix().isNull(), true); - - //

in - QDomNode placeNode = earthElement.firstChild(); - qDebug()<<"placeNode"<"; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << "

The best place

"; - xmlstream << ""; - xmlstream << "
"; - xmlstream << ""; - xmlstream << ""; - xmlstream << "
"; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - // - KoXmlElement rootElement; - rootElement = doc.documentElement(); - QCOMPARE(rootElement.isNull(), false); - QCOMPARE(rootElement.isElement(), true); - QCOMPARE(rootElement.parentNode().isNull(), false); - QCOMPARE(rootElement.hasChildNodes(), true); - QCOMPARE(KoXml::childNodesCount(rootElement), 1); - QCOMPARE(rootElement.tagName(), QString("universe")); - QCOMPARE(rootElement.prefix().isNull(), true); - - // now test converting KoXmlElement to QDomElement - QDomDocument solarDoc; - KoXml::asQDomElement(solarDoc, rootElement.firstChild().toElement()); - - // - QDomElement solarSystemElement = solarDoc.documentElement(); - QCOMPARE(solarSystemElement.isNull(), false); - QCOMPARE(solarSystemElement.isElement(), true); - QCOMPARE(solarSystemElement.parentNode().isNull(), false); - QCOMPARE(solarSystemElement.hasChildNodes(), true); - QCOMPARE(solarSystemElement.tagName(), QString("solarsystem")); - QCOMPARE(solarSystemElement.prefix().isNull(), true); - - // - QDomElement earthElement = solarSystemElement.namedItem("earth").toElement(); - QCOMPARE(earthElement.isNull(), false); - QCOMPARE(earthElement.isElement(), true); - QCOMPARE(earthElement.parentNode().isNull(), false); - QCOMPARE(earthElement.hasAttribute("habitable"), true); - QCOMPARE(earthElement.hasChildNodes(), true); - QCOMPARE(earthElement.tagName(), QString("earth")); - QCOMPARE(earthElement.prefix().isNull(), true); - - //

in - QDomNode placeNode = earthElement.firstChild(); - QCOMPARE(placeNode.isNull(), false); - QCOMPARE(placeNode.isElement(), true); - QCOMPARE(placeNode.toElement().text(), QString("The best place")); - QCOMPARE(placeNode.nextSibling().isNull(), false); - QCOMPARE(placeNode.previousSibling().isNull(), true); - QCOMPARE(placeNode.parentNode().isNull(), false); - QCOMPARE(placeNode.parentNode() == earthElement, true); - QCOMPARE(placeNode.hasChildNodes(), true); - QCOMPARE(placeNode.childNodes().count(), 1); - - //printf("Result:\n%s\n\n", qPrintable(universeDoc.toString())); -} - - -void TestXmlReaderWithoutSpaces::testSimpleOpenDocumentText() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - // content.xml from a simple OpenDocument text - // it has only paragraph "Hello, world!" - // automatic styles, declarations and unnecessary namespaces are omitted. - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << "Hello, world!"; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - const char* officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; - const char* textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; - - // - KoXmlElement contentElement; - contentElement = doc.documentElement(); - QCOMPARE(contentElement.isNull(), false); - QCOMPARE(contentElement.isElement(), true); - QCOMPARE(contentElement.parentNode().isNull(), false); - QCOMPARE(contentElement.parentNode().toDocument() == doc, true); - QCOMPARE(KoXml::childNodesCount(contentElement), 2); - QCOMPARE(contentElement.firstChild().isNull(), false); - QCOMPARE(contentElement.lastChild().isNull(), false); - QCOMPARE(contentElement.previousSibling().isNull(), false); - QCOMPARE(contentElement.nextSibling().isNull(), true); - QCOMPARE(contentElement.localName(), QString("document-content")); - QCOMPARE(contentElement.hasAttributeNS(officeNS, "version"), true); - QCOMPARE(contentElement.attributeNS(officeNS, "version", ""), QString("1.0")); - - // - KoXmlElement stylesElement; - stylesElement = KoXml::namedItemNS(contentElement, officeNS, "automatic-styles"); - QCOMPARE(stylesElement.isNull(), false); - QCOMPARE(stylesElement.isElement(), true); - QCOMPARE(stylesElement.parentNode().isNull(), false); - QCOMPARE(stylesElement.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(stylesElement), 0); - QCOMPARE(stylesElement.firstChild().isNull(), true); - QCOMPARE(stylesElement.lastChild().isNull(), true); - QCOMPARE(stylesElement.previousSibling().isNull(), true); - QCOMPARE(stylesElement.nextSibling().isNull(), false); - QCOMPARE(stylesElement.localName(), QString("automatic-styles")); - - // also same , but without namedItemNS - KoXmlNode styles2Element; - styles2Element = contentElement.firstChild().toElement(); - QCOMPARE(styles2Element.isNull(), false); - QCOMPARE(styles2Element.isElement(), true); - QCOMPARE(styles2Element.parentNode().isNull(), false); - QCOMPARE(styles2Element.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(styles2Element), 0); - QCOMPARE(styles2Element.firstChild().isNull(), true); - QCOMPARE(styles2Element.lastChild().isNull(), true); - QCOMPARE(styles2Element.previousSibling().isNull(), true); - QCOMPARE(styles2Element.nextSibling().isNull(), false); - QCOMPARE(styles2Element.localName(), QString("automatic-styles")); - - // - KoXmlElement bodyElement; - bodyElement = KoXml::namedItemNS(contentElement, officeNS, "body"); - QCOMPARE(bodyElement.isNull(), false); - QCOMPARE(bodyElement.isElement(), true); - QCOMPARE(bodyElement.parentNode().isNull(), false); - QCOMPARE(bodyElement.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(bodyElement), 1); - QCOMPARE(bodyElement.firstChild().isNull(), false); - QCOMPARE(bodyElement.lastChild().isNull(), false); - QCOMPARE(bodyElement.previousSibling().isNull(), false); - QCOMPARE(bodyElement.nextSibling().isNull(), true); - QCOMPARE(bodyElement.localName(), QString("body")); - - // also same , but without namedItemNS - KoXmlElement body2Element; - body2Element = stylesElement.nextSibling().toElement(); - QCOMPARE(body2Element.isNull(), false); - QCOMPARE(body2Element.isElement(), true); - QCOMPARE(body2Element.parentNode().isNull(), false); - QCOMPARE(body2Element.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(body2Element), 1); - QCOMPARE(body2Element.firstChild().isNull(), false); - QCOMPARE(body2Element.lastChild().isNull(), false); - QCOMPARE(body2Element.previousSibling().isNull(), false); - QCOMPARE(body2Element.nextSibling().isNull(), true); - QCOMPARE(body2Element.localName(), QString("body")); - - // - KoXmlElement textElement; - textElement = KoXml::namedItemNS(bodyElement, officeNS, "text"); - QCOMPARE(textElement.isNull(), false); - QCOMPARE(textElement.isElement(), true); - QCOMPARE(textElement.parentNode().isNull(), false); - QCOMPARE(textElement.parentNode() == bodyElement, true); - QCOMPARE(KoXml::childNodesCount(textElement), 1); - QCOMPARE(textElement.firstChild().isNull(), false); - QCOMPARE(textElement.lastChild().isNull(), false); - QCOMPARE(textElement.previousSibling().isNull(), true); - QCOMPARE(textElement.nextSibling().isNull(), true); - QCOMPARE(textElement.localName(), QString("text")); - - // the same , but without namedItemNS - KoXmlElement text2Element; - text2Element = bodyElement.firstChild().toElement(); - QCOMPARE(text2Element.isNull(), false); - QCOMPARE(text2Element.isElement(), true); - QCOMPARE(text2Element.parentNode().isNull(), false); - QCOMPARE(text2Element.parentNode() == bodyElement, true); - QCOMPARE(KoXml::childNodesCount(text2Element), 1); - QCOMPARE(text2Element.firstChild().isNull(), false); - QCOMPARE(text2Element.lastChild().isNull(), false); - QCOMPARE(text2Element.previousSibling().isNull(), true); - QCOMPARE(text2Element.nextSibling().isNull(), true); - QCOMPARE(text2Element.localName(), QString("text")); - - // - KoXmlElement parElement; - parElement = textElement.firstChild().toElement(); - QCOMPARE(parElement.isNull(), false); - QCOMPARE(parElement.isElement(), true); - QCOMPARE(parElement.parentNode().isNull(), false); - QCOMPARE(parElement.parentNode() == textElement, true); - QCOMPARE(KoXml::childNodesCount(parElement), 1); - QCOMPARE(parElement.firstChild().isNull(), false); - QCOMPARE(parElement.lastChild().isNull(), false); - QCOMPARE(parElement.previousSibling().isNull(), true); - QCOMPARE(parElement.nextSibling().isNull(), true); - QCOMPARE(parElement.tagName(), QString("p")); - QCOMPARE(parElement.text(), QString("Hello, world!")); - QCOMPARE(parElement.attributeNS(QString(textNS), "style-name", ""), QString("Standard")); -} - -void TestXmlReaderWithoutSpaces::testWhitespace() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - // content.xml for testing paragraphs with whitespace - /* The list of elements for which whitespace should be preserved can be - obtained from the Relax NG schema with these commands: - - cat OpenDocument-schema-v1.0-os.rng| xmlstarlet sel \ - -N s="http://relaxng.org/ns/structure/1.0" -t -m '//s:text' \ - -v '../@name' -n |grep : - cat OpenDocument-schema-v1.0-os.rng| xmlstarlet sel \ - -N s="http://relaxng.org/ns/structure/1.0" \ - -t -m "//s:ref[@name='paragraph-content']" -v '../../@name' -n |grep : - */ - - xmlstream << ""; - xmlstream << ""; - xmlstream << " "; - xmlstream << " "; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - KoXmlElement p1; - p1 = doc.documentElement().firstChild().toElement(); - QCOMPARE(p1.isNull(), false); - QCOMPARE(p1.isElement(), true); - QCOMPARE(KoXml::childNodesCount(p1), 1); - - KoXmlElement p2; - p2 = p1.nextSibling().toElement(); - QCOMPARE(p2.isNull(), false); - QCOMPARE(p2.isElement(), true); - QCOMPARE(KoXml::childNodesCount(p2), 3); -} - -void TestXmlReaderWithoutSpaces::testSimpleOpenDocumentSpreadsheet() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - // content.xml from a simple OpenDocument spreadsheet - // the document has three worksheets, the last two are empty. - // on the first sheet, cell A1 contains the text "Hello, world". - // automatic styles, font declarations and unnecessary namespaces are omitted. - - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << "Hello, world"; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - QString officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; - QString tableNS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"; - QString textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; - - // - KoXmlElement contentElement; - contentElement = doc.documentElement(); - QCOMPARE(contentElement.isNull(), false); - QCOMPARE(contentElement.isElement(), true); - QCOMPARE(contentElement.parentNode().isNull(), false); - QCOMPARE(contentElement.parentNode().toDocument() == doc, true); - QCOMPARE(KoXml::childNodesCount(contentElement), 1); - QCOMPARE(contentElement.firstChild().isNull(), false); - QCOMPARE(contentElement.lastChild().isNull(), false); - QCOMPARE(contentElement.previousSibling().isNull(), false); - QCOMPARE(contentElement.nextSibling().isNull(), true); - QCOMPARE(contentElement.localName(), QString("document-content")); - - // - KoXmlElement bodyElement; - bodyElement = contentElement.firstChild().toElement(); - QCOMPARE(bodyElement.isNull(), false); - QCOMPARE(bodyElement.isElement(), true); - QCOMPARE(bodyElement.parentNode().isNull(), false); - QCOMPARE(bodyElement.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(bodyElement), 1); - QCOMPARE(bodyElement.firstChild().isNull(), false); - QCOMPARE(bodyElement.lastChild().isNull(), false); - QCOMPARE(bodyElement.previousSibling().isNull(), true); - QCOMPARE(bodyElement.nextSibling().isNull(), true); - QCOMPARE(bodyElement.localName(), QString("body")); - - // - KoXmlElement spreadsheetElement; - spreadsheetElement = bodyElement.firstChild().toElement(); - QCOMPARE(spreadsheetElement.isNull(), false); - QCOMPARE(spreadsheetElement.isElement(), true); - QCOMPARE(spreadsheetElement.parentNode().isNull(), false); - QCOMPARE(spreadsheetElement.parentNode() == bodyElement, true); - QCOMPARE(KoXml::childNodesCount(spreadsheetElement), 3); - QCOMPARE(spreadsheetElement.firstChild().isNull(), false); - QCOMPARE(spreadsheetElement.lastChild().isNull(), false); - QCOMPARE(spreadsheetElement.previousSibling().isNull(), true); - QCOMPARE(spreadsheetElement.nextSibling().isNull(), true); - QCOMPARE(spreadsheetElement.localName(), QString("spreadsheet")); - - // for Sheet1 - KoXmlElement sheet1Element; - sheet1Element = spreadsheetElement.firstChild().toElement(); - QCOMPARE(sheet1Element.isNull(), false); - QCOMPARE(sheet1Element.isElement(), true); - QCOMPARE(sheet1Element.parentNode().isNull(), false); - QCOMPARE(sheet1Element.parentNode() == spreadsheetElement, true); - QCOMPARE(KoXml::childNodesCount(sheet1Element), 2); - QCOMPARE(sheet1Element.firstChild().isNull(), false); - QCOMPARE(sheet1Element.lastChild().isNull(), false); - QCOMPARE(sheet1Element.previousSibling().isNull(), true); - QCOMPARE(sheet1Element.nextSibling().isNull(), false); - QCOMPARE(sheet1Element.tagName(), QString("table")); - QCOMPARE(sheet1Element.hasAttributeNS(tableNS, "name"), true); - QCOMPARE(sheet1Element.attributeNS(tableNS, "name", ""), QString("Sheet1")); - QCOMPARE(sheet1Element.attributeNS(tableNS, "style-name", ""), QString("ta1")); - QCOMPARE(sheet1Element.attributeNS(tableNS, "print", ""), QString("false")); - - // KoXml::load( sheet1Element, 100 ); - - // - KoXmlElement columnElement; - columnElement = sheet1Element.firstChild().toElement(); - QCOMPARE(columnElement.isNull(), false); - QCOMPARE(columnElement.isElement(), true); - QCOMPARE(columnElement.parentNode().isNull(), false); - QCOMPARE(columnElement.parentNode() == sheet1Element, true); - QCOMPARE(KoXml::childNodesCount(columnElement), 0); - QCOMPARE(columnElement.firstChild().isNull(), true); - QCOMPARE(columnElement.lastChild().isNull(), true); - QCOMPARE(columnElement.previousSibling().isNull(), true); - QCOMPARE(columnElement.nextSibling().isNull(), false); - QCOMPARE(columnElement.tagName(), QString("table-column")); - QCOMPARE(columnElement.attributeNS(tableNS, "style-name", ""), QString("co1")); - QCOMPARE(columnElement.attributeNS(tableNS, "default-cell-style-name", ""), QString("Default")); - - // - KoXmlElement rowElement; - rowElement = columnElement.nextSibling().toElement(); - QCOMPARE(rowElement.isNull(), false); - QCOMPARE(rowElement.isElement(), true); - QCOMPARE(rowElement.parentNode().isNull(), false); - QCOMPARE(rowElement.parentNode() == sheet1Element, true); - QCOMPARE(KoXml::childNodesCount(rowElement), 1); - QCOMPARE(rowElement.firstChild().isNull(), false); - QCOMPARE(rowElement.lastChild().isNull(), false); - QCOMPARE(rowElement.previousSibling().isNull(), false); - QCOMPARE(rowElement.nextSibling().isNull(), true); - QCOMPARE(rowElement.tagName(), QString("table-row")); - QCOMPARE(rowElement.attributeNS(tableNS, "style-name", ""), QString("ro1")); - - // - KoXmlElement cellElement; - cellElement = rowElement.firstChild().toElement(); - QCOMPARE(cellElement.isNull(), false); - QCOMPARE(cellElement.isElement(), true); - QCOMPARE(cellElement.parentNode().isNull(), false); - QCOMPARE(cellElement.parentNode() == rowElement, true); - QCOMPARE(KoXml::childNodesCount(cellElement), 1); - QCOMPARE(cellElement.firstChild().isNull(), false); - QCOMPARE(cellElement.lastChild().isNull(), false); - QCOMPARE(cellElement.previousSibling().isNull(), true); - QCOMPARE(cellElement.nextSibling().isNull(), true); - QCOMPARE(cellElement.tagName(), QString("table-cell")); - QCOMPARE(cellElement.attributeNS(officeNS, "value-type", ""), QString("string")); - - // - KoXmlElement parElement; - parElement = cellElement.firstChild().toElement(); - QCOMPARE(parElement.isNull(), false); - QCOMPARE(parElement.isElement(), true); - QCOMPARE(parElement.parentNode().isNull(), false); - QCOMPARE(parElement.parentNode() == cellElement, true); - QCOMPARE(KoXml::childNodesCount(parElement), 1); - QCOMPARE(parElement.firstChild().isNull(), false); - QCOMPARE(parElement.lastChild().isNull(), false); - QCOMPARE(parElement.previousSibling().isNull(), true); - QCOMPARE(parElement.nextSibling().isNull(), true); - QCOMPARE(parElement.tagName(), QString("p")); - QCOMPARE(parElement.text(), QString("Hello, world")); - - // for Sheet2 - KoXmlElement sheet2Element; - sheet2Element = sheet1Element.nextSibling().toElement(); - QCOMPARE(sheet2Element.isNull(), false); - QCOMPARE(sheet2Element.isElement(), true); - QCOMPARE(sheet2Element.parentNode().isNull(), false); - QCOMPARE(sheet2Element.parentNode() == spreadsheetElement, true); - QCOMPARE(KoXml::childNodesCount(sheet2Element), 2); - QCOMPARE(sheet2Element.firstChild().isNull(), false); - QCOMPARE(sheet2Element.lastChild().isNull(), false); - QCOMPARE(sheet2Element.previousSibling().isNull(), false); - QCOMPARE(sheet2Element.nextSibling().isNull(), false); - QCOMPARE(sheet2Element.tagName(), QString("table")); - - // for Sheet3 - KoXmlElement sheet3Element; - sheet3Element = sheet2Element.nextSibling().toElement(); - QCOMPARE(sheet3Element.isNull(), false); - QCOMPARE(sheet3Element.isElement(), true); - QCOMPARE(sheet3Element.parentNode().isNull(), false); - QCOMPARE(sheet3Element.parentNode() == spreadsheetElement, true); - QCOMPARE(KoXml::childNodesCount(sheet3Element), 2); - QCOMPARE(sheet3Element.firstChild().isNull(), false); - QCOMPARE(sheet3Element.lastChild().isNull(), false); - QCOMPARE(sheet3Element.previousSibling().isNull(), false); - QCOMPARE(sheet3Element.nextSibling().isNull(), true); - QCOMPARE(sheet3Element.tagName(), QString("table")); -} - -void TestXmlReaderWithoutSpaces::testSimpleOpenDocumentPresentation() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - // content.xml from a simple OpenDocument presentation - // styles, declarations and unnecessary namespaces are omitted - // the first page is "Title" and has two text boxes - // the second page is - - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << "Foobar"; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << "Foo"; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - const char* officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; - const char* drawNS = "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"; - const char* textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; - const char* presentationNS = "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"; - const char* svgNS = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"; - - // - KoXmlElement contentElement; - contentElement = doc.documentElement(); - QCOMPARE(contentElement.isNull(), false); - - QCOMPARE(contentElement.isElement(), true); - QCOMPARE(contentElement.parentNode().isNull(), false); - QCOMPARE(contentElement.parentNode().toDocument() == doc, true); - QCOMPARE(KoXml::childNodesCount(contentElement), 3); - QCOMPARE(contentElement.firstChild().isNull(), false); - QCOMPARE(contentElement.lastChild().isNull(), false); - QCOMPARE(contentElement.previousSibling().isNull(), false); - QCOMPARE(contentElement.nextSibling().isNull(), true); - QCOMPARE(contentElement.localName(), QString("document-content")); - QCOMPARE(contentElement.hasAttributeNS(officeNS, "version"), true); - QCOMPARE(contentElement.attributeNS(officeNS, "version", ""), QString("1.0")); - - // - KoXmlElement scriptsElement; - scriptsElement = KoXml::namedItemNS(contentElement, officeNS, "scripts"); - QCOMPARE(scriptsElement.isNull(), false); - QCOMPARE(scriptsElement.isElement(), true); - QCOMPARE(scriptsElement.parentNode().isNull(), false); - QCOMPARE(scriptsElement.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(scriptsElement), 0); - QCOMPARE(scriptsElement.firstChild().isNull(), true); - QCOMPARE(scriptsElement.lastChild().isNull(), true); - QCOMPARE(scriptsElement.previousSibling().isNull(), true); - QCOMPARE(scriptsElement.nextSibling().isNull(), false); - QCOMPARE(scriptsElement.localName(), QString("scripts")); - - // - KoXmlElement stylesElement; - stylesElement = KoXml::namedItemNS(contentElement, officeNS, "automatic-styles"); - QCOMPARE(stylesElement.isNull(), false); - QCOMPARE(stylesElement.isElement(), true); - QCOMPARE(stylesElement.parentNode().isNull(), false); - QCOMPARE(stylesElement.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(stylesElement), 0); - QCOMPARE(stylesElement.firstChild().isNull(), true); - QCOMPARE(stylesElement.lastChild().isNull(), true); - QCOMPARE(stylesElement.previousSibling().isNull(), false); - QCOMPARE(stylesElement.nextSibling().isNull(), false); - QCOMPARE(stylesElement.localName(), QString("automatic-styles")); - - // also same , but without namedItemNS - KoXmlNode styles2Element; - styles2Element = scriptsElement.nextSibling().toElement(); - QCOMPARE(styles2Element.isNull(), false); - QCOMPARE(styles2Element.isElement(), true); - QCOMPARE(styles2Element.parentNode().isNull(), false); - QCOMPARE(styles2Element.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(styles2Element), 0); - QCOMPARE(styles2Element.firstChild().isNull(), true); - QCOMPARE(styles2Element.lastChild().isNull(), true); - QCOMPARE(styles2Element.previousSibling().isNull(), false); - QCOMPARE(styles2Element.nextSibling().isNull(), false); - QCOMPARE(styles2Element.localName(), QString("automatic-styles")); - - // - KoXmlElement bodyElement; - bodyElement = KoXml::namedItemNS(contentElement, officeNS, "body"); - QCOMPARE(bodyElement.isNull(), false); - QCOMPARE(bodyElement.isElement(), true); - QCOMPARE(bodyElement.parentNode().isNull(), false); - QCOMPARE(bodyElement.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(bodyElement), 1); - QCOMPARE(bodyElement.firstChild().isNull(), false); - QCOMPARE(bodyElement.lastChild().isNull(), false); - QCOMPARE(bodyElement.previousSibling().isNull(), false); - QCOMPARE(bodyElement.nextSibling().isNull(), true); - QCOMPARE(bodyElement.localName(), QString("body")); - - // also same , but without namedItemNS - KoXmlElement body2Element; - body2Element = stylesElement.nextSibling().toElement(); - QCOMPARE(body2Element.isNull(), false); - QCOMPARE(body2Element.isElement(), true); - QCOMPARE(body2Element.parentNode().isNull(), false); - QCOMPARE(body2Element.parentNode() == contentElement, true); - QCOMPARE(KoXml::childNodesCount(body2Element), 1); - QCOMPARE(body2Element.firstChild().isNull(), false); - QCOMPARE(body2Element.lastChild().isNull(), false); - QCOMPARE(body2Element.previousSibling().isNull(), false); - QCOMPARE(body2Element.nextSibling().isNull(), true); - QCOMPARE(body2Element.localName(), QString("body")); - - // - KoXmlElement presentationElement; - presentationElement = KoXml::namedItemNS(bodyElement, officeNS, "presentation"); - QCOMPARE(presentationElement.isNull(), false); - QCOMPARE(presentationElement.isElement(), true); - QCOMPARE(presentationElement.parentNode().isNull(), false); - QCOMPARE(presentationElement.parentNode() == bodyElement, true); - QCOMPARE(KoXml::childNodesCount(presentationElement), 2); - QCOMPARE(presentationElement.firstChild().isNull(), false); - QCOMPARE(presentationElement.lastChild().isNull(), false); - QCOMPARE(presentationElement.previousSibling().isNull(), true); - QCOMPARE(presentationElement.nextSibling().isNull(), true); - QCOMPARE(presentationElement.localName(), QString("presentation")); - - // the same , but without namedItemNS - KoXmlElement presentation2Element; - presentation2Element = bodyElement.firstChild().toElement(); - QCOMPARE(presentation2Element.isNull(), false); - QCOMPARE(presentation2Element.isElement(), true); - QCOMPARE(presentation2Element.parentNode().isNull(), false); - QCOMPARE(presentation2Element.parentNode() == bodyElement, true); - QCOMPARE(KoXml::childNodesCount(presentation2Element), 2); - QCOMPARE(presentation2Element.firstChild().isNull(), false); - QCOMPARE(presentation2Element.lastChild().isNull(), false); - QCOMPARE(presentation2Element.previousSibling().isNull(), true); - QCOMPARE(presentation2Element.nextSibling().isNull(), true); - QCOMPARE(presentation2Element.localName(), QString("presentation")); - - // for "Title" - KoXmlElement titlePageElement; - titlePageElement = presentationElement.firstChild().toElement(); - QCOMPARE(titlePageElement.isNull(), false); - QCOMPARE(titlePageElement.isElement(), true); - QCOMPARE(titlePageElement.parentNode().isNull(), false); - QCOMPARE(titlePageElement.parentNode() == presentationElement, true); - QCOMPARE(KoXml::childNodesCount(titlePageElement), 3); - QCOMPARE(titlePageElement.firstChild().isNull(), false); - QCOMPARE(titlePageElement.lastChild().isNull(), false); - QCOMPARE(titlePageElement.previousSibling().isNull(), true); - QCOMPARE(titlePageElement.nextSibling().isNull(), false); - QCOMPARE(titlePageElement.localName(), QString("page")); - QCOMPARE(titlePageElement.attributeNS(drawNS, "name", ""), QString("Title")); - QCOMPARE(titlePageElement.attributeNS(drawNS, "style-name", ""), QString("dp1")); - QCOMPARE(titlePageElement.attributeNS(drawNS, "master-page-name", ""), QString("lyt-cool")); - QCOMPARE(titlePageElement.attributeNS(presentationNS, - "presentation-page-layout-name", ""), QString("AL1T0")); - - // for the title frame - KoXmlElement titleFrameElement; - titleFrameElement = titlePageElement.firstChild().toElement(); - QCOMPARE(titleFrameElement.isNull(), false); - QCOMPARE(titleFrameElement.isElement(), true); - QCOMPARE(titleFrameElement.parentNode().isNull(), false); - QCOMPARE(titleFrameElement.parentNode() == titlePageElement, true); - QCOMPARE(KoXml::childNodesCount(titleFrameElement), 1); - QCOMPARE(titleFrameElement.firstChild().isNull(), false); - QCOMPARE(titleFrameElement.lastChild().isNull(), false); - QCOMPARE(titleFrameElement.previousSibling().isNull(), true); - QCOMPARE(titleFrameElement.nextSibling().isNull(), false); - QCOMPARE(titleFrameElement.localName(), QString("frame")); - QCOMPARE(titleFrameElement.attributeNS(presentationNS, "style-name", ""), QString("pr1")); - QCOMPARE(titleFrameElement.attributeNS(presentationNS, "class", ""), QString("title")); - QCOMPARE(titleFrameElement.attributeNS(presentationNS, "user-transformed", ""), QString("true")); - QCOMPARE(titleFrameElement.attributeNS(drawNS, "text-style-name", ""), QString("P2")); - QCOMPARE(titleFrameElement.attributeNS(drawNS, "layer", ""), QString("layout")); - QCOMPARE(titleFrameElement.attributeNS(svgNS, "width", ""), QString("23.912cm")); - QCOMPARE(titleFrameElement.attributeNS(svgNS, "height", ""), QString("3.508cm")); - QCOMPARE(titleFrameElement.attributeNS(svgNS, "x", ""), QString("2.058cm")); - QCOMPARE(titleFrameElement.attributeNS(svgNS, "y", ""), QString("1.543cm")); - - // of the title frame - KoXmlElement titleBoxElement; - titleBoxElement = titleFrameElement.firstChild().toElement(); - QCOMPARE(titleBoxElement.isNull(), false); - QCOMPARE(titleBoxElement.isElement(), true); - QCOMPARE(titleBoxElement.parentNode().isNull(), false); - QCOMPARE(titleBoxElement.parentNode() == titleFrameElement, true); - QCOMPARE(KoXml::childNodesCount(titleBoxElement), 1); - QCOMPARE(titleBoxElement.firstChild().isNull(), false); - QCOMPARE(titleBoxElement.lastChild().isNull(), false); - QCOMPARE(titleBoxElement.previousSibling().isNull(), true); - QCOMPARE(titleBoxElement.nextSibling().isNull(), true); - QCOMPARE(titleBoxElement.localName(), QString("text-box")); - - // for the title text-box - KoXmlElement titleParElement; - titleParElement = titleBoxElement.firstChild().toElement(); - QCOMPARE(titleParElement.isNull(), false); - QCOMPARE(titleParElement.isElement(), true); - QCOMPARE(titleParElement.parentNode().isNull(), false); - QCOMPARE(titleParElement.parentNode() == titleBoxElement, true); - QCOMPARE(KoXml::childNodesCount(titleParElement), 1); - QCOMPARE(titleParElement.firstChild().isNull(), false); - QCOMPARE(titleParElement.lastChild().isNull(), false); - QCOMPARE(titleParElement.previousSibling().isNull(), true); - QCOMPARE(titleParElement.nextSibling().isNull(), true); - QCOMPARE(titleParElement.localName(), QString("p")); - QCOMPARE(titleParElement.attributeNS(textNS, "style-name", ""), QString("P1")); - QCOMPARE(titleParElement.text(), QString("Foobar")); - - // for the subtitle frame - KoXmlElement subtitleFrameElement; - subtitleFrameElement = titleFrameElement.nextSibling().toElement(); - QCOMPARE(subtitleFrameElement.isNull(), false); - QCOMPARE(subtitleFrameElement.isElement(), true); - QCOMPARE(subtitleFrameElement.parentNode().isNull(), false); - QCOMPARE(subtitleFrameElement.parentNode() == titlePageElement, true); - QCOMPARE(KoXml::childNodesCount(subtitleFrameElement), 1); - QCOMPARE(subtitleFrameElement.firstChild().isNull(), false); - QCOMPARE(subtitleFrameElement.lastChild().isNull(), false); - QCOMPARE(subtitleFrameElement.previousSibling().isNull(), false); - QCOMPARE(subtitleFrameElement.nextSibling().isNull(), false); - QCOMPARE(subtitleFrameElement.localName(), QString("frame")); - QCOMPARE(subtitleFrameElement.attributeNS(presentationNS, "style-name", ""), QString("pr2")); - QCOMPARE(subtitleFrameElement.attributeNS(presentationNS, "class", ""), QString("subtitle")); - QCOMPARE(subtitleFrameElement.hasAttributeNS(presentationNS, "user-transformed"), false); - QCOMPARE(subtitleFrameElement.attributeNS(drawNS, "text-style-name", ""), QString("P3")); - QCOMPARE(subtitleFrameElement.attributeNS(drawNS, "layer", ""), QString("layout")); - QCOMPARE(subtitleFrameElement.attributeNS(svgNS, "width", ""), QString("23.912cm")); - QCOMPARE(subtitleFrameElement.attributeNS(svgNS, "height", ""), QString("13.231cm")); - QCOMPARE(subtitleFrameElement.attributeNS(svgNS, "x", ""), QString("2.058cm")); - QCOMPARE(subtitleFrameElement.attributeNS(svgNS, "y", ""), QString("5.838cm")); - - // of the subtitle frame - KoXmlElement subtitleBoxElement; - subtitleBoxElement = subtitleFrameElement.firstChild().toElement(); - QCOMPARE(subtitleBoxElement.isNull(), false); - QCOMPARE(subtitleBoxElement.isElement(), true); - QCOMPARE(subtitleBoxElement.parentNode().isNull(), false); - QCOMPARE(subtitleBoxElement.parentNode() == subtitleFrameElement, true); - QCOMPARE(KoXml::childNodesCount(subtitleBoxElement), 1); - QCOMPARE(subtitleBoxElement.firstChild().isNull(), false); - QCOMPARE(subtitleBoxElement.lastChild().isNull(), false); - QCOMPARE(subtitleBoxElement.previousSibling().isNull(), true); - QCOMPARE(subtitleBoxElement.nextSibling().isNull(), true); - QCOMPARE(subtitleBoxElement.localName(), QString("text-box")); - - // for the subtitle text-box - KoXmlElement subtitleParElement; - subtitleParElement = subtitleBoxElement.firstChild().toElement(); - QCOMPARE(subtitleParElement.isNull(), false); - QCOMPARE(subtitleParElement.isElement(), true); - QCOMPARE(subtitleParElement.parentNode().isNull(), false); - QCOMPARE(subtitleParElement.parentNode() == subtitleBoxElement, true); - QCOMPARE(KoXml::childNodesCount(subtitleParElement), 1); - QCOMPARE(subtitleParElement.firstChild().isNull(), false); - QCOMPARE(subtitleParElement.lastChild().isNull(), false); - QCOMPARE(subtitleParElement.previousSibling().isNull(), true); - QCOMPARE(subtitleParElement.nextSibling().isNull(), true); - QCOMPARE(subtitleParElement.localName(), QString("p")); - QCOMPARE(subtitleParElement.attributeNS(textNS, "style-name", ""), QString("P3")); - QCOMPARE(subtitleParElement.text(), QString("Foo")); -} - -void TestXmlReaderWithoutSpaces::testSimpleOpenDocumentFormula() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - // content.xml from a simple OpenDocument formula - // this is essentially MathML - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmlstream << "E"; - xmlstream << "="; - xmlstream << ""; - xmlstream << "mc"; - xmlstream << "2"; - xmlstream << ""; - xmlstream << ""; - xmlstream << "E = mc^2 "; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - KoXmlDocument doc(false); - QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - const char* mathNS = "http://www.w3.org/1998/Math/MathML"; - - // - KoXmlElement mathElement; - mathElement = doc.documentElement(); - QCOMPARE(mathElement.isNull(), false); - QCOMPARE(mathElement.isElement(), true); - QCOMPARE(mathElement.parentNode().isNull(), false); - QCOMPARE(mathElement.parentNode().toDocument() == doc, true); - QCOMPARE(mathElement.firstChild().isNull(), false); - QCOMPARE(mathElement.lastChild().isNull(), false); - QCOMPARE(mathElement.previousSibling().isNull(), false); - QCOMPARE(mathElement.nextSibling().isNull(), true); - QCOMPARE(mathElement.localName(), QString("math")); - - // - KoXmlElement semanticsElement; - semanticsElement = KoXml::namedItemNS(mathElement, mathNS, "semantics"); - QCOMPARE(semanticsElement.isNull(), false); - QCOMPARE(semanticsElement.isElement(), true); - QCOMPARE(semanticsElement.parentNode().isNull(), false); - QCOMPARE(semanticsElement.parentNode().toElement() == mathElement, true); - QCOMPARE(semanticsElement.firstChild().isNull(), false); - QCOMPARE(semanticsElement.lastChild().isNull(), false); - QCOMPARE(semanticsElement.previousSibling().isNull(), true); - QCOMPARE(semanticsElement.nextSibling().isNull(), true); - QCOMPARE(semanticsElement.localName(), QString("semantics")); - - // the same but without namedItemNS - KoXmlElement semantics2Element; - semantics2Element = mathElement.firstChild().toElement(); - QCOMPARE(semantics2Element.isNull(), false); - QCOMPARE(semantics2Element.isElement(), true); - QCOMPARE(semantics2Element.parentNode().isNull(), false); - QCOMPARE(semantics2Element.parentNode().toElement() == mathElement, true); - QCOMPARE(semantics2Element.firstChild().isNull(), false); - QCOMPARE(semantics2Element.lastChild().isNull(), false); - QCOMPARE(semantics2Element.previousSibling().isNull(), true); - QCOMPARE(semantics2Element.nextSibling().isNull(), true); - QCOMPARE(semantics2Element.localName(), QString("semantics")); - - // - KoXmlElement mrowElement; - mrowElement = semanticsElement.firstChild().toElement(); - QCOMPARE(mrowElement.isNull(), false); - QCOMPARE(mrowElement.isElement(), true); - QCOMPARE(mrowElement.parentNode().isNull(), false); - QCOMPARE(mrowElement.parentNode().toElement() == semanticsElement, true); - QCOMPARE(mrowElement.firstChild().isNull(), false); - QCOMPARE(mrowElement.lastChild().isNull(), false); - QCOMPARE(mrowElement.previousSibling().isNull(), true); - QCOMPARE(mrowElement.nextSibling().isNull(), false); - QCOMPARE(mrowElement.localName(), QString("mrow")); - - // for "E" - KoXmlElement miElement; - miElement = mrowElement.firstChild().toElement(); - QCOMPARE(miElement.isNull(), false); - QCOMPARE(miElement.isElement(), true); - QCOMPARE(miElement.parentNode().isNull(), false); - QCOMPARE(miElement.parentNode().toElement() == mrowElement, true); - QCOMPARE(miElement.firstChild().isNull(), false); - QCOMPARE(miElement.lastChild().isNull(), false); - QCOMPARE(miElement.previousSibling().isNull(), true); - QCOMPARE(miElement.nextSibling().isNull(), false); - QCOMPARE(miElement.localName(), QString("mi")); - - // for "=" - KoXmlElement moElement; - moElement = miElement.nextSibling().toElement(); - QCOMPARE(moElement.isNull(), false); - QCOMPARE(moElement.isElement(), true); - QCOMPARE(moElement.parentNode().isNull(), false); - QCOMPARE(moElement.parentNode().toElement() == mrowElement, true); - QCOMPARE(moElement.firstChild().isNull(), false); - QCOMPARE(moElement.lastChild().isNull(), false); - QCOMPARE(moElement.previousSibling().isNull(), false); - QCOMPARE(moElement.nextSibling().isNull(), false); - QCOMPARE(moElement.localName(), QString("mo")); - QCOMPARE(moElement.attributeNS(mathNS, "stretchy", ""), QString("false")); - - // for "mc" and superscripted "2" - KoXmlElement msupElement; - msupElement = moElement.nextSibling().toElement(); - QCOMPARE(msupElement.isNull(), false); - QCOMPARE(msupElement.isElement(), true); - QCOMPARE(msupElement.parentNode().isNull(), false); - QCOMPARE(msupElement.parentNode().toElement() == mrowElement, true); - QCOMPARE(msupElement.firstChild().isNull(), false); - QCOMPARE(msupElement.lastChild().isNull(), false); - QCOMPARE(msupElement.previousSibling().isNull(), false); - QCOMPARE(msupElement.nextSibling().isNull(), true); - QCOMPARE(msupElement.localName(), QString("msup")); - - // inside the for "mc" - KoXmlElement mcElement; - mcElement = msupElement.firstChild().toElement(); - QCOMPARE(mcElement.isNull(), false); - QCOMPARE(mcElement.isElement(), true); - QCOMPARE(mcElement.parentNode().isNull(), false); - QCOMPARE(mcElement.parentNode().toElement() == msupElement, true); - QCOMPARE(mcElement.firstChild().isNull(), false); - QCOMPARE(mcElement.lastChild().isNull(), false); - QCOMPARE(mcElement.previousSibling().isNull(), true); - QCOMPARE(mcElement.nextSibling().isNull(), false); - QCOMPARE(mcElement.localName(), QString("mi")); - QCOMPARE(mcElement.text(), QString("mc")); - QCOMPARE(mcElement.attributeNS(mathNS, "fontstyle", ""), QString("italic")); - - // inside the for "2" (superscript) - KoXmlElement mnElement; - mnElement = mcElement.nextSibling().toElement(); - QCOMPARE(mnElement.isNull(), false); - QCOMPARE(mnElement.isElement(), true); - QCOMPARE(mnElement.parentNode().isNull(), false); - QCOMPARE(mnElement.parentNode().toElement() == msupElement, true); - QCOMPARE(mnElement.firstChild().isNull(), false); - QCOMPARE(mnElement.lastChild().isNull(), false); - QCOMPARE(mnElement.previousSibling().isNull(), false); - QCOMPARE(mnElement.nextSibling().isNull(), true); - QCOMPARE(mnElement.localName(), QString("mn")); - QCOMPARE(mnElement.text(), QString("2")); - - // - KoXmlElement annotationElement; - annotationElement = semanticsElement.lastChild().toElement(); - QCOMPARE(annotationElement.isNull(), false); - QCOMPARE(annotationElement.isElement(), true); - QCOMPARE(annotationElement.parentNode().isNull(), false); - QCOMPARE(annotationElement.parentNode().toElement() == semanticsElement, true); - QCOMPARE(annotationElement.firstChild().isNull(), false); - QCOMPARE(annotationElement.lastChild().isNull(), false); - QCOMPARE(annotationElement.previousSibling().isNull(), false); - QCOMPARE(annotationElement.nextSibling().isNull(), true); - QCOMPARE(annotationElement.localName(), QString("annotation")); - QCOMPARE(annotationElement.text(), QString("E = mc^2 ")); - QCOMPARE(annotationElement.attributeNS(mathNS, "encoding", ""), QString("StarMath 5.0")); -} - -void TestXmlReaderWithoutSpaces::testLargeOpenDocumentSpreadsheet() -{ - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - int sheetCount = 4; - int rowCount = 200; - int colCount = 200 / 16; - - QBuffer xmldevice; - xmldevice.open(QIODevice::WriteOnly); - QTextStream xmlstream(&xmldevice); - - // content.xml - xmlstream << "\n"; - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - for (int i = 0; i < sheetCount; i++) { - QString sheetName = QString("Sheet%1").arg(i + 1); - xmlstream << ""; - for (int j = 0; j < rowCount; j++) { - xmlstream << ""; - for (int k = 0; k < colCount; k++) { - xmlstream << ""; - xmlstream << "Hello, world"; - xmlstream << ""; - } - xmlstream << ""; - } - xmlstream << ""; - } - xmlstream << ""; - xmlstream << ""; - xmlstream << ""; - xmldevice.close(); - - printf("Raw XML size: %lld KB\n", xmldevice.size() / 1024); - - - QTime timer; - -#if 0 - // just to test parsing speed with plain dumb handler - QXmlStreamReader *reader = new QXmlStreamReader(xmldevice); - reader->setNamespaceProcessing(true); - timer.start(); - ParseError error = parseDocument(*reader, doc); - printf("Large spreadsheet: QXmlStreamReader parsing time is %d ms\n", timer.elapsed()); - delete reader; - xmldevice.seek(0); -#endif - - KoXmlDocument doc(false); - - timer.start(); - QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - if (!errorMsg.isEmpty()) { - qDebug("Error: %s", qPrintable(errorMsg)); - return; - } - - printf("Large spreadsheet: KoXmlDocument parsing time is %d ms\n", timer.elapsed()); - - // release memory taken by the XML document content - //xmlstream.setDevice( 0 ); - - // namespaces that will be used - QString officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; - QString tableNS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"; - QString textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; - - // - KoXmlElement contentElement; - contentElement = doc.documentElement(); - QCOMPARE(contentElement.isNull(), false); - QCOMPARE(contentElement.isElement(), true); - QCOMPARE(contentElement.localName(), QString("document-content")); - - // - KoXmlElement bodyElement; - bodyElement = contentElement.firstChild().toElement(); - QCOMPARE(bodyElement.isNull(), false); - QCOMPARE(bodyElement.isElement(), true); - QCOMPARE(bodyElement.localName(), QString("body")); - - // - KoXmlElement spreadsheetElement; - spreadsheetElement = bodyElement.firstChild().toElement(); - QCOMPARE(spreadsheetElement.isNull(), false); - QCOMPARE(spreadsheetElement.isElement(), true); - QCOMPARE(spreadsheetElement.localName(), QString("spreadsheet")); - - // now we visit every sheet, every row, every cell - timer.start(); - KoXmlElement tableElement; - tableElement = spreadsheetElement.firstChild().toElement(); - for (int table = 0; table < sheetCount; table++) { - QString tableName = QString("Sheet%1").arg(table + 1); - QCOMPARE(tableElement.isNull(), false); - QCOMPARE(tableElement.isElement(), true); - QCOMPARE(tableElement.localName(), QString("table")); - QCOMPARE(tableElement.hasAttributeNS(tableNS, "name"), true); - QCOMPARE(tableElement.attributeNS(tableNS, "name", ""), tableName); - QCOMPARE(tableElement.attributeNS(tableNS, "print", ""), QString("false")); - - // load everything for this table - //KoXml::load( tableElement, 99 ); - - QCOMPARE(tableElement.parentNode().isNull(), false); - QCOMPARE(tableElement.parentNode() == spreadsheetElement, true); - QCOMPARE(tableElement.firstChild().isNull(), false); - QCOMPARE(tableElement.lastChild().isNull(), false); - - KoXmlElement rowElement; - rowElement = tableElement.firstChild().toElement(); - for (int row = 0; row < rowCount; row++) { - QCOMPARE(rowElement.isNull(), false); - QCOMPARE(rowElement.isElement(), true); - QCOMPARE(rowElement.localName(), QString("table-row")); - QCOMPARE(rowElement.parentNode().isNull(), false); - QCOMPARE(rowElement.parentNode() == tableElement, true); - QCOMPARE(rowElement.firstChild().isNull(), false); - QCOMPARE(rowElement.lastChild().isNull(), false); - - KoXmlElement cellElement; - cellElement = rowElement.firstChild().toElement(); - for (int col = 0; col < colCount; col++) { - QCOMPARE(cellElement.isNull(), false); - QCOMPARE(cellElement.isElement(), true); - QCOMPARE(cellElement.localName(), QString("table-cell")); - QCOMPARE(cellElement.text(), QString("Hello, world")); - QCOMPARE(cellElement.hasAttributeNS(officeNS, "value-type"), true); - QCOMPARE(cellElement.attributeNS(officeNS, "value-type", ""), QString("string")); - QCOMPARE(cellElement.parentNode().isNull(), false); - QCOMPARE(cellElement.parentNode() == rowElement, true); - QCOMPARE(cellElement.firstChild().isNull(), false); - QCOMPARE(cellElement.lastChild().isNull(), false); - cellElement = cellElement.nextSibling().toElement(); - } - - //KoXml::unload( rowElement ); - rowElement = rowElement.nextSibling().toElement(); - } - - KoXml::unload(tableElement); - tableElement = tableElement.nextSibling().toElement(); - } - - printf("Large spreadsheet: iterating time is %d ms\n", timer.elapsed()); -} - -void TestXmlReaderWithoutSpaces::testExternalOpenDocumentSpreadsheet(const QString& filename) -{ - QProcess unzip; - QStringList arguments; - arguments << "-o" << filename << "content.xml"; - - printf("Unzipping content.xml from %s...\n", qPrintable(filename)); - - unzip.start("unzip", arguments); - if (!unzip.waitForStarted()) { - printf("Error: can't invoke unzip. Check your PATH and installation!\n\n"); - return; - } - - if (!unzip.waitForFinished()) { - printf("Error: unzip failed, can't continue!\n\n"); - return; - } - - printf("Procesing content.xml....\n"); - - QString errorMsg; - int errorLine = 0; - int errorColumn = 0; - - QFile xmlfile("content.xml"); - if (!xmlfile.open(QFile::ReadOnly)) { - printf("Can not open file '%s'\n", qPrintable(filename)); - return; - } - - printf("Test external file: %s %lld KB\n", qPrintable(filename), xmlfile.size() / 1024); - - QTime timer; - timer.start(); - - KoXmlDocument doc(false); - - QCOMPARE(KoXml::setDocument(doc, &xmlfile, true, &errorMsg, &errorLine, &errorColumn), true); - QCOMPARE(errorMsg.isEmpty(), true); - QCOMPARE(errorLine, 0); - QCOMPARE(errorColumn, 0); - - printf("External spreadsheet: parsing time is %d ms\n", timer.elapsed()); - - // namespaces that will be used - QString officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; - QString tableNS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"; - QString textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; - - // - KoXmlElement contentElement; - contentElement = doc.documentElement(); - QCOMPARE(contentElement.isNull(), false); - QCOMPARE(contentElement.isElement(), true); - QCOMPARE(contentElement.localName(), QString("document-content")); - - long totalCellCount = 0; - - KoXmlElement bodyElement; - forEachElement(bodyElement, contentElement) { - // - if (bodyElement.localName() != QString("body")) - continue; - - // now we iterate inside the body - timer.start(); - - // - KoXmlElement spreadsheetElement; - spreadsheetElement = bodyElement.firstChild().toElement(); - QCOMPARE(spreadsheetElement.isNull(), false); - QCOMPARE(spreadsheetElement.isElement(), true); - QCOMPARE(spreadsheetElement.localName(), QString("spreadsheet")); - - // now we visit every sheet - long tableCount = -1; - KoXmlElement tableElement; - tableElement = spreadsheetElement.firstChild().toElement(); - for (;;) { - if (tableElement.isNull()) - break; - - if (tableElement.localName() != QString("table")) { - tableElement = tableElement.nextSibling().toElement(); - continue; - } - - QString tableName = tableElement.attributeNS(tableNS, "name", ""); - tableCount++; - - printf(" sheet #%ld (%s): ", tableCount + 1, qPrintable(tableName)); - - // use to preload everything in this sheet, will slow it down! - // KoXml::load( tableElement, 50 ); - - long rowCount = -1; - long cellCount = -1; - - KoXmlElement rowElement; - rowElement = tableElement.firstChild().toElement(); - for (;;) { - if (rowElement.isNull()) - break; - - if (rowElement.localName() != QString("table-row")) { - rowElement = rowElement.nextSibling().toElement(); - continue; - } - - rowCount++; - KoXml::load(rowElement, 4); - - QCOMPARE(rowElement.isElement(), true); - QCOMPARE(rowElement.localName(), QString("table-row")); - QCOMPARE(rowElement.parentNode().isNull(), false); - QCOMPARE(rowElement.parentNode() == tableElement, true); - - KoXmlElement cellElement; - cellElement = rowElement.firstChild().toElement(); - for (; ;) { - if (cellElement.isNull()) - break; - - if (cellElement.localName() != QString("table-cell")) { - cellElement = cellElement.nextSibling().toElement(); - continue; - } - - cellCount++; - - QCOMPARE(cellElement.isNull(), false); - QCOMPARE(cellElement.isElement(), true); - QCOMPARE(cellElement.localName(), QString("table-cell")); - QString text1 = cellElement.text(); - QString text2 = cellElement.text(); - QCOMPARE(text1, text2); - QString type1 = cellElement.attributeNS(officeNS, "value-type", QString()); - QString type2 = cellElement.attributeNS(officeNS, "value-type", QString()); - QCOMPARE(type1, type2); - QString style1 = cellElement.attributeNS(tableNS, "style-name", QString()); - QString style2 = cellElement.attributeNS(tableNS, "style-name", QString()); - QCOMPARE(style1, style2); - - QCOMPARE(cellElement.parentNode().isNull(), false); - QCOMPARE(cellElement.parentNode() == rowElement, true); - - cellElement = cellElement.nextSibling().toElement(); - } - - - // better not to unload, freeing memory takes time - KoXml::unload(rowElement); - - rowElement = rowElement.nextSibling().toElement(); - } - - printf(" %ld rows, %ld cells\n", rowCount + 1, cellCount + 1); - totalCellCount += (cellCount + 1); - - // IMPORTANT: helps minimizing memory usage !! - // we do not need that element anymore, so just throw it away - KoXml::unload(tableElement); - - tableElement = tableElement.nextSibling().toElement(); - } - - KoXml::unload(spreadsheetElement); - } - - printf("Total number of cells: %ld\n", totalCellCount); - - int elapsed = timer.elapsed(); - printf("External spreadsheet: iterating time is %d ms\n", elapsed); - if (elapsed > 0) - printf(" approx. %ld cells/second\n", totalCellCount*1000 / elapsed); - - // uncomment to check the XML - xmlfile.remove(); -} -#endif - -QTEST_GUILESS_MAIN(TestXmlReaderWithoutSpaces) -#include - diff --git a/libs/store/KoXmlReader.cpp b/libs/store/KoXmlReader.cpp index 7a6c626992..7890266284 100644 --- a/libs/store/KoXmlReader.cpp +++ b/libs/store/KoXmlReader.cpp @@ -1,2349 +1,106 @@ /* This file is part of the KDE project Copyright (C) 2005-2006 Ariya Hidayat 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 "KoXmlReader.h" #include "KoXmlNS.h" -/* - This is a memory-efficient DOM implementation for Calligra. See the API - documentation for details. - - IMPORTANT ! - - * When you change this stuff, make sure it DOES NOT BREAK the test suite. - Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights - have been sacrificed for this piece of code, do not let those precious - hours wasted! - - * Run koxmlreadertest.cpp WITH Valgrind and make sure NO illegal - memory read/write and any type of leak occurs. If you are not familiar - with Valgrind then RTFM first and come back again later on. - - * The public API shall remain as compatible as QDom. - - * All QDom-compatible methods should behave the same. All QDom-compatible - functions should return the same result. In case of doubt, run - koxmlreadertest.cpp but uncomment KOXML_USE_QDOM in koxmlreader.h - so that the tests are performed with standard QDom. - - Some differences compared to QDom: - - - DOM tree in KoXmlDocument is read-only, you can not modify it. This is - sufficient for Calligra since the tree is only accessed when loading - a document to the application. For saving the document to XML file, - use KoXmlWriter. - - - Because the dynamic loading and unloading, you have to use the - nodes (and therefore also elements) carefully since the whole API - (just like QDom) is reference-based, not pointer-based. If the - parent node is unloaded from memory, the reference is not valid - anymore and may give unpredictable result. - The easiest way: use the node/element in very short time only. - - - Comment node (like QDomComment) is not implemented as comments are - simply ignored. - - - DTD, entity and entity reference are not handled. Thus, the associated - nodes (like QDomDocumentType, QDomEntity, QDomEntityReference) are also - not implemented. - - - Attribute mapping node is not implemented. But of course, functions to - query attributes of an element are available. - - - */ - -#include -#include -#include - -#ifndef KOXML_USE_QDOM - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/* - Use more compact representation of in-memory nodes. - - Advantages: faster iteration, can facilitate real-time compression. - Disadvantages: still buggy, eat slightly more memory. -*/ -#define KOXML_COMPACT - -/* - Use real-time compression. Only works in conjuction with KOXML_COMPACT - above because otherwise the non-compact layout will slow down everything. -*/ -#define KOXML_COMPRESS - - -// prevent mistake, see above -#ifdef KOXML_COMPRESS -#ifndef KOXML_COMPACT -#error Please enable also KOXML_COMPACT -#endif -#endif - -// this is used to quickly get namespaced attribute(s) -typedef QPair KoXmlStringPair; - -class KoQName { -public: - QString nsURI; - QString name; - - explicit KoQName(const QString& nsURI_, const QString& name_) - : nsURI(nsURI_), name(name_) {} - bool operator==(const KoQName& qname) const { - // local name is more likely to differ, so compare that first - return name == qname.name && nsURI == qname.nsURI; - } -}; - -uint qHash(const KoQName& qname) -{ - // possibly add a faster hash function that only includes some trailing - // part of the nsURI - - // in case of doubt, use this: - // return qHash(qname.nsURI)^qHash(qname.name); - return qHash(qname.nsURI)^qHash(qname.name); -} - -static inline bool operator==(const KoXmlStringPair &a, const KoXmlStringPair &b) -{ - return a.second == b.second && a.first == b.first; -} - -// Older versions of OpenOffice.org used different namespaces. This function -// does translate the old namespaces into the new ones. -static QString fixNamespace(const QString &nsURI) -{ - static QString office = QString::fromLatin1("http://openoffice.org/2000/office"); - static QString text = QString::fromLatin1("http://openoffice.org/2000/text"); - static QString style = QString::fromLatin1("http://openoffice.org/2000/style"); - static QString fo = QString::fromLatin1("http://www.w3.org/1999/XSL/Format"); - static QString table = QString::fromLatin1("http://openoffice.org/2000/table"); - static QString drawing = QString::fromLatin1("http://openoffice.org/2000/drawing"); - static QString datastyle = QString::fromLatin1("http://openoffice.org/2000/datastyle"); - static QString svg = QString::fromLatin1("http://www.w3.org/2000/svg"); - static QString chart = QString::fromLatin1("http://openoffice.org/2000/chart"); - static QString dr3d = QString::fromLatin1("http://openoffice.org/2000/dr3d"); - static QString form = QString::fromLatin1("http://openoffice.org/2000/form"); - static QString script = QString::fromLatin1("http://openoffice.org/2000/script"); - static QString meta = QString::fromLatin1("http://openoffice.org/2000/meta"); - static QString config = QString::fromLatin1("http://openoffice.org/2001/config"); - static QString pres = QString::fromLatin1("http://openoffice.org/2000/presentation"); - static QString manifest = QString::fromLatin1("http://openoffice.org/2001/manifest"); - if (nsURI == text) - return KoXmlNS::text; - if (nsURI == style) - return KoXmlNS::style; - if (nsURI == office) - return KoXmlNS::office; - if (nsURI == fo) - return KoXmlNS::fo; - if (nsURI == table) - return KoXmlNS::table; - if (nsURI == drawing) - return KoXmlNS::draw; - if (nsURI == datastyle) - return KoXmlNS::number; - if (nsURI == svg) - return KoXmlNS::svg; - if (nsURI == chart) - return KoXmlNS::chart; - if (nsURI == dr3d) - return KoXmlNS::dr3d; - if (nsURI == form) - return KoXmlNS::form; - if (nsURI == script) - return KoXmlNS::script; - if (nsURI == meta) - return KoXmlNS::meta; - if (nsURI == config) - return KoXmlNS::config; - if (nsURI == pres) - return KoXmlNS::presentation; - if (nsURI == manifest) - return KoXmlNS::manifest; - return nsURI; -} - -// ================================================================== -// -// KoXmlPackedItem -// -// ================================================================== - -// 12 bytes on most system 32 bit systems, 16 bytes on 64 bit systems -class KoXmlPackedItem -{ -public: -bool attr: 1; -KoXmlNode::NodeType type: 3; - -#ifdef KOXML_COMPACT -quint32 childStart: 28; -#else -unsigned depth: 28; -#endif - - unsigned qnameIndex; - QString value; - - // it is important NOT to have a copy constructor, so that growth is optimal - // see http://doc.trolltech.com/4.2/containers.html#growth-strategies -#if 0 - KoXmlPackedItem(): attr(false), type(KoXmlNode::NullNode), childStart(0), depth(0) {} -#endif -}; - -Q_DECLARE_TYPEINFO(KoXmlPackedItem, Q_MOVABLE_TYPE); - -#ifdef KOXML_COMPRESS -static QDataStream& operator<<(QDataStream& s, const KoXmlPackedItem& item) -{ - quint8 flag = item.attr ? 1 : 0; - - s << flag; - s << (quint8) item.type; - s << item.childStart; - s << item.qnameIndex; - s << item.value; - - return s; -} - -static QDataStream& operator>>(QDataStream& s, KoXmlPackedItem& item) -{ - quint8 flag; - quint8 type; - quint32 child; - QString value; - - s >> flag; - s >> type; - s >> child; - s >> item.qnameIndex; - s >> value; - - item.attr = (flag != 0); - item.type = (KoXmlNode::NodeType) type; - item.childStart = child; - item.value = value; - - return s; -} -#endif - -// ================================================================== -// -// KoXmlPackedDocument -// -// ================================================================== - -#ifdef KOXML_COMPRESS - -#include "KoXmlVector.h" - -// when number of buffered items reach this, compression will start -// small value will give better memory usage at the cost of speed -// bigger value will be better in term of speed, but use more memory -#define ITEMS_FULL (1*256) - -typedef KoXmlVector KoXmlPackedGroup; -#else -typedef QVector KoXmlPackedGroup; -#endif - -// growth strategy: increase every GROUP_GROW_SIZE items -// this will override standard QVector's growth strategy -#define GROUP_GROW_SHIFT 3 -#define GROUP_GROW_SIZE (1 << GROUP_GROW_SHIFT) - -class KoXmlPackedDocument -{ -public: - bool processNamespace; -#ifdef KOXML_COMPACT - // map given depth to the list of items - QHash groups; -#else - QVector items; -#endif - - QList qnameList; - QString docType; - -private: - QHash qnameHash; - - unsigned cacheQName(const QString& name, const QString& nsURI) { - KoQName qname(nsURI, name); - - const unsigned ii = qnameHash.value(qname, (unsigned)-1); - if (ii != (unsigned)-1) - return ii; - - // not yet declared, so we add it - unsigned i = qnameList.count(); - qnameList.append(qname); - qnameHash.insert(qname, i); - - return i; - } - - QHash valueHash; - QStringList valueList; - - QString cacheValue(const QString& value) { - if (value.isEmpty()) - return 0; - - const unsigned& ii = valueHash[value]; - if (ii > 0) - return valueList[ii]; - - // not yet declared, so we add it - unsigned i = valueList.count(); - valueList.append(value); - valueHash.insert(value, i); - - return valueList[i]; - } - -#ifdef KOXML_COMPACT -public: - const KoXmlPackedItem& itemAt(unsigned depth, unsigned index) { - const KoXmlPackedGroup& group = groups[depth]; - return group[index]; - } - - unsigned itemCount(unsigned depth) { - const KoXmlPackedGroup& group = groups[depth]; - return group.count(); - } - - /* - NOTE: - Function clear, newItem, addElement, addAttribute, addText, - addCData, addProcessing are all related. These are all necessary - for stateful manipulation of the document. See also the calls - to these function from parseDocument(). - - The state itself is defined by the member variables - currentDepth and the groups (see above). - */ - - unsigned currentDepth; - - KoXmlPackedItem& newItem(unsigned depth) { - KoXmlPackedGroup& group = groups[depth]; - -#ifdef KOXML_COMPRESS - KoXmlPackedItem& item = group.newItem(); -#else - // reserve up front - if ((groups.size() % GROUP_GROW_SIZE) == 0) - group.reserve(GROUP_GROW_SIZE * (1 + (groups.size() >> GROUP_GROW_SHIFT))); - group.resize(group.count() + 1); - - KoXmlPackedItem& item = group[group.count()-1]; -#endif - - // this is necessary, because intentionally we don't want to have - // a constructor for KoXmlPackedItem - item.attr = false; - item.type = KoXmlNode::NullNode; - item.qnameIndex = 0; - item.childStart = itemCount(depth + 1); - item.value.clear(); - - return item; - } - - void clear() { - currentDepth = 0; - qnameHash.clear(); - qnameList.clear(); - valueHash.clear(); - valueList.clear(); - groups.clear(); - docType.clear(); - - // first node is root - KoXmlPackedItem& rootItem = newItem(0); - rootItem.type = KoXmlNode::DocumentNode; - } - - void finish() { - // won't be needed anymore - qnameHash.clear(); - valueHash.clear(); - valueList.clear(); - - // optimize, see documentation on QVector::squeeze - for (int d = 0; d < groups.count(); ++d) { - KoXmlPackedGroup& group = groups[d]; - group.squeeze(); - } - } - - // in case namespace processing, 'name' contains the prefix already - void addElement(const QString& name, const QString& nsURI) { - KoXmlPackedItem& item = newItem(currentDepth + 1); - item.type = KoXmlNode::ElementNode; - item.qnameIndex = cacheQName(name, nsURI); - - ++currentDepth; - } - - void closeElement() { - --currentDepth; - } - - void addDTD(const QString& dt) { - docType = dt; - } - - void addAttribute(const QString& name, const QString& nsURI, const QString& value) { - KoXmlPackedItem& item = newItem(currentDepth + 1); - item.attr = true; - item.qnameIndex = cacheQName(name, nsURI); - //item.value = cacheValue( value ); - item.value = value; - } - - void addText(const QString& text) { - KoXmlPackedItem& item = newItem(currentDepth + 1); - item.type = KoXmlNode::TextNode; - item.value = text; - } - - void addCData(const QString& text) { - KoXmlPackedItem& item = newItem(currentDepth + 1); - item.type = KoXmlNode::CDATASectionNode; - item.value = text; - } - - void addProcessingInstruction() { - KoXmlPackedItem& item = newItem(currentDepth + 1); - item.type = KoXmlNode::ProcessingInstructionNode; - } - -public: - KoXmlPackedDocument(): processNamespace(false), currentDepth(0) { - clear(); - } - -#else - -private: - unsigned elementDepth; - -public: - - KoXmlPackedItem& newItem() { - unsigned count = items.count() + 512; - count = 1024 * (count >> 10); - items.reserve(count); - - items.resize(items.count() + 1); - - // this is necessary, because intentionally we don't want to have - // a constructor for KoXmlPackedItem - KoXmlPackedItem& item = items[items.count()-1]; - item.attr = false; - item.type = KoXmlNode::NullNode; - item.qnameIndex = 0; - item.depth = 0; - - return item; - } - - void addElement(const QString& name, const QString& nsURI) { - // we are going one level deeper - ++elementDepth; - - KoXmlPackedItem& item = newItem(); - - item.attr = false; - item.type = KoXmlNode::ElementNode; - item.depth = elementDepth; - item.qnameIndex = cacheQName(name, nsURI); - } - - void closeElement() { - // we are going up one level - --elementDepth; - } - - void addDTD(const QString& dt) { - docType = dt; - } - - void addAttribute(const QString& name, const QString& nsURI, const QString& value) { - KoXmlPackedItem& item = newItem(); - - item.attr = true; - item.type = KoXmlNode::NullNode; - item.depth = elementDepth; - item.qnameIndex = cacheQName(name, nsURI); - //item.value = cacheValue( value ); - item.value = value; - } - - void addText(const QString& str) { - KoXmlPackedItem& item = newItem(); - - item.attr = false; - item.type = KoXmlNode::TextNode; - item.depth = elementDepth + 1; - item.qnameIndex = 0; - item.value = str; - } - - void addCData(const QString& str) { - KoXmlPackedItem& item = newItem(); - - item.attr = false; - item.type = KoXmlNode::CDATASectionNode; - item.depth = elementDepth + 1; - item.qnameIndex = 0; - item.value = str; - } - - void addProcessingInstruction() { - KoXmlPackedItem& item = newItem(); - - item.attr = false; - item.type = KoXmlNode::ProcessingInstructionNode; - item.depth = elementDepth + 1; - item.qnameIndex = 0; - item.value.clear(); - } - - void clear() { - qnameHash.clear(); - qnameList.clear(); - valueHash.clear(); - valueList.clear(); - items.clear(); - elementDepth = 0; - - KoXmlPackedItem& rootItem = newItem(); - rootItem.attr = false; - rootItem.type = KoXmlNode::DocumentNode; - rootItem.depth = 0; - rootItem.qnameIndex = 0; - } - - void finish() { - qnameHash.clear(); - valueList.clear(); - valueHash.clear(); - items.squeeze(); - } - - KoXmlPackedDocument(): processNamespace(false), elementDepth(0) { - } - -#endif - -}; - -namespace { - - class ParseError { - public: - QString errorMsg; - int errorLine; - int errorColumn; - bool error; - - ParseError() :errorLine(-1), errorColumn(-1), error(false) {} - }; - - void parseElement(QXmlStreamReader &xml, KoXmlPackedDocument &doc, bool stripSpaces = true); - - // parse one element as if this were a standalone xml document - ParseError parseDocument(QXmlStreamReader &xml, KoXmlPackedDocument &doc, bool stripSpaces = true) - { - doc.clear(); - ParseError error; - xml.readNext(); - while (!xml.atEnd() && xml.tokenType() != QXmlStreamReader::EndDocument && !xml.hasError()) { - switch (xml.tokenType()) { - case QXmlStreamReader::StartElement: - parseElement(xml, doc, stripSpaces); - break; - case QXmlStreamReader::DTD: - doc.addDTD(xml.dtdName().toString()); - break; - case QXmlStreamReader::StartDocument: - if (!xml.documentEncoding().isEmpty() || !xml.documentVersion().isEmpty()) { - doc.addProcessingInstruction(); - } - break; - case QXmlStreamReader::ProcessingInstruction: - doc.addProcessingInstruction(); - break; - default: - break; - } - xml.readNext(); - } - if (xml.hasError()) { - error.error = true; - error.errorMsg = xml.errorString(); - error.errorColumn = xml.columnNumber(); - error.errorLine = xml.lineNumber(); - } else { - doc.finish(); - } - return error; - } - - void parseElementContents(QXmlStreamReader &xml, KoXmlPackedDocument &doc) - { - xml.readNext(); - QString ws; - while (!xml.atEnd()) { - switch (xml.tokenType()) { - case QXmlStreamReader::EndElement: - // if an element contains only whitespace, put it in the dom - if (!ws.isEmpty()) { - doc.addText(ws); - } - return; - case QXmlStreamReader::StartElement: - // The whitespaces between > and < are also a text element - if (!ws.isEmpty()) { - doc.addText(ws); - ws.clear(); - } - // Do not strip spaces - parseElement(xml, doc, false); - break; - case QXmlStreamReader::Characters: - if (xml.isCDATA()) { - doc.addCData(xml.text().toString()); - } else if (!xml.isWhitespace()) { - doc.addText(xml.text().toString()); - } else { - ws += xml.text(); - } - break; - case QXmlStreamReader::ProcessingInstruction: - doc.addProcessingInstruction(); - break; - default: - break; - } - xml.readNext(); - } - } - - void parseElementContentsStripSpaces(QXmlStreamReader &xml, KoXmlPackedDocument &doc) - { - xml.readNext(); - QString ws; - bool sawElement = false; - while (!xml.atEnd()) { - switch (xml.tokenType()) { - case QXmlStreamReader::EndElement: - // if an element contains only whitespace, put it in the dom - if (!ws.isEmpty() && !sawElement) { - doc.addText(ws); - } - return; - case QXmlStreamReader::StartElement: - sawElement = true; - // Do strip spaces - parseElement(xml, doc, true); - break; - case QXmlStreamReader::Characters: - if (xml.isCDATA()) { - doc.addCData(xml.text().toString()); - } else if (!xml.isWhitespace()) { - doc.addText(xml.text().toString()); - } else if (!sawElement) { - ws += xml.text(); - } - break; - case QXmlStreamReader::ProcessingInstruction: - doc.addProcessingInstruction(); - break; - default: - break; - } - xml.readNext(); - } - } - - void parseElement(QXmlStreamReader &xml, KoXmlPackedDocument &doc, bool stripSpaces) - { - // Unfortunately MSVC fails using QXmlStreamReader::const_iterator - // so we apply a for loop instead. https://bugreports.qt.io/browse/QTBUG-45368 - doc.addElement(xml.qualifiedName().toString(), - fixNamespace(xml.namespaceUri().toString())); - QXmlStreamAttributes attr = xml.attributes(); - for (int a = 0; a < attr.count(); a++) { - doc.addAttribute(attr[a].qualifiedName().toString(), - attr[a].namespaceUri().toString(), - attr[a].value().toString()); - } - if (stripSpaces) - parseElementContentsStripSpaces(xml, doc); - else - parseElementContents(xml, doc); - // reader.tokenType() is now QXmlStreamReader::EndElement - doc.closeElement(); - } -} - - -// ================================================================== -// -// KoXmlNodeData -// -// ================================================================== - -class KoXmlNodeData -{ -public: - - explicit KoXmlNodeData(unsigned long initialRefCount = 1); - ~KoXmlNodeData(); - - // generic properties - KoXmlNode::NodeType nodeType; - bool loaded; - -#ifdef KOXML_COMPACT - unsigned nodeDepth; -#endif - - QString tagName; - QString namespaceURI; - QString prefix; - QString localName; - - void ref() { - ++refCount; - } - void unref() { - if (!--refCount) { - delete this; - } - } - - // type information - QString nodeName() const; - - // for tree and linked-list - KoXmlNodeData* parent; - KoXmlNodeData* prev; - KoXmlNodeData* next; - KoXmlNodeData* first; - KoXmlNodeData* last; - - QString text(); - - // node manipulation - void clear(); - - // attributes - inline void setAttribute(const QString& name, const QString& value); - inline QString attribute(const QString& name, const QString& def) const; - inline bool hasAttribute(const QString& name) const; - inline void setAttributeNS(const QString& nsURI, const QString& name, const QString& value); - inline QString attributeNS(const QString& nsURI, const QString& name, const QString& def) const; - inline bool hasAttributeNS(const QString& nsURI, const QString& name) const; - inline void clearAttributes(); - inline QStringList attributeNames() const; - inline QList< QPair > attributeFullNames() const; - - - // for text and CDATA - QString data() const; - - // reference from within the packed doc - KoXmlPackedDocument* packedDoc; - unsigned long nodeIndex; - - // used when doing on-demand (re)parse - void loadChildren(int depth = 1); - void unloadChildren(); - - void dump(); - - static KoXmlNodeData null; - - // compatibility - void asQDomNode(QDomDocument& ownerDoc) const; - -private: - QHash attr; - QHash attrNS; - QString textData; - // reference counting - unsigned long refCount; - friend #include -}; - -KoXmlNodeData KoXmlNodeData::null; - - -KoXmlNodeData::KoXmlNodeData(unsigned long initialRefCount) - : nodeType(KoXmlNode::NullNode) - , loaded(false) -#ifdef KOXML_COMPACT - , nodeDepth(0) -#endif - , parent(0), prev(0), next(0), first(0), last(0) - , packedDoc(0), nodeIndex(0) - , refCount(initialRefCount) -{ -} - -KoXmlNodeData::~KoXmlNodeData() -{ - clear(); -} - -void KoXmlNodeData::clear() -{ - if (first) - for (KoXmlNodeData* node = first; node ;) { - KoXmlNodeData* next = node->next; - node->unref(); - node = next; - } - - // only document can delete these - // normal nodes don't "own" them - if (nodeType == KoXmlNode::DocumentNode) - delete packedDoc; - - nodeType = KoXmlNode::NullNode; - tagName.clear(); - prefix.clear(); - namespaceURI.clear(); - textData.clear(); - packedDoc = 0; - - attr.clear(); - attrNS.clear(); - - parent = 0; - prev = next = 0; - first = last = 0; - - loaded = false; -} - -QString KoXmlNodeData::text() -{ - QString t; - - loadChildren(); - - KoXmlNodeData* node = first; - while (node) { - switch (node->nodeType) { - case KoXmlNode::ElementNode: - t += node->text(); break; - case KoXmlNode::TextNode: - t += node->data(); break; - case KoXmlNode::CDATASectionNode: - t += node->data(); break; - default: break; - } - node = node->next; - } - - return t; -} - -QString KoXmlNodeData::nodeName() const -{ - switch (nodeType) { - case KoXmlNode::ElementNode: { - QString n(tagName); - if (!prefix.isEmpty()) - n.prepend(':').prepend(prefix); - return n; - } - break; - - case KoXmlNode::TextNode: return QLatin1String("#text"); - case KoXmlNode::CDATASectionNode: return QLatin1String("#cdata-section"); - case KoXmlNode::DocumentNode: return QLatin1String("#document"); - case KoXmlNode::DocumentTypeNode: return tagName; - - default: return QString(); break; - } - - // should not happen - return QString(); -} - -void KoXmlNodeData::setAttribute(const QString& name, const QString& value) -{ - attr.insert(name, value); -} - -QString KoXmlNodeData::attribute(const QString& name, const QString& def) const -{ - return attr.value(name, def); -} - -bool KoXmlNodeData::hasAttribute(const QString& name) const -{ - return attr.contains(name); -} - -void KoXmlNodeData::setAttributeNS(const QString& nsURI, - const QString& name, const QString& value) -{ - int i = name.indexOf(':'); - if (i != -1) { - QString localName(name.mid(i + 1)); - KoXmlStringPair key(nsURI, localName); - attrNS.insert(key, value); - } -} - -QString KoXmlNodeData::attributeNS(const QString& nsURI, const QString& name, - const QString& def) const -{ - KoXmlStringPair key(nsURI, name); - return attrNS.value(key, def); -} - -bool KoXmlNodeData::hasAttributeNS(const QString& nsURI, const QString& name) const -{ - KoXmlStringPair key(nsURI, name); - return attrNS.contains(key); -} - -void KoXmlNodeData::clearAttributes() -{ - attr.clear(); - attrNS.clear(); -} - -// FIXME how about namespaced attributes ? -QStringList KoXmlNodeData::attributeNames() const -{ - QStringList result; - result = attr.keys(); - - return result; -} - - -QList< QPair > KoXmlNodeData::attributeFullNames() const -{ - QList< QPair > result; - result = attrNS.keys(); - - return result; -} - -QString KoXmlNodeData::data() const -{ - return textData; -} - -#ifdef KOXML_COMPACT - -void KoXmlNodeData::loadChildren(int depth) -{ - // sanity check - if (!packedDoc) return; - - // already loaded ? - if (loaded && (depth <= 1)) return; - - // in case depth is different - unloadChildren(); - - - KoXmlNodeData* lastDat = 0; - - unsigned childStop = 0; - if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1) - childStop = packedDoc->itemCount(nodeDepth + 1); - else { - const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1); - childStop = next.childStart; - } - - const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex); - - for (unsigned i = self.childStart; i < childStop; ++i) { - const KoXmlPackedItem& item = packedDoc->itemAt(nodeDepth + 1, i); - bool textItem = (item.type == KoXmlNode::TextNode); - textItem |= (item.type == KoXmlNode::CDATASectionNode); - - // attribute belongs to this node - if (item.attr) { - KoQName qname = packedDoc->qnameList[item.qnameIndex]; - QString value = item.value; - - QString prefix; - - QString qName; // with prefix - QString localName; // without prefix, i.e. local name - - localName = qName = qname.name; - int i = qName.indexOf(':'); - if (i != -1) prefix = qName.left(i); - if (i != -1) localName = qName.mid(i + 1); - - if (packedDoc->processNamespace) { - setAttributeNS(qname.nsURI, qName, value); - setAttribute(localName, value); - } else - setAttribute(qName, value); - } else { - KoQName qname = packedDoc->qnameList[item.qnameIndex]; - QString value = item.value; - - QString nodeName = qname.name; - QString localName; - QString prefix; - - if (packedDoc->processNamespace) { - localName = qname.name; - int di = qname.name.indexOf(':'); - if (di != -1) { - localName = qname.name.mid(di + 1); - prefix = qname.name.left(di); - } - nodeName = localName; - } - - // make a node out of this item - KoXmlNodeData* dat = new KoXmlNodeData; - dat->nodeIndex = i; - dat->packedDoc = packedDoc; - dat->nodeDepth = nodeDepth + 1; - dat->nodeType = item.type; - dat->tagName = nodeName; - dat->localName = localName; - dat->prefix = prefix; - dat->namespaceURI = qname.nsURI; - dat->parent = this; - dat->prev = lastDat; - dat->next = 0; - dat->first = 0; - dat->last = 0; - dat->loaded = false; - dat->textData = (textItem) ? value : QString(); - - // adjust our linked-list - first = (first) ? first : dat; - last = dat; - if (lastDat) - lastDat->next = dat; - lastDat = dat; - - // recursive - if (depth > 1) - dat->loadChildren(depth - 1); - } - } - - loaded = true; -} - -#else - -void KoXmlNodeData::loadChildren(int depth) -{ - // sanity check - if (!packedDoc) return; - - // already loaded ? - if (loaded && (depth <= 1)) return; - - // cause we don't know how deep this node's children already loaded are - unloadChildren(); - - KoXmlNodeData* lastDat = 0; - int nodeDepth = packedDoc->items[nodeIndex].depth; - - for (int i = nodeIndex + 1; i < packedDoc->items.count(); ++i) { - KoXmlPackedItem& item = packedDoc->items[i]; - bool textItem = (item.type == KoXmlNode::TextNode); - textItem |= (item.type == KoXmlNode::CDATASectionNode); - - // element already outside our depth - if (!item.attr && (item.type == KoXmlNode::ElementNode)) - if (item.depth <= (unsigned)nodeDepth) - break; - - // attribute belongs to this node - if (item.attr && (item.depth == (unsigned)nodeDepth)) { - KoQName qname = packedDoc->qnameList[item.qnameIndex]; - QString value = item.value; - - QString prefix; - - QString qName; // with prefix - QString localName; // without prefix, i.e. local name - - localName = qName = qname.name; - int i = qName.indexOf(':'); - if (i != -1) prefix = qName.left(i); - if (i != -1) localName = qName.mid(i + 1); - - if (packedDoc->processNamespace) { - setAttributeNS(qname.nsURI, qName, value); - setAttribute(localName, value); - } else - setAttribute(qname.name, value); - } - - // the child node - if (!item.attr) { - bool instruction = (item.type == KoXmlNode::ProcessingInstructionNode); - bool ok = (textItem || instruction) ? (item.depth == (unsigned)nodeDepth) : (item.depth == (unsigned)nodeDepth + 1); - - ok = (item.depth == (unsigned)nodeDepth + 1); - - if (ok) { - KoQName qname = packedDoc->qnameList[item.qnameIndex]; - QString value = item.value; - - QString nodeName = qname.name; - QString localName; - QString prefix; - - if (packedDoc->processNamespace) { - localName = qname.name; - int di = qname.name.indexOf(':'); - if (di != -1) { - localName = qname.name.mid(di + 1); - prefix = qname.name.left(di); - } - nodeName = localName; - } - - // make a node out of this item - KoXmlNodeData* dat = new KoXmlNodeData; - dat->nodeIndex = i; - dat->packedDoc = packedDoc; - dat->nodeType = item.type; - dat->tagName = nodeName; - dat->localName = localName; - dat->prefix = prefix; - dat->namespaceURI = qname.nsURI; - dat->count = 1; - dat->parent = this; - dat->prev = lastDat; - dat->next = 0; - dat->first = 0; - dat->last = 0; - dat->loaded = false; - dat->textData = (textItem) ? value : QString(); - - // adjust our linked-list - first = (first) ? first : dat; - last = dat; - if (lastDat) - lastDat->next = dat; - lastDat = dat; - - // recursive - if (depth > 1) - dat->loadChildren(depth - 1); - } - } - } - - loaded = true; -} -#endif - -void KoXmlNodeData::unloadChildren() -{ - // sanity check - if (!packedDoc) return; - - if (!loaded) return; - - if (first) - for (KoXmlNodeData* node = first; node ;) { - KoXmlNodeData* next = node->next; - node->unloadChildren(); - node->unref(); - node = next; - } - - clearAttributes(); - loaded = false; - first = last = 0; -} - -#ifdef KOXML_COMPACT - - -static void itemAsQDomNode(QDomDocument& ownerDoc, KoXmlPackedDocument* packedDoc, - unsigned nodeDepth, unsigned nodeIndex, QDomNode parentNode = QDomNode()) -{ - // sanity check - if (!packedDoc) - return; - - const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex); - - unsigned childStop = 0; - if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1) - childStop = packedDoc->itemCount(nodeDepth + 1); - else { - const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1); - childStop = next.childStart; - } - - // nothing to do here - if (self.type == KoXmlNode::NullNode) - return; - - // create the element properly - if (self.type == KoXmlNode::ElementNode) { - QDomElement element; - - KoQName qname = packedDoc->qnameList[self.qnameIndex]; - qname.nsURI = fixNamespace(qname.nsURI); - - if (packedDoc->processNamespace) - element = ownerDoc.createElementNS(qname.nsURI, qname.name); - else - element = ownerDoc.createElement(qname.name); - - if ( parentNode.isNull() ) { - ownerDoc.appendChild( element ); - } else { - parentNode.appendChild( element ); - } - // check all subnodes for attributes - for (unsigned i = self.childStart; i < childStop; ++i) { - const KoXmlPackedItem& item = packedDoc->itemAt(nodeDepth + 1, i); - bool textItem = (item.type == KoXmlNode::TextNode); - textItem |= (item.type == KoXmlNode::CDATASectionNode); - - // attribute belongs to this node - if (item.attr) { - KoQName qname = packedDoc->qnameList[item.qnameIndex]; - qname.nsURI = fixNamespace(qname.nsURI ); - QString value = item.value; - - QString prefix; - - QString qName; // with prefix - QString localName; // without prefix, i.e. local name - - localName = qName = qname.name; - int i = qName.indexOf(':'); - if (i != -1) prefix = qName.left(i); - if (i != -1) localName = qName.mid(i + 1); - - if (packedDoc->processNamespace) { - element.setAttributeNS(qname.nsURI, qName, value); - element.setAttribute(localName, value); - } else - element.setAttribute(qname.name, value); - } else { - // add it recursively - itemAsQDomNode(ownerDoc, packedDoc, nodeDepth + 1, i, element); - } - } - return; - } - - // create the text node - if (self.type == KoXmlNode::TextNode) { - QString text = self.value; - - // FIXME: choose CDATA when the value contains special characters - QDomText textNode = ownerDoc.createTextNode(text); - if ( parentNode.isNull() ) { - ownerDoc.appendChild( textNode ); - } else { - parentNode.appendChild( textNode ); - } - return; - } - // nothing matches? strange... -} - -void KoXmlNodeData::asQDomNode(QDomDocument& ownerDoc) const -{ - itemAsQDomNode(ownerDoc, packedDoc, nodeDepth, nodeIndex); -} - -#else - -static void itemAsQDomNode(QDomDocument& ownerDoc, KoXmlPackedDocument* packedDoc, - unsigned nodeIndex, QDomNode parentNode = QDomNode()) -{ - // sanity check - if (!packedDoc) - return; - - KoXmlPackedItem& item = packedDoc->items[nodeIndex]; - - // nothing to do here - if (item.type == KoXmlNode::NullNode) - return; - - // create the element properly - if (item.type == KoXmlNode::ElementNode) { - QDomElement element; - - KoQName qname = packedDoc->qnameList[item.qnameIndex]; - qname.nsURI = fixNamespace(qname.nsURI); - - if (packedDoc->processNamespace) - element = ownerDoc.createElementNS(qname.nsURI, qname.name); - else - element = ownerDoc.createElement(qname.name); - - if ( parentNode.isNull() ) { - ownerDoc.appendChild( element ); - } else { - parentNode.appendChild( element ); - } - // check all subnodes for attributes - int nodeDepth = item.depth; - for (int i = nodeIndex + 1; i < packedDoc->items.count(); ++i) { - KoXmlPackedItem& item = packedDoc->items[i]; - bool textItem = (item.type == KoXmlNode::TextNode); - textItem |= (item.type == KoXmlNode::CDATASectionNode); - - // element already outside our depth - if (!item.attr && (item.type == KoXmlNode::ElementNode)) - if (item.depth <= (unsigned)nodeDepth) - break; - - // attribute belongs to this node - if (item.attr && (item.depth == (unsigned)nodeDepth)) { - KoQName qname = packedDoc->qnameList[item.qnameIndex]; - qname.nsURI = fixNamespace(qname.nsURI); - QString value = item.value; - QString prefix; - - QString qName; // with prefix - QString localName; // without prefix, i.e. local name - - localName = qName = qname.name; - int i = qName.indexOf(':'); - if (i != -1) prefix = qName.left(i); - if (i != -1) localName = qName.mid(i + 1); - - if (packedDoc->processNamespace) { - element.setAttributeNS(qname.nsURI, qName, value); - element.setAttribute(localName, value); - } else - element.setAttribute(qname.name, value); - } - - // direct child of this node - if (!item.attr && (item.depth == (unsigned)nodeDepth + 1)) { - // add it recursively - itemAsQDomNode(ownerDoc, packedDoc, i, element); - } - } - return; - } - - // create the text node - if (item.type == KoXmlNode::TextNode) { - QString text = item.value; - // FIXME: choose CDATA when the value contains special characters - QDomText textNode = ownerDoc.createTextNode(text); - if ( parentNode.isNull() ) { - ownerDoc.appendChild( textNode ); - } else { - parentNode.appendChild( textNode ); - } - return; - } - - // nothing matches? strange... -} - -void KoXmlNodeData::asQDomNode(QDomDocument& ownerDoc) const -{ - itemAsQDomNode(ownerDoc, packedDoc, nodeIndex); -} - -#endif - -void KoXmlNodeData::dump() -{ - printf("NodeData %p\n", (void*)this); - - printf(" nodeIndex: %d\n", (int)nodeIndex); - printf(" packedDoc: %p\n", (void*)packedDoc); - - printf(" nodeType : %d\n", (int)nodeType); - printf(" tagName: %s\n", qPrintable(tagName)); - printf(" namespaceURI: %s\n", qPrintable(namespaceURI)); - printf(" prefix: %s\n", qPrintable(prefix)); - printf(" localName: %s\n", qPrintable(localName)); - - printf(" parent : %p\n", (void*)parent); - printf(" prev : %p\n", (void*)prev); - printf(" next : %p\n", (void*)next); - printf(" first : %p\n", (void*)first); - printf(" last : %p\n", (void*)last); - - printf(" refCount: %ld\n", refCount); - - if (loaded) - printf(" loaded: TRUE\n"); - else - printf(" loaded: FALSE\n"); -} - - -// ================================================================== -// -// KoXmlNodeData -// -// ================================================================== - -class KoXmlDocumentData : public KoXmlNodeData -{ -public: - - KoXmlDocumentData(unsigned long initialRefCount = 1); - ~KoXmlDocumentData(); - - bool setContent(QXmlStreamReader *reader, - QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0); - - KoXmlDocumentType dt; - - bool emptyDocument :1; - // to read the xml with or without spaces - bool stripSpaces :1; -}; - -#define KOXMLDOCDATA(d) static_cast(d) - - -KoXmlDocumentData::KoXmlDocumentData(unsigned long initialRefCount) - : KoXmlNodeData(initialRefCount) - , emptyDocument(true) - , stripSpaces(true) -{ -} - -KoXmlDocumentData::~KoXmlDocumentData() -{ -} - -bool KoXmlDocumentData::setContent(QXmlStreamReader* reader, QString* errorMsg, int* errorLine, int* errorColumn) -{ - // sanity checks - if (!reader) return false; - - if (nodeType != KoXmlNode::DocumentNode) - return false; - - clear(); - nodeType = KoXmlNode::DocumentNode; - - packedDoc = new KoXmlPackedDocument; - packedDoc->processNamespace = reader->namespaceProcessing(); - - ParseError error = parseDocument(*reader, *packedDoc, stripSpaces); - if (error.error) { - // parsing error has occurred - if (errorMsg) *errorMsg = error.errorMsg; - if (errorLine) *errorLine = error.errorLine; - if (errorColumn) *errorColumn = error.errorColumn; - return false; - } - - // initially load - loadChildren(); - - KoXmlNodeData *typeData = new KoXmlNodeData(0); - typeData->nodeType = KoXmlNode::DocumentTypeNode; - typeData->tagName = packedDoc->docType; - typeData->parent = this; - dt = KoXmlDocumentType(typeData); - - return true; -} - -// ================================================================== -// -// KoXmlNode -// -// ================================================================== - -// Creates a null node -KoXmlNode::KoXmlNode() -{ - d = &KoXmlNodeData::null; - d->ref(); -} - -// Destroys this node -KoXmlNode::~KoXmlNode() -{ - d->unref(); -} - -// Creates a copy of another node -KoXmlNode::KoXmlNode(const KoXmlNode& node) -{ - d = node.d; - d->ref(); -} - -// Creates a node for specific implementation -KoXmlNode::KoXmlNode(KoXmlNodeData* data) -{ - d = data; - data->ref(); -} - -// Creates a shallow copy of another node -KoXmlNode& KoXmlNode::operator=(const KoXmlNode & node) -{ - if (this != &node) { - d->unref(); - d = node.d; - d->ref(); - } - return *this; -} - -// Note: two null nodes are always equal -bool KoXmlNode::operator==(const KoXmlNode& node) const -{ - if (isNull() && node.isNull()) return true; - return(d == node.d); -} - -// Note: two null nodes are always equal -bool KoXmlNode::operator!=(const KoXmlNode& node) const -{ - if (isNull() && !node.isNull()) return true; - if (!isNull() && node.isNull()) return true; - if (isNull() && node.isNull()) return false; - return(d != node.d); -} - -KoXmlNode::NodeType KoXmlNode::nodeType() const -{ - return d->nodeType; -} - -bool KoXmlNode::isNull() const -{ - return d->nodeType == NullNode; -} - -bool KoXmlNode::isElement() const -{ - return d->nodeType == ElementNode; -} - -bool KoXmlNode::isText() const -{ - return (d->nodeType == TextNode) || isCDATASection(); -} - -bool KoXmlNode::isCDATASection() const -{ - return d->nodeType == CDATASectionNode; -} - -bool KoXmlNode::isDocument() const -{ - return d->nodeType == DocumentNode; -} - -bool KoXmlNode::isDocumentType() const -{ - return d->nodeType == DocumentTypeNode; -} - -void KoXmlNode::clear() -{ - d->unref(); - d = new KoXmlNodeData; -} - -QString KoXmlNode::nodeName() const -{ - return d->nodeName(); -} - -QString KoXmlNode::prefix() const -{ - return isElement() ? d->prefix : QString(); -} - -QString KoXmlNode::namespaceURI() const -{ - return isElement() ? d->namespaceURI : QString(); -} - -QString KoXmlNode::localName() const -{ - return isElement() ? d->localName : QString(); -} - -KoXmlDocument KoXmlNode::ownerDocument() const -{ - KoXmlNodeData* node = d; - while (node->parent) node = node->parent; - - if (node->nodeType == DocumentNode) { - return KoXmlDocument(static_cast(node)); - } - return KoXmlDocument(); -} - -KoXmlNode KoXmlNode::parentNode() const -{ - return d->parent ? KoXmlNode(d->parent) : KoXmlNode(); -} - -bool KoXmlNode::hasChildNodes() const -{ - if (isText()) - return false; - - if (!d->loaded) - d->loadChildren(); - - return d->first != 0 ; -} - -int KoXmlNode::childNodesCount() const -{ - if (isText()) - return 0; - - if (!d->loaded) - d->loadChildren(); - - KoXmlNodeData* node = d->first; - int count = 0; - while (node) { - ++count; - node = node->next; - } - - return count; -} - -QStringList KoXmlNode::attributeNames() const -{ - if (!d->loaded) - d->loadChildren(); - - return d->attributeNames(); -} - -QList< QPair > KoXmlNode::attributeFullNames() const -{ - if (!d->loaded) - d->loadChildren(); - - return d->attributeFullNames(); -} - -KoXmlNode KoXmlNode::firstChild() const -{ - if (!d->loaded) - d->loadChildren(); - return d->first ? KoXmlNode(d->first) : KoXmlNode(); -} - -KoXmlElement KoXmlNode::firstChildElement() const -{ - KoXmlElement element; - forEachElement (element, (*this)) { - return element; - } - return KoXmlElement(); -} - -KoXmlNode KoXmlNode::lastChild() const -{ - if (!d->loaded) - d->loadChildren(); - return d->last ? KoXmlNode(d->last) : KoXmlNode(); -} - -KoXmlNode KoXmlNode::nextSibling() const -{ - return d->next ? KoXmlNode(d->next) : KoXmlNode(); -} - -KoXmlNode KoXmlNode::previousSibling() const -{ - return d->prev ? KoXmlNode(d->prev) : KoXmlNode(); -} - -KoXmlNode KoXmlNode::namedItem(const QString& name) const -{ - if (!d->loaded) - d->loadChildren(); - - for (KoXmlNodeData* node = d->first; node; node = node->next) { - if (node->nodeName() == name) - return KoXmlNode(node); - } - - // not found - return KoXmlNode(); -} - -KoXmlNode KoXmlNode::namedItemNS(const QString& nsURI, const QString& name) const -{ - if (!d->loaded) - d->loadChildren(); - - for (KoXmlNodeData* node = d->first; node; node = node->next) { - if (node->nodeType == KoXmlNode::ElementNode - && node->localName == name - && node->namespaceURI == nsURI - ) { - return KoXmlNode(node); - } - } - - // not found - return KoXmlNode(); -} - -KoXmlNode KoXmlNode::namedItemNS(const QString& nsURI, const QString& name, KoXmlNamedItemType type) const -{ - if (!d->loaded) - d->loadChildren(); - - for (KoXmlNodeData* node = d->first; node; node = node->next) { - if (node->nodeType != KoXmlNode::ElementNode) - continue; - if (node->localName == name && node->namespaceURI == nsURI) { - return KoXmlNode(node); - } - bool isPrelude = false; - switch (type) { - case KoXmlTextContentPrelude: - isPrelude = - (node->localName == "tracked-changes" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "variable-decls" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "user-field-decls" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "user-field-decl" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "sequence-decls" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "sequence-decl" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "dde-connection-decls" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "alphabetical-index-auto-mark-file" && node->namespaceURI == KoXmlNS::text) || - (node->localName == "forms" && node->namespaceURI == KoXmlNS::office); - break; - } - if (!isPrelude) { - return KoXmlNode(); // no TextContentPrelude means it follows TextContentMain, so stop here. - } - } - - // not found - return KoXmlNode(); -} - -KoXmlElement KoXmlNode::toElement() const -{ - return isElement() ? KoXmlElement(d) : KoXmlElement(); -} - -KoXmlText KoXmlNode::toText() const -{ - return isText() ? KoXmlText(d) : KoXmlText(); -} - -KoXmlCDATASection KoXmlNode::toCDATASection() const -{ - return isCDATASection() ? KoXmlCDATASection(d) : KoXmlCDATASection(); -} - -KoXmlDocument KoXmlNode::toDocument() const -{ - if (isDocument()) { - return KoXmlDocument(static_cast(d)); - } - return KoXmlDocument(); -} - -void KoXmlNode::load(int depth) -{ - d->loadChildren(depth); -} - -void KoXmlNode::unload() -{ - d->unloadChildren(); -} - -void KoXmlNode::asQDomNode(QDomDocument& ownerDoc) const -{ - Q_ASSERT(!isDocument()); - d->asQDomNode(ownerDoc); -} - -// ================================================================== -// -// KoXmlElement -// -// ================================================================== - -// Creates an empty element -KoXmlElement::KoXmlElement(): KoXmlNode() -{ -} - -KoXmlElement::~KoXmlElement() -{ -} - -// Creates a shallow copy of another element -KoXmlElement::KoXmlElement(const KoXmlElement& element): KoXmlNode(element.d) -{ -} - -KoXmlElement::KoXmlElement(KoXmlNodeData* data): KoXmlNode(data) -{ -} - -// Copies another element -KoXmlElement& KoXmlElement::operator=(const KoXmlElement & element) -{ - KoXmlNode::operator=(element); - return *this; -} - -bool KoXmlElement::operator== (const KoXmlElement& element) const -{ - if (isNull() || element.isNull()) return false; - return (d == element.d); -} - -bool KoXmlElement::operator!= (const KoXmlElement& element) const -{ - if (isNull() && element.isNull()) return false; - if (isNull() || element.isNull()) return true; - return (d != element.d); -} - -QString KoXmlElement::tagName() const -{ - return isElement() ? d->tagName : QString(); -} - -QString KoXmlElement::text() const -{ - return d->text(); -} - -QString KoXmlElement::attribute(const QString& name) const -{ - if (!isElement()) - return QString(); - - if (!d->loaded) - d->loadChildren(); - - return d->attribute(name, QString()); -} - -QString KoXmlElement::attribute(const QString& name, - const QString& defaultValue) const -{ - if (!isElement()) - return defaultValue; - - if (!d->loaded) - d->loadChildren(); - - return d->attribute(name, defaultValue); -} - -QString KoXmlElement::attributeNS(const QString& namespaceURI, - const QString& localName, const QString& defaultValue) const -{ - if (!isElement()) - return defaultValue; - - if (!d->loaded) - d->loadChildren(); - - KoXmlStringPair key(namespaceURI, localName); - return d->attrNS.value(key, defaultValue); - -// return d->attributeNS( namespaceURI, localName, defaultValue ); -} - -bool KoXmlElement::hasAttribute(const QString& name) const -{ - if (!d->loaded) - d->loadChildren(); - - return isElement() ? d->hasAttribute(name) : false; -} - -bool KoXmlElement::hasAttributeNS(const QString& namespaceURI, - const QString& localName) const -{ - if (!d->loaded) - d->loadChildren(); - - return isElement() ? d->hasAttributeNS(namespaceURI, localName) : false; -} - -// ================================================================== -// -// KoXmlText -// -// ================================================================== - -KoXmlText::KoXmlText(): KoXmlNode() -{ -} - -KoXmlText::~KoXmlText() -{ -} - -KoXmlText::KoXmlText(const KoXmlText& text): KoXmlNode(text.d) -{ -} - -KoXmlText::KoXmlText(KoXmlNodeData* data): KoXmlNode(data) -{ -} - -bool KoXmlText::isText() const -{ - return true; -} - -QString KoXmlText::data() const -{ - return d->data(); -} - -KoXmlText& KoXmlText::operator=(const KoXmlText & element) -{ - KoXmlNode::operator=(element); - return *this; -} - -// ================================================================== -// -// KoXmlCDATASection -// -// ================================================================== - -KoXmlCDATASection::KoXmlCDATASection(): KoXmlText() -{ -} - -KoXmlCDATASection::KoXmlCDATASection(const KoXmlCDATASection& cdata) - : KoXmlText(cdata) -{ -} - -KoXmlCDATASection::~KoXmlCDATASection() -{ -} - -KoXmlCDATASection::KoXmlCDATASection(KoXmlNodeData* cdata): - KoXmlText(cdata) -{ -} - -bool KoXmlCDATASection::isCDATASection() const -{ - return true; -} - -KoXmlCDATASection& KoXmlCDATASection::operator=(const KoXmlCDATASection & cdata) -{ - KoXmlNode::operator=(cdata); - return *this; -} - -// ================================================================== -// -// KoXmlDocumentType -// -// ================================================================== - -KoXmlDocumentType::KoXmlDocumentType(): KoXmlNode() -{ -} - -KoXmlDocumentType::~KoXmlDocumentType() -{ -} - -KoXmlDocumentType::KoXmlDocumentType(const KoXmlDocumentType& dt): - KoXmlNode(dt.d) -{ -} - -QString KoXmlDocumentType::name() const -{ - return nodeName(); -} - -KoXmlDocumentType::KoXmlDocumentType(KoXmlNodeData* dt): KoXmlNode(dt) -{ -} - -KoXmlDocumentType& KoXmlDocumentType::operator=(const KoXmlDocumentType & dt) -{ - KoXmlNode::operator=(dt); - return *this; -} - -// ================================================================== -// -// KoXmlDocument -// -// ================================================================== - -KoXmlDocument::KoXmlDocument(bool stripSpaces): KoXmlNode(new KoXmlDocumentData(0)) -{ - KOXMLDOCDATA(d)->emptyDocument = false; - KOXMLDOCDATA(d)->stripSpaces = stripSpaces; -} - -KoXmlDocument::~KoXmlDocument() -{ -} - -KoXmlDocument::KoXmlDocument(KoXmlDocumentData* data): KoXmlNode(data) -{ - KOXMLDOCDATA(d)->emptyDocument = true; -} - -// Creates a copy of another document -KoXmlDocument::KoXmlDocument(const KoXmlDocument& doc): KoXmlNode(doc.d) -{ -} - -// Creates a shallow copy of another document -KoXmlDocument& KoXmlDocument::operator=(const KoXmlDocument & doc) -{ - KoXmlNode::operator=(doc); - return *this; -} - -// Checks if this document and doc are equals -bool KoXmlDocument::operator==(const KoXmlDocument& doc) const -{ - return(d == doc.d); -} - -// Checks if this document and doc are not equals -bool KoXmlDocument::operator!=(const KoXmlDocument& doc) const -{ - return(d != doc.d); -} - -KoXmlElement KoXmlDocument::documentElement() const -{ - if (!d->loaded) - d->loadChildren(); - - for (KoXmlNodeData* node = d->first; node; node = node->next) { - if (node->nodeType == KoXmlNode::ElementNode) { - return KoXmlElement(node); - } - } - - return KoXmlElement(); -} - -KoXmlDocumentType KoXmlDocument::doctype() const -{ - return KOXMLDOCDATA(d)->dt; -} - -QString KoXmlDocument::nodeName() const -{ - return (KOXMLDOCDATA(d)->emptyDocument) ? QString::fromLatin1("#document") : QString(); -} - -void KoXmlDocument::clear() -{ - d->unref(); - KoXmlDocumentData *dat = new KoXmlDocumentData; - dat->emptyDocument = false; - d = dat; -} - -namespace { - /* Use an entity resolver that ignores undefined entities and simply - returns an empty string for them. - */ - class DumbEntityResolver : public QXmlStreamEntityResolver { - public: - QString resolveUndeclaredEntity ( const QString &) override { return ""; } - }; - -} - -bool KoXmlDocument::setContent(QXmlStreamReader *reader, - QString* errorMsg, int* errorLine, int* errorColumn) -{ - if (d->nodeType != KoXmlNode::DocumentNode) { - const bool stripSpaces = KOXMLDOCDATA(d)->stripSpaces; - d->unref(); - KoXmlDocumentData *dat = new KoXmlDocumentData; - dat->nodeType = KoXmlNode::DocumentNode; - dat->stripSpaces = stripSpaces; - d = dat; - } - - const bool result = KOXMLDOCDATA(d)->setContent(reader, errorMsg, errorLine, errorColumn); - - return result; -} - -// no namespace processing -bool KoXmlDocument::setContent(QIODevice* device, QString* errorMsg, - int* errorLine, int* errorColumn) -{ - return setContent(device, false, errorMsg, errorLine, errorColumn); -} - -bool KoXmlDocument::setContent(QIODevice* device, bool namespaceProcessing, - QString* errorMsg, int* errorLine, int* errorColumn) -{ - if (d->nodeType != KoXmlNode::DocumentNode) { - const bool stripSpaces = KOXMLDOCDATA(d)->stripSpaces; - d->unref(); - KoXmlDocumentData *dat = new KoXmlDocumentData; - dat->nodeType = KoXmlNode::DocumentNode; - dat->stripSpaces = stripSpaces; - d = dat; - } - - if (!device->isOpen()) device->open(QIODevice::ReadOnly); - QXmlStreamReader reader(device); - reader.setNamespaceProcessing(namespaceProcessing); - DumbEntityResolver entityResolver; - reader.setEntityResolver(&entityResolver); - - const bool result = KOXMLDOCDATA(d)->setContent(&reader, errorMsg, errorLine, errorColumn); - - return result; -} - -bool KoXmlDocument::setContent(const QByteArray& text, bool namespaceProcessing, - QString *errorMsg, int *errorLine, int *errorColumn) -{ - QBuffer buffer; - buffer.setData(text); - return setContent(&buffer, namespaceProcessing, errorMsg, errorLine, errorColumn); -} - -bool KoXmlDocument::setContent(const QString& text, bool namespaceProcessing, - QString *errorMsg, int *errorLine, int *errorColumn) -{ - if (d->nodeType != KoXmlNode::DocumentNode) { - const bool stripSpaces = KOXMLDOCDATA(d)->stripSpaces; - d->unref(); - KoXmlDocumentData *dat = new KoXmlDocumentData; - dat->nodeType = KoXmlNode::DocumentNode; - dat->stripSpaces = stripSpaces; - d = dat; - } - - QXmlStreamReader reader(text); - reader.setNamespaceProcessing(namespaceProcessing); - DumbEntityResolver entityResolver; - reader.setEntityResolver(&entityResolver); - - const bool result = KOXMLDOCDATA(d)->setContent(&reader, errorMsg, errorLine, errorColumn); - - return result; -} - -bool KoXmlDocument::setContent(const QString& text, - QString *errorMsg, int *errorLine, int *errorColumn) -{ - return setContent(text, false, errorMsg, errorLine, errorColumn); -} - -void KoXmlDocument::setWhitespaceStripping(bool stripSpaces) -{ - KOXMLDOCDATA(d)->stripSpaces = stripSpaces; -} - +#include +#include +#include -#endif // ================================================================== // // functions in KoXml namespace // // ================================================================== KoXmlElement KoXml::namedItemNS(const KoXmlNode& node, const QString& nsURI, const QString& localName) { -#ifdef KOXML_USE_QDOM // David's solution for namedItemNS, only for QDom stuff KoXmlNode n = node.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (n.isElement() && n.localName() == localName && n.namespaceURI() == nsURI) return n.toElement(); } return KoXmlElement(); -#else - return node.namedItemNS(nsURI, localName).toElement(); -#endif } KoXmlElement KoXml::namedItemNS(const KoXmlNode& node, const QString& nsURI, const QString& localName, KoXmlNamedItemType type) { -#ifdef KOXML_USE_QDOM Q_UNUSED(type) return namedItemNS(node, nsURI, localName); -#else - return node.namedItemNS(nsURI, localName, type).toElement(); -#endif } void KoXml::load(KoXmlNode& node, int depth) { -#ifdef KOXML_USE_QDOM // do nothing, QDom has no on-demand loading Q_UNUSED(node); Q_UNUSED(depth); -#else - node.load(depth); -#endif } void KoXml::unload(KoXmlNode& node) { -#ifdef KOXML_USE_QDOM // do nothing, QDom has no on-demand unloading Q_UNUSED(node); -#else - node.unload(); -#endif } int KoXml::childNodesCount(const KoXmlNode& node) { -#ifdef KOXML_USE_QDOM return node.childNodes().count(); -#else - // compatibility function, because no need to implement - // a class like QDomNodeList - return node.childNodesCount(); -#endif } QStringList KoXml::attributeNames(const KoXmlNode& node) { -#ifdef KOXML_USE_QDOM QStringList result; QDomNamedNodeMap attrMap = node.attributes(); for (int i = 0; i < attrMap.count(); ++i) result += attrMap.item(i).toAttr().name(); return result; -#else - // compatibility function, because no need to implement - // a class like QDomNamedNodeMap - return node.attributeNames(); -#endif } void KoXml::asQDomNode(QDomDocument& ownerDoc, const KoXmlNode& node) { Q_ASSERT(!node.isDocument()); -#ifdef KOXML_USE_QDOM ownerDoc.appendChild(ownerDoc.importNode(node, true)); -#else - node.asQDomNode(ownerDoc); -#endif } void KoXml::asQDomElement(QDomDocument &ownerDoc, const KoXmlElement& element) { KoXml::asQDomNode(ownerDoc, element); } QDomDocument KoXml::asQDomDocument(const KoXmlDocument& document) { -#ifdef KOXML_USE_QDOM return document; -#else - QDomDocument qdoc( document.nodeName() ); - if ( document.hasChildNodes() ) { - for ( KoXmlNode n = document.firstChild(); ! n.isNull(); n = n.nextSibling() ) { - KoXml::asQDomNode(qdoc, n); - } - } - return qdoc; -#endif } bool KoXml::setDocument(KoXmlDocument& doc, QIODevice *device, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn) { bool result = doc.setContent(device, namespaceProcessing, errorMsg, errorLine, errorColumn); return result; } diff --git a/libs/store/KoXmlReader.h b/libs/store/KoXmlReader.h index 37e2300764..aa0f888348 100644 --- a/libs/store/KoXmlReader.h +++ b/libs/store/KoXmlReader.h @@ -1,453 +1,181 @@ /* This file is part of the KDE project Copyright (C) 2005-2006 Ariya Hidayat 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 KO_XMLREADER_H #define KO_XMLREADER_H -// KOXML_USE_QDOM is defined there #include "KoXmlReaderForward.h" #include "kritastore_export.h" #include #include class QIODevice; /** * The office-text-content-prelude type. */ enum KoXmlNamedItemType { KoXmlTextContentPrelude ///< office-text-content-prelude //KoXmlTextContentMain, ///< office-text-content-main //KoXmlTextContentEpilogue ///< office-text-content-epilogue }; -#ifndef KOXML_USE_QDOM - -class QXmlStreamReader; - -class KoXmlNodeData; -class KoXmlDocumentData; -class QDomDocument; -class QStringList; - - -/** -* KoXmlNode represents a node in a DOM tree. -* -* KoXmlNode is a base class for KoXmlElement, KoXmlText. -* Often, these subclasses are used for getting the data instead of KoXmlNode. -* However, as base class, KoXmlNode is very helpful when for example iterating -* all child nodes within one parent node. -* -* KoXmlNode implements an explicit sharing, a node shares its data with -* other copies (if exist). -* -* XXX: DO NOT ADD CONVENIENCE API HERE BECAUSE THIS CLASS MUST REMAIN COMPATIBLE WITH QDOMNODE! -* -* @author Ariya Hidayat -*/ -class KRITASTORE_EXPORT KoXmlNode -{ -public: - - enum NodeType { - NullNode = 0, - ElementNode, - TextNode, - CDATASectionNode, - ProcessingInstructionNode, - DocumentNode, - DocumentTypeNode - }; - - KoXmlNode(); - KoXmlNode(const KoXmlNode& node); - KoXmlNode& operator=(const KoXmlNode& node); - bool operator== (const KoXmlNode&) const; - bool operator!= (const KoXmlNode&) const; - virtual ~KoXmlNode(); - - virtual KoXmlNode::NodeType nodeType() const; - virtual bool isNull() const; - virtual bool isElement() const; - virtual bool isText() const; - virtual bool isCDATASection() const; - virtual bool isDocument() const; - virtual bool isDocumentType() const; - - virtual void clear(); - KoXmlElement toElement() const; - KoXmlText toText() const; - KoXmlCDATASection toCDATASection() const; - KoXmlDocument toDocument() const; - - virtual QString nodeName() const; - virtual QString namespaceURI() const; - virtual QString prefix() const; - virtual QString localName() const; - - KoXmlDocument ownerDocument() const; - KoXmlNode parentNode() const; - - bool hasChildNodes() const; - KoXmlNode firstChild() const; - KoXmlNode lastChild() const; - KoXmlNode nextSibling() const; - KoXmlNode previousSibling() const; - - KoXmlElement firstChildElement() const; - - // equivalent to node.childNodes().count() if node is a QDomNode instance - int childNodesCount() const; - - // workaround to get and iterate over all attributes - QStringList attributeNames() const; - QList< QPair > attributeFullNames() const; - - KoXmlNode namedItem(const QString& name) const; - KoXmlNode namedItemNS(const QString& nsURI, const QString& name) const; - KoXmlNode namedItemNS(const QString& nsURI, const QString& name, KoXmlNamedItemType type) const; - - /** - * Loads all child nodes (if any) of this node. Normally you do not need - * to call this function as the child nodes will be automatically - * loaded when necessary. - */ - void load(int depth = 1); - - /** - * Releases all child nodes of this node. - */ - void unload(); - - // compatibility - /** - * @internal do not call directly - * Use KoXml::asQDomDocument(), KoXml::asQDomElement() or KoXml::asQDomNode() instead - */ - void asQDomNode(QDomDocument& ownerDoc) const; - -protected: - KoXmlNodeData* d; - explicit KoXmlNode(KoXmlNodeData*); -}; - -/** -* KoXmlElement represents a tag element in a DOM tree. -* -* KoXmlElement holds information about an XML tag, along with its attributes. -* -* @author Ariya Hidayat -*/ - -class KRITASTORE_EXPORT KoXmlElement: public KoXmlNode -{ -public: - KoXmlElement(); - KoXmlElement(const KoXmlElement& element); - KoXmlElement& operator=(const KoXmlElement& element); - ~KoXmlElement() override; - bool operator== (const KoXmlElement&) const; - bool operator!= (const KoXmlElement&) const; - - QString tagName() const; - QString text() const; - - QString attribute(const QString& name) const; - QString attribute(const QString& name, const QString& defaultValue) const; - QString attributeNS(const QString& namespaceURI, const QString& localName, - const QString& defaultValue = QString()) const; - bool hasAttribute(const QString& name) const; - bool hasAttributeNS(const QString& namespaceURI, const QString& localName) const; - -private: - friend class KoXmlNode; - friend class KoXmlDocument; - explicit KoXmlElement(KoXmlNodeData*); -}; - -/** -* KoXmlText represents a text in a DOM tree. -* @author Ariya Hidayat -*/ -class KRITASTORE_EXPORT KoXmlText: public KoXmlNode -{ -public: - KoXmlText(); - KoXmlText(const KoXmlText& text); - KoXmlText& operator=(const KoXmlText& text); - ~KoXmlText() override; - - QString data() const; - bool isText() const override; - -private: - friend class KoXmlNode; - friend class KoXmlCDATASection; - friend class KoXmlDocument; - explicit KoXmlText(KoXmlNodeData*); -}; - -/** -* KoXmlCDATASection represents a CDATA section in a DOM tree. -* @author Ariya Hidayat -*/ -class KRITASTORE_EXPORT KoXmlCDATASection: public KoXmlText -{ -public: - KoXmlCDATASection(); - KoXmlCDATASection(const KoXmlCDATASection& cdata); - KoXmlCDATASection& operator=(const KoXmlCDATASection& cdata); - ~KoXmlCDATASection() override; - - bool isCDATASection() const override; - -private: - friend class KoXmlNode; - friend class KoXmlDocument; - explicit KoXmlCDATASection(KoXmlNodeData*); -}; - -/** -* KoXmlDocumentType represents the DTD of the document. At the moment, -* it can used only to get the document type, i.e. no support for -* entities etc. -* -* @author Ariya Hidayat -*/ - -class KRITASTORE_EXPORT KoXmlDocumentType: public KoXmlNode -{ -public: - KoXmlDocumentType(); - KoXmlDocumentType(const KoXmlDocumentType&); - KoXmlDocumentType& operator=(const KoXmlDocumentType&); - ~KoXmlDocumentType() override; - - QString name() const; - -private: - friend class KoXmlNode; - friend class KoXmlDocument; - friend class KoXmlDocumentData; - explicit KoXmlDocumentType(KoXmlNodeData*); -}; - - -/** -* KoXmlDocument represents an XML document, structured in a DOM tree. -* -* KoXmlDocument is designed to be memory efficient. Unlike QDomDocument from -* Qt's XML module, KoXmlDocument does not store all nodes in the DOM tree. -* Some nodes will be loaded and parsed on-demand only. -* -* KoXmlDocument is read-only, you can not modify its content. -* -* @author Ariya Hidayat -*/ - -class KRITASTORE_EXPORT KoXmlDocument: public KoXmlNode -{ -public: - explicit KoXmlDocument(bool stripSpaces = false); - KoXmlDocument(const KoXmlDocument& node); - KoXmlDocument& operator=(const KoXmlDocument& node); - bool operator==(const KoXmlDocument&) const; - bool operator!=(const KoXmlDocument&) const; - ~KoXmlDocument() override; - - KoXmlElement documentElement() const; - - KoXmlDocumentType doctype() const; - - QString nodeName() const override; - void clear() override; - - bool setContent(QIODevice* device, bool namespaceProcessing, - QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0); - bool setContent(QIODevice* device, - QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0); - bool setContent(QXmlStreamReader *reader, - QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0); - bool setContent(const QByteArray& text, bool namespaceProcessing, - QString *errorMsg = 0, int *errorLine = 0, int *errorColumn = 0); - bool setContent(const QString& text, bool namespaceProcessing, - QString *errorMsg = 0, int *errorLine = 0, int *errorColumn = 0); - - // no namespace processing - bool setContent(const QString& text, - QString *errorMsg = 0, int *errorLine = 0, int *errorColumn = 0); - /** - * Change the way an XMLDocument will be read: - * if stripSpaces = true then a will only have one child - * if stripSpaces = false then a will have 3 children. - */ - void setWhitespaceStripping(bool stripSpaces); - -private: - friend class KoXmlNode; - explicit KoXmlDocument(KoXmlDocumentData*); -}; - -#endif // KOXML_USE_QDOM - /** * This namespace contains a few convenience functions to simplify code using QDom * (when loading OASIS documents, in particular). * * To find the child element with a given name, use KoXml::namedItemNS. * * To find all child elements with a given name, use * QDomElement e; * forEachElement( e, parent ) * { * if ( e.localName() == "..." && e.namespaceURI() == KoXmlNS::... ) * { * ... * } * } * Note that this means you don't ever need to use QDomNode nor toElement anymore! * Also note that localName is the part without the prefix, this is the whole point * of namespace-aware methods. * * To find the attribute with a given name, use QDomElement::attributeNS. * * Do not use getElementsByTagNameNS, it's recursive (which is never needed in Calligra). * Do not use tagName() or nodeName() or prefix(), since the prefix isn't fixed. * * @author David Faure */ namespace KoXml { /** * A namespace-aware version of QDomNode::namedItem(), * which also takes care of casting to a QDomElement. * * Use this when a domelement is known to have only *one* child element * with a given tagname. * * Note: do *NOT* use getElementsByTagNameNS, it's recursive! */ KRITASTORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode& node, const QString& nsURI, const QString& localName); /** * A namespace-aware version of QDomNode::namedItem(). * which also takes care of casting to a QDomElement. * * Use this when you like to return the first or an invalid * KoXmlElement with a known type. * * This is an optimized version of the namedItemNS above to * give fast access to certain sections of the document using * the office-text-content-prelude condition as @a KoXmlNamedItemType . */ KRITASTORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode& node, const QString& nsURI, const QString& localName, KoXmlNamedItemType type); /** * Explicitly load child nodes of specified node, up to given depth. * This function has no effect if QDom is used. */ KRITASTORE_EXPORT void load(KoXmlNode& node, int depth = 1); /** * Unload child nodes of specified node. * This function has no effect if QDom is used. */ KRITASTORE_EXPORT void unload(KoXmlNode& node); /** * Get the number of child nodes of specified node. */ KRITASTORE_EXPORT int childNodesCount(const KoXmlNode& node); /** * Return the name of all attributes of specified node. */ KRITASTORE_EXPORT QStringList attributeNames(const KoXmlNode& node); /** * Convert KoXmlNode classes to the corresponding QDom classes, which has * @p ownerDoc as the owner document (QDomDocument instance). * The converted @p node (and its children) are added to ownerDoc. * * NOTE: * - If ownerDoc is not empty, this may fail, @see QDomDocument * - @p node must not be a KoXmlDocument, use asQDomDocument() * * @see asQDomDocument, asQDomElement */ KRITASTORE_EXPORT void asQDomNode(QDomDocument& ownerDoc, const KoXmlNode& node); /** * Convert KoXmlNode classes to the corresponding QDom classes, which has * @p ownerDoc as the owner document (QDomDocument instance). * The converted @p element (and its children) is added to ownerDoc. * * NOTE: If ownerDoc is not empty, this may fail, @see QDomDocument * */ KRITASTORE_EXPORT void asQDomElement(QDomDocument& ownerDoc, const KoXmlElement& element); /** * Converts the whole @p document into a QDomDocument - * If KOXML_USE_QDOM is defined, just returns @p document */ KRITASTORE_EXPORT QDomDocument asQDomDocument(const KoXmlDocument& document); /* * Load an XML document from specified device to a document. You can of * course use it with QFile (which inherits QIODevice). * This is much more memory efficient than standard QDomDocument::setContent * because the data from the device is buffered, unlike * QDomDocument::setContent which just loads everything in memory. * * Note: it is assumed that the XML uses UTF-8 encoding. */ KRITASTORE_EXPORT bool setDocument(KoXmlDocument& doc, QIODevice* device, bool namespaceProcessing, QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0); } /** * \def forEachElement( elem, parent ) * \brief Loop through all child elements of \parent. * This convenience macro is used to implement the forEachElement loop. * The \elem parameter is a name of a QDomElement variable and the \parent * is the name of the parent element. For example: * * QDomElement e; * forEachElement( e, parent ) * { * qDebug() << e.localName() << " element found."; * ... * } */ #define forEachElement( elem, parent ) \ for ( KoXmlNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \ if ( ( elem = _node.toElement() ).isNull() ) {} else #endif // KO_XMLREADER_H diff --git a/libs/store/KoXmlReaderForward.h b/libs/store/KoXmlReaderForward.h index 9aa79ad0a8..a615d28cd0 100644 --- a/libs/store/KoXmlReaderForward.h +++ b/libs/store/KoXmlReaderForward.h @@ -1,49 +1,37 @@ /* This file is part of the KDE project Copyright (C) 2005-2006 Ariya Hidayat Copyright (C) 2007 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. */ #ifndef KOXMLREADERFORWARD_H #define KOXMLREADERFORWARD_H // use standard QDom, useful to test KoXml classes against Qt's QDom #define KOXML_USE_QDOM -#ifdef KOXML_USE_QDOM - #include typedef QDomNode KoXmlNode; typedef QDomElement KoXmlElement; typedef QDomText KoXmlText; typedef QDomCDATASection KoXmlCDATASection; typedef QDomDocumentType KoXmlDocumentType; typedef QDomDocument KoXmlDocument; -#else - -class KoXmlElement; -class KoXmlNode; -class KoXmlText; -class KoXmlCDATASection; -class KoXmlDocumentType; -class KoXmlDocument; - -#endif #endif // KOXMLREADERFORWARD_H diff --git a/plugins/flake/textshape/kotext/changetracker/KoChangeTracker.cpp b/plugins/flake/textshape/kotext/changetracker/KoChangeTracker.cpp index fd0e3a84b2..de796e7423 100644 --- a/plugins/flake/textshape/kotext/changetracker/KoChangeTracker.cpp +++ b/plugins/flake/textshape/kotext/changetracker/KoChangeTracker.cpp @@ -1,741 +1,668 @@ /* This file is part of the KDE project * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> * Copyright (C) 2011 Boudewijn Rempt * * 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 "KoChangeTracker.h" //Calligra includes #include "styles/KoCharacterStyle.h" #include "KoChangeTrackerElement.h" #include #include #include #include #include #include #include #include #include "KoFormatChangeInformation.h" #include //KDE includes #include "TextDebug.h" #include //Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoChangeTracker::Private { public: Private() : changeId(1), recordChanges(false), displayChanges(false), insertionBgColor(101,255,137), deletionBgColor(255,185,185), formatChangeBgColor(195,195,255), changeSaveFormat(UNKNOWN) { } ~Private() { } QMultiHash children; QMultiHash duplicateIds; QHash parents; QHash changes; QHash loadedChanges; QHash changeInformation; QList saveChanges; QList acceptedRejectedChanges; int changeId; bool recordChanges; bool displayChanges; QColor insertionBgColor, deletionBgColor, formatChangeBgColor; QString changeAuthorName; KoChangeTracker::ChangeSaveFormat changeSaveFormat; }; KoChangeTracker::KoChangeTracker(QObject *parent) : QObject(parent), d(new Private()) { d->changeId = 1; } KoChangeTracker::~KoChangeTracker() { delete d; } void KoChangeTracker::setRecordChanges(bool enabled) { d->recordChanges = enabled; } bool KoChangeTracker::recordChanges() const { return d->recordChanges; } void KoChangeTracker::setDisplayChanges(bool enabled) { d->displayChanges = enabled; } bool KoChangeTracker::displayChanges() const { return d->displayChanges; } QString KoChangeTracker::authorName() const { return d->changeAuthorName; } void KoChangeTracker::setAuthorName(const QString &authorName) { d->changeAuthorName = authorName; } KoChangeTracker::ChangeSaveFormat KoChangeTracker::saveFormat() const { return d->changeSaveFormat; } void KoChangeTracker::setSaveFormat(ChangeSaveFormat saveFormat) { d->changeSaveFormat = saveFormat; } int KoChangeTracker::getFormatChangeId(const KUndo2MagicString &title, const QTextFormat &format, const QTextFormat &prevFormat, int existingChangeId) { if ( existingChangeId ) { d->children.insert(existingChangeId, d->changeId); d->parents.insert(d->changeId, existingChangeId); } KoChangeTrackerElement *changeElement = new KoChangeTrackerElement(title, KoGenChange::FormatChange); changeElement->setChangeFormat(format); changeElement->setPrevFormat(prevFormat); QLocale l; changeElement->setDate(l.toString(QDateTime::currentDateTime()).replace(QLocale().decimalPoint(), QString("."))); changeElement->setCreator(d->changeAuthorName); changeElement->setEnabled(d->recordChanges); d->changes.insert(d->changeId, changeElement); return d->changeId++; } int KoChangeTracker::getInsertChangeId(const KUndo2MagicString &title, int existingChangeId) { if ( existingChangeId ) { d->children.insert(existingChangeId, d->changeId); d->parents.insert(d->changeId, existingChangeId); } KoChangeTrackerElement *changeElement = new KoChangeTrackerElement(title, KoGenChange::InsertChange); QLocale l; changeElement->setDate(l.toString(QDateTime::currentDateTime()).replace(QLocale().decimalPoint(), QString("."))); changeElement->setCreator(d->changeAuthorName); changeElement->setEnabled(d->recordChanges); d->changes.insert(d->changeId, changeElement); return d->changeId++; } int KoChangeTracker::getDeleteChangeId(const KUndo2MagicString &title, const QTextDocumentFragment &selection, int existingChangeId) { if ( existingChangeId ) { d->children.insert(existingChangeId, d->changeId); d->parents.insert(d->changeId, existingChangeId); } KoChangeTrackerElement *changeElement = new KoChangeTrackerElement(title, KoGenChange::DeleteChange); QLocale l; changeElement->setDate(l.toString(QDateTime::currentDateTime()).replace(QLocale().decimalPoint(), QString("."))); changeElement->setCreator(d->changeAuthorName); changeElement->setDeleteData(selection); changeElement->setEnabled(d->recordChanges); d->changes.insert(d->changeId, changeElement); return d->changeId++; } KoChangeTrackerElement* KoChangeTracker::elementById(int id) const { if (isDuplicateChangeId(id)) { id = originalChangeId(id); } return d->changes.value(id); } bool KoChangeTracker::removeById(int id, bool freeMemory) { if (freeMemory) { KoChangeTrackerElement *temp = d->changes.value(id); delete temp; } return d->changes.remove(id); } bool KoChangeTracker::containsInlineChanges(const QTextFormat &format) const { if (format.property(KoCharacterStyle::ChangeTrackerId).toInt()) return true; return false; } int KoChangeTracker::mergeableId(KoGenChange::Type type, const KUndo2MagicString &title, int existingId) const { if (!existingId || !d->changes.value(existingId)) return 0; if (d->changes.value(existingId)->getChangeType() == type && d->changes.value(existingId)->getChangeTitle() == title) { return existingId; } else { if (d->parents.contains(existingId)) { return mergeableId(type, title, d->parents.value(existingId)); } else { return 0; } } } int KoChangeTracker::split(int changeId) { KoChangeTrackerElement *element = new KoChangeTrackerElement(*d->changes.value(changeId)); d->changes.insert(d->changeId, element); return d->changeId++; } bool KoChangeTracker::isParent(int testedParentId, int testedChildId) const { if ((testedParentId == testedChildId) && !d->acceptedRejectedChanges.contains(testedParentId)) return true; else if (d->parents.contains(testedChildId)) return isParent(testedParentId, d->parents.value(testedChildId)); else return false; } void KoChangeTracker::setParent(int child, int parent) { if (!d->children.values(parent).contains(child)) { d->children.insert(parent, child); } if (!d->parents.contains(child)) { d->parents.insert(child, parent); } } int KoChangeTracker::parent(int changeId) const { if (!d->parents.contains(changeId)) return 0; if (d->acceptedRejectedChanges.contains(d->parents.value(changeId))) return parent(d->parents.value(changeId)); return d->parents.value(changeId); } int KoChangeTracker::createDuplicateChangeId(int existingChangeId) { int duplicateChangeId = d->changeId; d->changeId++; d->duplicateIds.insert(existingChangeId, duplicateChangeId); return duplicateChangeId; } bool KoChangeTracker::isDuplicateChangeId(int duplicateChangeId) const { return d->duplicateIds.values().contains(duplicateChangeId); } int KoChangeTracker::originalChangeId(int duplicateChangeId) const { int originalChangeId = 0; QMultiHash::const_iterator i = d->duplicateIds.constBegin(); while (i != d->duplicateIds.constEnd()) { if (duplicateChangeId == i.value()) { originalChangeId = i.key(); break; } ++i; } return originalChangeId; } void KoChangeTracker::acceptRejectChange(int changeId, bool set) { if (set) { if (!d->acceptedRejectedChanges.contains(changeId)) d->acceptedRejectedChanges.append(changeId); } else { if (d->acceptedRejectedChanges.contains(changeId)) d->acceptedRejectedChanges.removeAll(changeId); } d->changes.value(changeId)->setAcceptedRejected(set); } bool KoChangeTracker::saveInlineChange(int changeId, KoGenChange &change) { if (!d->changes.contains(changeId)) return false; change.setType(d->changes.value(changeId)->getChangeType()); change.addChangeMetaData("dc-creator", d->changes.value(changeId)->getCreator()); change.addChangeMetaData("dc-date", d->changes.value(changeId)->getDate()); if (d->changes.value(changeId)->hasExtraMetaData()) change.addChildElement("changeMetaData", d->changes.value(changeId)->getExtraMetaData()); return true; } QMap KoChangeTracker::saveInlineChanges(QMap changeTransTable, KoGenChanges &genChanges) { foreach (int changeId, d->changes.keys()) { // return if the id we find in the changetranstable already has a length. if (changeTransTable.value(changeId).length()) { continue; } if ((elementById(changeId)->getChangeType() == KoGenChange::DeleteChange) && (saveFormat() == KoChangeTracker::ODF_1_2)) { continue; } KoGenChange change; if (saveFormat() == KoChangeTracker::ODF_1_2) { change.setChangeFormat(KoGenChange::ODF_1_2); } else { change.setChangeFormat(KoGenChange::DELTAXML); } saveInlineChange(changeId, change); QString changeName = genChanges.insert(change); changeTransTable.insert(changeId, changeName); } return changeTransTable; } void KoChangeTracker::setFormatChangeInformation(int formatChangeId, KoFormatChangeInformation *formatInformation) { d->changeInformation.insert(formatChangeId, formatInformation); } KoFormatChangeInformation *KoChangeTracker::formatChangeInformation(int formatChangeId) const { return d->changeInformation.value(formatChangeId); } -void KoChangeTracker::loadOdfChanges(const KoXmlElement& element) -{ -#ifndef KOXML_USE_QDOM - if (element.namespaceURI() == KoXmlNS::text) { - KoXmlElement tag; - forEachElement(tag, element) { - if (! tag.isNull()) { - const QString localName = tag.localName(); - if (localName == "changed-region") { - KoChangeTrackerElement *changeElement = 0; - KoXmlElement region; - forEachElement(region, tag) { - if (!region.isNull()) { - if (region.localName() == "insertion") { - changeElement = new KoChangeTrackerElement(kundo2_noi18n(tag.attributeNS(KoXmlNS::text,"id")),KoGenChange::InsertChange); - } else if (region.localName() == "format-change") { - changeElement = new KoChangeTrackerElement(kundo2_noi18n(tag.attributeNS(KoXmlNS::text,"id")),KoGenChange::FormatChange); - } else if (region.localName() == "deletion") { - changeElement = new KoChangeTrackerElement(kundo2_noi18n(tag.attributeNS(KoXmlNS::text,"id")),KoGenChange::DeleteChange); - } - KoXmlElement metadata = region.namedItemNS(KoXmlNS::office,"change-info").toElement(); - if (!metadata.isNull()) { - KoXmlElement date = metadata.namedItem("dc:date").toElement(); - if (!date.isNull()) { - changeElement->setDate(date.text()); - } - KoXmlElement creator = metadata.namedItem("dc:creator").toElement(); - if (!date.isNull()) { - changeElement->setCreator(creator.text()); - } - //TODO load comments -/* KoXmlElement extra = metadata.namedItem("dc-").toElement(); - if (!date.isNull()) { - debugText << "creator: " << creator.text(); - changeElement->setCreator(creator.text()); - }*/ - } - changeElement->setEnabled(d->recordChanges); - d->changes.insert( d->changeId, changeElement); - d->loadedChanges.insert(tag.attributeNS(KoXmlNS::text,"id"), d->changeId++); - } - } - } - } - } - } else { - //This is the ODF 1.2 Change Format - KoXmlElement tag; - forEachElement(tag, element) { - if (! tag.isNull()) { - const QString localName = tag.localName(); - if (localName == "change-transaction") { - KoChangeTrackerElement *changeElement = 0; - //Set the change element as an insertion element for now - //Will be changed to the correct type when actual changes referencing this change-id are encountered - changeElement = new KoChangeTrackerElement(kundo2_noi18n(tag.attributeNS(KoXmlNS::delta,"change-id")),KoGenChange::InsertChange); - KoXmlElement metadata = tag.namedItemNS(KoXmlNS::delta,"change-info").toElement(); - if (!metadata.isNull()) { - KoXmlElement date = metadata.namedItem("dc:date").toElement(); - if (!date.isNull()) { - changeElement->setDate(date.text()); - } - KoXmlElement creator = metadata.namedItem("dc:creator").toElement(); - if (!creator.isNull()) { - changeElement->setCreator(creator.text()); - } - } - changeElement->setEnabled(d->recordChanges); - d->changes.insert( d->changeId, changeElement); - d->loadedChanges.insert(tag.attributeNS(KoXmlNS::delta,"change-id"), d->changeId++); - } - } - } - } -#endif +void KoChangeTracker::loadOdfChanges(const KoXmlElement& ) +{ } int KoChangeTracker::getLoadedChangeId(const QString &odfId) const { return d->loadedChanges.value(odfId); } int KoChangeTracker::getDeletedChanges(QVector& deleteVector) const { int numAppendedItems = 0; foreach (KoChangeTrackerElement *element, d->changes.values()) { if(element->getChangeType() == KoGenChange::DeleteChange && !element->acceptedRejected()) { deleteVector << element; numAppendedItems++; } } return numAppendedItems; } QColor KoChangeTracker::getInsertionBgColor() const { return d->insertionBgColor; } QColor KoChangeTracker::getDeletionBgColor() const { return d->deletionBgColor; } QColor KoChangeTracker::getFormatChangeBgColor() const { return d->formatChangeBgColor; } void KoChangeTracker::setInsertionBgColor(const QColor& bgColor) { d->insertionBgColor = bgColor; } void KoChangeTracker::setDeletionBgColor(const QColor& bgColor) { d->deletionBgColor = bgColor; } void KoChangeTracker::setFormatChangeBgColor(const QColor& bgColor) { d->formatChangeBgColor = bgColor; } ////A convenience function to get a ListIdType from a 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; //} QTextDocumentFragment KoChangeTracker::generateDeleteFragment(const QTextCursor &cursor) { QTextCursor editCursor(cursor); QTextDocument *document = cursor.document(); QTextDocument deletedDocument; QTextDocument deleteCursor(&deletedDocument); KoInlineTextObjectManager *textObjectManager = KoTextDocument(document).inlineTextObjectManager(); if (textObjectManager) { for (int i = cursor.anchor();i <= cursor.position(); i++) { if (document->characterAt(i) == QChar::ObjectReplacementCharacter) { editCursor.setPosition(i+1); } } } QTextBlock currentBlock = document->findBlock(cursor.anchor()); QTextBlock startBlock = currentBlock; QTextBlock endBlock = document->findBlock(cursor.position()).next(); currentBlock = document->findBlock(cursor.anchor()); startBlock = currentBlock; endBlock = document->findBlock(cursor.position()).next(); for (;currentBlock != endBlock; currentBlock = currentBlock.next()) { editCursor.setPosition(currentBlock.position()); if (editCursor.currentTable()) { QTextTableFormat tableFormat = editCursor.currentTable()->format(); editCursor.currentTable()->setFormat(tableFormat); } if (currentBlock != startBlock) { QTextBlockFormat blockFormat; editCursor.mergeBlockFormat(blockFormat); } } return cursor.selection(); } bool KoChangeTracker::checkListDeletion(const QTextList &list, const QTextCursor &cursor) { int startOfList = (list.item(0).position() - 1); int endOfList = list.item(list.count() -1).position() + list.item(list.count() -1).length() - 1; if ((cursor.anchor() <= startOfList) && (cursor.position() >= endOfList)) return true; else { /***************************************************************************************************/ /* Qt Quirk Work-Around */ /***************************************************************************************************/ if ((cursor.anchor() == (startOfList + 1)) && (cursor.position() > endOfList)) { return true; /***************************************************************************************************/ } else if((cursor.anchor() <= startOfList) && (list.count() == 1)) { return true; } else { return false; } } } void KoChangeTracker::insertDeleteFragment(QTextCursor &cursor) { QTextDocument tempDoc; QTextCursor tempCursor(&tempDoc); bool deletedListItem = false; for (QTextBlock currentBlock = tempDoc.begin(); currentBlock != tempDoc.end(); currentBlock = currentBlock.next()) { //This condition is for the work-around for a Qt behaviour //Even if a delete ends at the end of a table, the fragment will have an empty block after the table //If such a block is detected then, just ignore it if ((currentBlock.next() == tempDoc.end()) && (currentBlock.text().length() == 0) && (QTextCursor(currentBlock.previous()).currentTable())) { continue; } tempCursor.setPosition(currentBlock.position()); QTextList *textList = tempCursor.currentList(); int outlineLevel = currentBlock.blockFormat().property(KoParagraphStyle::OutlineLevel).toInt(); KoList *currentList = KoTextDocument(cursor.document()).list(cursor.block()); int docOutlineLevel = cursor.block().blockFormat().property(KoParagraphStyle::OutlineLevel).toInt(); if (docOutlineLevel) { //Even though we got a list, it is actually a list for storing headings. So don't consider it currentList = 0; } QTextList *previousTextList = currentBlock.previous().isValid() ? QTextCursor(currentBlock.previous()).currentList():0; if (textList && previousTextList && (textList != previousTextList) && (KoList::level(currentBlock) == KoList::level(currentBlock.previous()))) { //Even though we are already in a list, the QTextList* of the current block is differnt from that of the previous block //Also the levels of the list-items ( previous and current ) are the same. //This can happen only when two lists are merged together without any intermediate content. //So we need to create a new list. currentList = 0; } if (textList) { if (deletedListItem && currentBlock != tempDoc.begin()) { // Found a deleted list item in the fragment. So insert a new list-item int deletedListItemLevel = KoList::level(currentBlock); if (!(QTextCursor(currentBlock.previous()).currentTable())) { cursor.insertBlock(currentBlock.blockFormat(), currentBlock.charFormat()); } else { cursor.mergeBlockFormat(currentBlock.blockFormat()); } if(!currentList) { if (!outlineLevel) { //This happens when a part of a paragraph and a succeeding list-item are deleted together //So go to the next block and insert it in the list there. QTextCursor tmp(cursor); tmp.setPosition(tmp.block().next().position()); currentList = KoTextDocument(tmp.document()).list(tmp.block()); } else { // This is a heading. So find the KoList for heading and add the block there KoList *headingList = KoTextDocument(cursor.document()).headingList(); currentList = headingList; } } currentList->add(cursor.block(), deletedListItemLevel); } } else if (tempCursor.currentTable()) { QTextTable *deletedTable = tempCursor.currentTable(); int numRows = deletedTable->rows(); int numColumns = deletedTable->columns(); QTextTable *insertedTable = cursor.insertTable(numRows, numColumns, deletedTable->format()); for (int i=0; icellAt(i,j).firstCursorPosition().position()); tempCursor.setPosition(deletedTable->cellAt(i,j).lastCursorPosition().position(), QTextCursor::KeepAnchor); insertedTable->cellAt(i,j).setFormat(deletedTable->cellAt(i,j).format().toTableCellFormat()); cursor.setPosition(insertedTable->cellAt(i,j).firstCursorPosition().position()); cursor.insertFragment(tempCursor.selection()); } } tempCursor.setPosition(deletedTable->cellAt(numRows-1,numColumns-1).lastCursorPosition().position()); currentBlock = tempCursor.block(); //Move the cursor outside of table cursor.setPosition(cursor.position() + 1); continue; } else { // This block does not contain a list. So no special work here. if ((currentBlock != tempDoc.begin()) && !(QTextCursor(currentBlock.previous()).currentTable())) { cursor.insertBlock(currentBlock.blockFormat(), currentBlock.charFormat()); } if (QTextCursor(currentBlock.previous()).currentTable()) { cursor.mergeBlockFormat(currentBlock.blockFormat()); } } /********************************************************************************************************************/ /*This section of code is a work-around for a bug in the Qt. This work-around is safe. If and when the bug is fixed */ /*the if condition would never be true and the code would never get executed */ /********************************************************************************************************************/ if ((KoList::level(cursor.block()) != KoList::level(currentBlock)) && currentBlock.text().length()) { if (!currentList) { QTextCursor tmp(cursor); tmp.setPosition(tmp.block().previous().position()); currentList = KoTextDocument(tmp.document()).list(tmp.block()); } currentList->add(cursor.block(), KoList::level(currentBlock)); } /********************************************************************************************************************/ // Finally insert all the contents of the block into the main document. QTextBlock::iterator it; for (it = currentBlock.begin(); !(it.atEnd()); ++it) { QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) { cursor.insertText(currentFragment.text(), currentFragment.charFormat()); } } } } int KoChangeTracker::fragmentLength(const QTextDocumentFragment &fragment) { QTextDocument tempDoc; QTextCursor tempCursor(&tempDoc); tempCursor.insertFragment(fragment); int length = 0; bool deletedListItem = false; for (QTextBlock currentBlock = tempDoc.begin(); currentBlock != tempDoc.end(); currentBlock = currentBlock.next()) { tempCursor.setPosition(currentBlock.position()); if (tempCursor.currentList()) { if (currentBlock != tempDoc.begin() && deletedListItem) length += 1; //For the Block separator } else if (tempCursor.currentTable()) { QTextTable *deletedTable = tempCursor.currentTable(); int numRows = deletedTable->rows(); int numColumns = deletedTable->columns(); for (int i=0; icellAt(i,j).lastCursorPosition().position() - deletedTable->cellAt(i,j).firstCursorPosition().position()); } } tempCursor.setPosition(deletedTable->cellAt(numRows-1,numColumns-1).lastCursorPosition().position()); currentBlock = tempCursor.block(); length += 1; continue; } else { if ((currentBlock != tempDoc.begin()) && !(QTextCursor(currentBlock.previous()).currentTable())) length += 1; //For the Block Separator } QTextBlock::iterator it; for (it = currentBlock.begin(); !(it.atEnd()); ++it) { QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) length += currentFragment.text().length(); } } return length; } diff --git a/plugins/flake/textshape/kotext/opendocument/KoTextWriter_p.cpp b/plugins/flake/textshape/kotext/opendocument/KoTextWriter_p.cpp index a78a2c79d2..8412988323 100644 --- a/plugins/flake/textshape/kotext/opendocument/KoTextWriter_p.cpp +++ b/plugins/flake/textshape/kotext/opendocument/KoTextWriter_p.cpp @@ -1,1148 +1,1116 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2014 Denis Kuplyakov * * 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; 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 #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 "TextDebug.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; } typedef QPair Attribute; KoTextWriter::Private::Private(KoShapeSavingContext &context) : rdfData(0) , sharedData(0) , styleManager(0) , document(0) , writer(0) , context(context) { currentPairedInlineObjectsStack = new QStack(); writer = &context.xmlWriter(); } 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); // Here we are going to detect all sections that // are positioned entirely inside selection. // They will stay untouched, and others will be omitted. // So we are using stack to detect them, by going through // the selection and finding open/close pairs. QSet entireWithinSectionNames; QStack sectionNamesStack; QTextCursor cur(document); cur.setPosition(from); while (to == -1 || cur.position() <= to) { if (cur.block().position() >= from) { // Begin of the block is inside selection. foreach (const KoSection *sec, KoSectionUtils::sectionStartings(cur.blockFormat())) { sectionNamesStack.push_back(sec->name()); } } if (to == -1 || cur.block().position() + cur.block().length() - 1 <= to) { // End of the block is inside selection. foreach (const KoSectionEnd *sec, KoSectionUtils::sectionEndings(cur.blockFormat())) { if (!sectionNamesStack.empty() && sectionNamesStack.top() == sec->name()) { sectionNamesStack.pop(); entireWithinSectionNames.insert(sec->name()); } } } if (!KoSectionUtils::getNextBlock(cur)) { break; } } 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(); foreach (const KoSection *section, KoSectionUtils::sectionStartings(format)) { // We are writing in only sections, that are completely inside selection. if (entireWithinSectionNames.contains(section->name())) { 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; } if (cursor.currentTable() && cursor.currentTable() != currentTable) { // Call the code to save the table.... saveTable(cursor.currentTable(), listStyles, from, to); // 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; } saveParagraph(block, from, to); foreach (const KoSectionEnd *sectionEnd, KoSectionUtils::sectionEndings(format)) { // We are writing in only sections, that are completely inside selection. if (entireWithinSectionNames.contains(sectionEnd->name())) { sectionEnd->saveOdf(context); } } block = block.next(); } // while 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; } //---------------------------- PRIVATE ----------------------------------------------------------- void KoTextWriter::Private::openTagRegion(ElementType elementType, TagInformation& tagInformation) { //debugText << "tag:" << tagInformation.name() << openedTagStack.size(); if (tagInformation.name()) { writer->startElement(tagInformation.name(), elementType != ParagraphOrHeader); foreach (const Attribute &attribute, tagInformation.attributes()) { writer->addAttribute(attribute.first.toLocal8Bit(), attribute.second); } } openedTagStack.push(tagInformation.name()); //debugText << "stack" << openedTagStack.size(); } void KoTextWriter::Private::closeTagRegion() { // the tag needs to be closed even if there is no change tracking //debugText << "stack" << openedTagStack.size(); const char *tagName = openedTagStack.pop(); //debugText << "tag:" << tagName << openedTagStack.size(); if (tagName) { writer->endElement(); // close the tag } } 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) +void KoTextWriter::Private::saveInlineRdf(KoTextInlineRdf* , TagInformation* ) { -#ifndef KOXML_USE_QDOM - QBuffer rdfXmlData; - KoXmlWriter rdfXmlWriter(&rdfXmlData); - rdfXmlWriter.startDocument("rdf"); - rdfXmlWriter.startElement("rdf"); - rdf->saveOdf(context, &rdfXmlWriter); - rdfXmlWriter.endElement(); - rdfXmlWriter.endDocument(); - - KoXmlDocument xmlReader; - xmlReader.setContent(rdfXmlData.data(), true); - KoXmlElement mainElement = xmlReader.documentElement(); - foreach (const Attribute &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)); - } -#endif } /* 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"); } openTagRegion(KoTextWriter::Private::ParagraphOrHeader, blockTagInformation); QString styleName = saveParagraphStyle(block); if (!styleName.isEmpty()) writer->addAttribute("text:style-name", styleName); KoElementReference xmlid; xmlid.invalidate(); QTextBlock currentBlock = block; KoTextBlockData blockData(currentBlock); if (blockData.saveXmlID()) { xmlid = context.xmlid(&blockData); xmlid.saveOdf(writer, KoElementReference::TextId); } // 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 debugText << "have inline rdf xmlid:" << inlineRdf->xmlId() << "active xml id" << xmlid.toString(); inlineRdf->saveOdf(context, writer, xmlid); } const KoTextRangeManager *textRangeManager = KoTextDocument(block.document()).textRangeManager(); if (textRangeManager) { // write tags for ranges which end at the first position of the block const QHash endingTextRangesAtStart = textRangeManager->textRangesChangingWithin(block.document(), block.position(), block.position(), globalFrom, globalTo); foreach (const KoTextRange *range, endingTextRangesAtStart) { range->saveOdf(context, block.position(), KoTextRange::EndTag); } } QString previousFragmentLink; // 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(); if ((!previousFragmentLink.isEmpty()) && (charFormat.anchorHref() != previousFragmentLink || !charFormat.isAnchor())) { // Close the current text:a closeTagRegion(); previousFragmentLink.clear(); } 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 debugText << "have inline rdf xmlid:" << inlineRdf->xmlId(); saveInlineRdf(inlineRdf, &linkTagInformation); } openTagRegion(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) { 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 = textRangeManager ? textRangeManager->textRangesChangingWithin(block.document(), currentFragment.position(), currentFragment.position()+1, globalFrom, (globalTo==-1)?-1:globalTo+1) : QHash(); // 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) { inlineObject->saveOdf(context); } 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)) { debugText << "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); } openTagRegion(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 = textRangeManager ? textRangeManager->textRangesChangingWithin(block.document(), spanFrom, spanTo, globalFrom, (globalTo==-1)?-1:globalTo+1) : QHash(); // 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(); } // if (inlineObject) previousCharFormat = charFormat; lastEndPosition = fragmentEnd; } } if (!previousFragmentLink.isEmpty()) { writer->endElement(); } if (it.atEnd() && textRangeManager && ((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 = textRangeManager->textRangesChangingWithin(block.document(), 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); } } closeTagRegion(); } void KoTextWriter::Private::saveTable(QTextTable *table, QHash &listStyles, int from, int to) { 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 firstColumn = 0; int lastColumn = table->columns() -1; int firstRow = 0; int lastRow = table->rows() -1; if (to != -1 && from >= table->firstPosition() && to <= table->lastPosition()) { firstColumn = table->cellAt(from).column(); firstRow = table->cellAt(from).row(); lastColumn = table->cellAt(to).column(); lastRow = table->cellAt(to).row(); if (firstColumn == lastColumn && firstRow == lastRow && from >= table->firstPosition()) { // we only selected something inside a single cell so don't save a table writeBlocks(table->document(), from, to, listStyles, table); return; } } openTagRegion(KoTextWriter::Private::Table, tableTagInformation); for (int c = firstColumn ; c <= lastColumn; c++) { KoTableColumnStyle columnStyle = tcarManager.columnStyle(c); int repetition = 0; for (; repetition <= (lastColumn - 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); openTagRegion(KoTextWriter::Private::TableColumn, tableColumnInformation); closeTagRegion(); c += repetition; } if (numberHeadingRows) writer->startElement("table:table-header-rows"); // TODO make work for copying part of table that has header rows - copy header rows additionally or not ? for (int r = firstRow; r <= lastRow; 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); } openTagRegion(KoTextWriter::Private::TableRow, tableRowInformation); for (int c = firstColumn; c <= lastColumn; c++) { QTextTableCell cell = table->cellAt(r, c); 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); openTagRegion(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"); } openTagRegion(KoTextWriter::Private::TableCell, tableCellInformation); } closeTagRegion(); } closeTagRegion(); if (r + 1 == numberHeadingRows) { writer->endElement(); // table:table-header-rows writer->startElement("table:table-rows"); } } if (numberHeadingRows) writer->endElement(); // table:table-rows closeTagRegion(); } 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); bool listStarted = false; 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); } openTagRegion(KoTextWriter::Private::List, listTagInformation); } if (!headingLevel) { 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); openTagRegion(KoTextWriter::Private::NumberedParagraph, paraTagInformation); writeBlocks(textDocument.document(), block.position(), block.position() + block.length() - 1, listStyles, currentTable, textList); closeTagRegion(); } else { const bool listHeader = blockFormat.boolProperty(KoParagraphStyle::IsListHeader)|| blockFormat.boolProperty(KoParagraphStyle::UnnumberedListItem); 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) { openTagRegion(KoTextWriter::Private::ListItem, listItemTagInformation); } else { // This is a sub-list. So check for a list-change openTagRegion(KoTextWriter::Private::List, listItemTagInformation); } if (KoListStyle::isNumberingStyle(textList->format().style())) { KoTextBlockData blockData(block); 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(); } 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(); } return block; } 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.setCodec("UTF-8"); nameSpacesStream << ""; generatedXmlString.prepend(nameSpaceDefinitions); generatedXmlString.append(""); } -void KoTextWriter::Private::writeAttributes(QTextStream &outputXmlStream, KoXmlElement &element) +void KoTextWriter::Private::writeAttributes(QTextStream &, KoXmlElement &) { -#ifndef KOXML_USE_QDOM - - QList > attributes = element.attributeFullNames(); - foreach (const Attribute &attributeNamePair, attributes) { - if (attributeNamePair.first == KoXmlNS::text) { - outputXmlStream << " text:" << attributeNamePair.second << "="; - outputXmlStream << "\"" << element.attributeNS(KoXmlNS::text, attributeNamePair.second) << "\""; - } else { - //To Be Added when needed - } - } -#endif } 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") && !KoXml::childNodesCount(element)) { 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/plugins/flake/textshape/kotext/styles/KoTableCellStyle.cpp b/plugins/flake/textshape/kotext/styles/KoTableCellStyle.cpp index 51ee414ddd..b9e3ba7d02 100644 --- a/plugins/flake/textshape/kotext/styles/KoTableCellStyle.cpp +++ b/plugins/flake/textshape/kotext/styles/KoTableCellStyle.cpp @@ -1,1057 +1,1051 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Roopesh Chander * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2009 KO GmbH * Copyright (C) 2011 Pierre Ducroquet * * 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 "KoTableCellStyle.h" #include "KoTableCellStyle_p.h" #include #include #include #include #include "KoParagraphStyle.h" #include #include #include #include #include #include #include #include #include "TextDebug.h" #include KoTableCellStyle::RotationAlignment rotationAlignmentFromString(const QString& align) { if (align == "bottom") return KoTableCellStyle::RAlignBottom; if (align == "center") return KoTableCellStyle::RAlignCenter; if (align == "top") return KoTableCellStyle::RAlignTop; return KoTableCellStyle::RAlignNone; } QString rotationAlignmentToString(KoTableCellStyle::RotationAlignment align) { if (align == KoTableCellStyle::RAlignBottom) return "bottom"; if (align == KoTableCellStyle::RAlignTop) return "top"; if (align == KoTableCellStyle::RAlignCenter) return "center"; return "none"; } KoTableCellStylePrivate::KoTableCellStylePrivate() : paragraphStyle(0) , parentStyle(0) , next(0) { } KoTableCellStylePrivate::~KoTableCellStylePrivate() { } void KoTableCellStylePrivate::setProperty(int key, const QVariant &value) { stylesPrivate.add(key, value); } KoTableCellStyle::KoTableCellStyle(QObject *parent) : QObject(parent) , d_ptr(new KoTableCellStylePrivate) { Q_D(KoTableCellStyle); d->paragraphStyle = new KoParagraphStyle(this); } KoTableCellStyle::KoTableCellStyle(const QTextTableCellFormat &format, QObject *parent) : QObject(parent) , d_ptr(new KoTableCellStylePrivate) { Q_D(KoTableCellStyle); d->stylesPrivate = format.properties(); d->paragraphStyle = new KoParagraphStyle(this); } KoTableCellStyle::KoTableCellStyle(const KoTableCellStyle &other) :QObject(other.parent()) , d_ptr(new KoTableCellStylePrivate) { Q_D(KoTableCellStyle); copyProperties(&other); d->paragraphStyle = other.paragraphStyle()->clone(this); } KoTableCellStyle& KoTableCellStyle::operator=(const KoTableCellStyle &other) { Q_D(KoTableCellStyle); if (this == &other) { return *this; } copyProperties(&other); d->paragraphStyle = other.paragraphStyle()->clone(this); return *this; } KoTableCellStyle::~KoTableCellStyle() { delete d_ptr; } KoTableCellStyle *KoTableCellStyle::fromTableCell(const QTextTableCell &tableCell, QObject *parent) { QTextTableCellFormat tableCellFormat = tableCell.format().toTableCellFormat(); return new KoTableCellStyle(tableCellFormat, parent); } QTextCharFormat KoTableCellStyle::cleanCharFormat(const QTextCharFormat &charFormat) { if (charFormat.isTableCellFormat()) { QTextTableCellFormat format; const QMap props = charFormat.properties(); QMap::const_iterator it = props.begin(); while (it != props.end()) { // lets save all Qt's table cell properties if (it.key()>=QTextFormat::TableCellRowSpan && it.key()=StyleId && it.key()parentStyle = parent; } void KoTableCellStyle::setLeftPadding(qreal padding) { setProperty(QTextFormat::TableCellLeftPadding, padding); } void KoTableCellStyle::setTopPadding(qreal padding) { setProperty(QTextFormat::TableCellTopPadding, padding); } void KoTableCellStyle::setRightPadding(qreal padding) { setProperty(QTextFormat::TableCellRightPadding, padding); } void KoTableCellStyle::setBottomPadding(qreal padding) { setProperty(QTextFormat::TableCellBottomPadding, padding); } qreal KoTableCellStyle::leftPadding() const { return propertyDouble(QTextFormat::TableCellLeftPadding); } qreal KoTableCellStyle::rightPadding() const { return propertyDouble(QTextFormat::TableCellRightPadding); } qreal KoTableCellStyle::topPadding() const { return propertyDouble(QTextFormat::TableCellTopPadding); } qreal KoTableCellStyle::bottomPadding() const { return propertyDouble(QTextFormat::TableCellBottomPadding); } void KoTableCellStyle::setPadding(qreal padding) { setBottomPadding(padding); setTopPadding(padding); setRightPadding(padding); setLeftPadding(padding); } KoParagraphStyle *KoTableCellStyle::paragraphStyle() const { Q_D(const KoTableCellStyle); return d->paragraphStyle; } bool KoTableCellStyle::shrinkToFit() const { return propertyBoolean(ShrinkToFit); } void KoTableCellStyle::setShrinkToFit(bool state) { setProperty(ShrinkToFit, state); } void KoTableCellStyle::setProperty(int key, const QVariant &value) { Q_D(KoTableCellStyle); if (d->parentStyle) { QVariant var = d->parentStyle->value(key); if (!var.isNull() && var == value) { // same as parent, so its actually a reset. d->stylesPrivate.remove(key); return; } } d->stylesPrivate.add(key, value); } void KoTableCellStyle::remove(int key) { Q_D(KoTableCellStyle); d->stylesPrivate.remove(key); } QVariant KoTableCellStyle::value(int key) const { Q_D(const KoTableCellStyle); QVariant var = d->stylesPrivate.value(key); if (var.isNull() && d->parentStyle) var = d->parentStyle->value(key); return var; } bool KoTableCellStyle::hasProperty(int key) const { Q_D(const KoTableCellStyle); return d->stylesPrivate.contains(key); } qreal KoTableCellStyle::propertyDouble(int key) const { QVariant variant = value(key); if (variant.isNull()) return 0.0; return variant.toDouble(); } QPen KoTableCellStyle::propertyPen(int key) const { const QVariant prop = value(key); if (prop.userType() != QVariant::Pen) return QPen(Qt::NoPen); return qvariant_cast(prop); } int KoTableCellStyle::propertyInt(int key) const { QVariant variant = value(key); if (variant.isNull()) return 0; return variant.toInt(); } bool KoTableCellStyle::propertyBoolean(int key) const { QVariant variant = value(key); if (variant.isNull()) return false; return variant.toBool(); } QColor KoTableCellStyle::propertyColor(int key) const { QVariant variant = value(key); if (variant.isNull()) { return QColor(); } return qvariant_cast(variant); } void KoTableCellStyle::applyStyle(QTextTableCellFormat &format) const { Q_D(const KoTableCellStyle); if (d->parentStyle) { d->parentStyle->applyStyle(format); } QList keys = d->stylesPrivate.keys(); for (int i = 0; i < keys.count(); i++) { QVariant variant = d->stylesPrivate.value(keys[i]); format.setProperty(keys[i], variant); } // Hack : build KoBorder here if (d->parentStyle && d->parentStyle->hasProperty(Borders) && this->hasProperty(Borders)) { KoBorder parentBorder = d->parentStyle->borders(); KoBorder childBorder = this->borders(); if (childBorder.hasBorder(KoBorder::LeftBorder)) parentBorder.setBorderData(KoBorder::LeftBorder, childBorder.borderData(KoBorder::LeftBorder)); if (childBorder.hasBorder(KoBorder::RightBorder)) parentBorder.setBorderData(KoBorder::RightBorder, childBorder.borderData(KoBorder::RightBorder)); if (childBorder.hasBorder(KoBorder::TopBorder)) parentBorder.setBorderData(KoBorder::TopBorder, childBorder.borderData(KoBorder::TopBorder)); if (childBorder.hasBorder(KoBorder::BottomBorder)) parentBorder.setBorderData(KoBorder::BottomBorder, childBorder.borderData(KoBorder::BottomBorder)); if (childBorder.hasBorder(KoBorder::BltrBorder)) parentBorder.setBorderData(KoBorder::BltrBorder, childBorder.borderData(KoBorder::BltrBorder)); if (childBorder.hasBorder(KoBorder::TlbrBorder)) parentBorder.setBorderData(KoBorder::TlbrBorder, childBorder.borderData(KoBorder::TlbrBorder)); format.setProperty(Borders, QVariant::fromValue(parentBorder)); } } void KoTableCellStyle::applyStyle(QTextTableCell &cell) const { Q_D(const KoTableCellStyle); QTextTableCellFormat format = cell.format().toTableCellFormat(); applyStyle(format); if (d->paragraphStyle) { d->paragraphStyle->KoCharacterStyle::applyStyle(format); } cell.setFormat(format); } void KoTableCellStyle::setBackground(const QBrush &brush) { setProperty(CellBackgroundBrush, brush); } void KoTableCellStyle::clearBackground() { Q_D(KoTableCellStyle); d->stylesPrivate.remove(CellBackgroundBrush); } QBrush KoTableCellStyle::background() const { Q_D(const KoTableCellStyle); QVariant variant = d->stylesPrivate.value(CellBackgroundBrush); if (variant.isNull()) { return QBrush(); } return qvariant_cast(variant); } void KoTableCellStyle::setWrap(bool state) { setProperty(Wrap, state); } bool KoTableCellStyle::wrap() const { return propertyBoolean(Wrap); } void KoTableCellStyle::setAlignment(Qt::Alignment alignment) { setProperty(VerticalAlignment, (int) alignment); } Qt::Alignment KoTableCellStyle::alignment() const { if (propertyInt(VerticalAlignment) == 0) return Qt::AlignTop; return static_cast(propertyInt(VerticalAlignment)); } KoTableCellStyle *KoTableCellStyle::parentStyle() const { Q_D(const KoTableCellStyle); return d->parentStyle; } QString KoTableCellStyle::name() const { Q_D(const KoTableCellStyle); return d->name; } void KoTableCellStyle::setName(const QString &name) { Q_D(KoTableCellStyle); if (name == d->name) return; d->name = name; emit nameChanged(name); } int KoTableCellStyle::styleId() const { return propertyInt(StyleId); } void KoTableCellStyle::setStyleId(int id) { Q_D(KoTableCellStyle); setProperty(StyleId, id); if (d->next == 0) d->next = id; } QString KoTableCellStyle::masterPageName() const { return value(MasterPageName).toString(); } void KoTableCellStyle::setMasterPageName(const QString &name) { setProperty(MasterPageName, name); } void KoTableCellStyle::setCellProtection(KoTableCellStyle::CellProtectionFlag protection) { setProperty(CellProtection, protection); } KoTableCellStyle::CellProtectionFlag KoTableCellStyle::cellProtection() const { return (CellProtectionFlag) propertyInt(CellProtection); } void KoTableCellStyle::setTextDirection(KoText::Direction value) { setProperty(TextWritingMode, value); } KoText::Direction KoTableCellStyle::textDirection() const { return (KoText::Direction) propertyInt(TextWritingMode); } bool KoTableCellStyle::printContent() const { return (hasProperty(PrintContent) && propertyBoolean(PrintContent)); } void KoTableCellStyle::setPrintContent(bool state) { setProperty(PrintContent, state); } bool KoTableCellStyle::repeatContent() const { return (hasProperty(RepeatContent) && propertyBoolean(RepeatContent)); } void KoTableCellStyle::setRepeatContent(bool state) { setProperty(RepeatContent, state); } int KoTableCellStyle::decimalPlaces() const { return propertyInt(DecimalPlaces); } void KoTableCellStyle::setDecimalPlaces(int places) { setProperty(DecimalPlaces, places); } bool KoTableCellStyle::alignFromType() const { return (hasProperty(AlignFromType) && propertyBoolean(AlignFromType)); } void KoTableCellStyle::setAlignFromType(bool state) { setProperty(AlignFromType, state); } qreal KoTableCellStyle::rotationAngle() const { return propertyDouble(RotationAngle); } void KoTableCellStyle::setRotationAngle(qreal value) { if (value >= 0) setProperty(RotationAngle, value); } void KoTableCellStyle::setVerticalGlyphOrientation(bool state) { setProperty(VerticalGlyphOrientation, state); } bool KoTableCellStyle::verticalGlyphOrientation() const { if (hasProperty(VerticalGlyphOrientation)) return propertyBoolean(VerticalGlyphOrientation); return true; } void KoTableCellStyle::setDirection(KoTableCellStyle::CellTextDirection direction) { setProperty(Direction, direction); } KoBorder KoTableCellStyle::borders() const { if (hasProperty(Borders)) return value(Borders).value(); return KoBorder(); } void KoTableCellStyle::setBorders(const KoBorder& borders) { setProperty(Borders, QVariant::fromValue(borders)); } KoShadowStyle KoTableCellStyle::shadow() const { if (hasProperty(Shadow)) return value(Shadow).value(); return KoShadowStyle(); } void KoTableCellStyle::setShadow(const KoShadowStyle& shadow) { setProperty(Shadow, QVariant::fromValue(shadow)); } KoTableCellStyle::RotationAlignment KoTableCellStyle::rotationAlignment() const { return static_cast(propertyInt(RotationAlign)); } void KoTableCellStyle::setRotationAlignment(KoTableCellStyle::RotationAlignment align) { setProperty(RotationAlign, align); } KoTableCellStyle::CellTextDirection KoTableCellStyle::direction() const { if (hasProperty(Direction)) return (KoTableCellStyle::CellTextDirection) propertyInt(Direction); return KoTableCellStyle::Default; } void KoTableCellStyle::loadOdf(const KoXmlElement *element, KoShapeLoadingContext &scontext) { KoOdfLoadingContext &context = scontext.odfLoadingContext(); Q_D(KoTableCellStyle); if (element->hasAttributeNS(KoXmlNS::style, "display-name")) d->name = element->attributeNS(KoXmlNS::style, "display-name", QString()); if (d->name.isEmpty()) // if no style:display-name is given us the style:name d->name = element->attributeNS(KoXmlNS::style, "name", QString()); QString masterPage = element->attributeNS(KoXmlNS::style, "master-page-name", QString()); if (! masterPage.isEmpty()) { setMasterPageName(masterPage); } paragraphStyle()->loadOdf(element, scontext, true); // load the par and char properties -#ifndef KOXML_USE_QDOM - // Borders - we don't handle inheritance unfortunately - hope it's not a big problem - KoBorder borders = this->borders(); - borders.loadOdf(element->namedItemNS(KoXmlNS::style, "table-cell-properties").toElement()); - setBorders(borders); -#endif context.styleStack().save(); QString family = element->attributeNS(KoXmlNS::style, "family", "table-cell"); context.addStyles(element, family.toLocal8Bit().constData()); // Load all parents - only because we don't support inheritance. context.styleStack().setTypeProperties("table-cell"); loadOdfProperties(scontext, context.styleStack()); context.styleStack().setTypeProperties("graphic"); loadOdfProperties(scontext, context.styleStack()); context.styleStack().restore(); } void KoTableCellStyle::loadOdfProperties(KoShapeLoadingContext &context, KoStyleStack &styleStack) { // Padding if (styleStack.hasProperty(KoXmlNS::fo, "padding-left")) setLeftPadding(KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "padding-left"))); if (styleStack.hasProperty(KoXmlNS::fo, "padding-right")) setRightPadding(KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "padding-right"))); if (styleStack.hasProperty(KoXmlNS::fo, "padding-top")) setTopPadding(KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "padding-top"))); if (styleStack.hasProperty(KoXmlNS::fo, "padding-bottom")) setBottomPadding(KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "padding-bottom"))); if (styleStack.hasProperty(KoXmlNS::fo, "padding")) setPadding(KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "padding"))); if (styleStack.hasProperty(KoXmlNS::style, "shadow")) { KoShadowStyle shadow; if (shadow.loadOdf(styleStack.property(KoXmlNS::style, "shadow"))) { setShadow(shadow); } } // The fo:background-color attribute specifies the background color of a cell. if (styleStack.hasProperty(KoXmlNS::fo, "background-color")) { const QString bgcolor = styleStack.property(KoXmlNS::fo, "background-color"); QBrush brush = background(); if (bgcolor == "transparent") setBackground(Qt::NoBrush); else { if (brush.style() == Qt::NoBrush) brush.setStyle(Qt::SolidPattern); brush.setColor(bgcolor); // #rrggbb format setBackground(brush); } } QString fillStyle = styleStack.property(KoXmlNS::draw, "fill"); if (fillStyle == "solid" || fillStyle == "hatch") { styleStack.save(); QBrush brush = KoOdfGraphicStyles::loadOdfFillStyle(styleStack, fillStyle, context.odfLoadingContext().stylesReader()); setBackground(brush); styleStack.restore(); } if (styleStack.hasProperty(KoXmlNS::style, "shrink-to-fit")) { setShrinkToFit(styleStack.property(KoXmlNS::style, "shrink-to-fit") == "true"); } if (styleStack.hasProperty(KoXmlNS::style, "print-content")) { setPrintContent(styleStack.property(KoXmlNS::style, "print-content") == "true"); } if (styleStack.hasProperty(KoXmlNS::style, "repeat-content")) { setRepeatContent(styleStack.property(KoXmlNS::style, "repeat-content") == "true"); } if (styleStack.hasProperty(KoXmlNS::style, "repeat-content")) { setRepeatContent(styleStack.property(KoXmlNS::style, "repeat-content") == "true"); } if (styleStack.hasProperty(KoXmlNS::style, "decimal-places")) { bool ok; int value = styleStack.property(KoXmlNS::style, "decimal-places").toInt(&ok); if (ok) setDecimalPlaces(value); } if (styleStack.hasProperty(KoXmlNS::style, "rotation-angle")) { setRotationAngle(KoUnit::parseAngle(styleStack.property(KoXmlNS::style, "rotation-angle"))); } if (styleStack.hasProperty(KoXmlNS::style, "glyph-orientation-vertical")) { setVerticalGlyphOrientation(styleStack.property(KoXmlNS::style, "glyph-orientation-vertical") == "auto"); } if (styleStack.hasProperty(KoXmlNS::style, "direction")) { if (styleStack.property(KoXmlNS::style, "direction") == "ltr") setDirection(KoTableCellStyle::LeftToRight); else setDirection(KoTableCellStyle::TopToBottom); } if (styleStack.hasProperty(KoXmlNS::style, "rotation-align")) { setRotationAlignment(rotationAlignmentFromString(styleStack.property(KoXmlNS::style, "rotation-align"))); } if (styleStack.hasProperty(KoXmlNS::style, "text-align-source")) { setAlignFromType(styleStack.property(KoXmlNS::style, "text-align-source") == "value-type"); } if (styleStack.hasProperty(KoXmlNS::fo, "wrap-option")) { setWrap(styleStack.property(KoXmlNS::fo, "wrap-option") == "wrap"); } if (styleStack.hasProperty(KoXmlNS::style, "cell-protect")) { QString protection = styleStack.property(KoXmlNS::style, "cell-protect"); if (protection == "none") setCellProtection(NoProtection); else if (protection == "hidden-and-protected") setCellProtection(HiddenAndProtected); else if (protection == "protected") setCellProtection(Protected); else if (protection == "formula-hidden") setCellProtection(FormulaHidden); else if ((protection == "protected formula-hidden") || (protection == "formula-hidden protected")) setCellProtection(ProtectedAndFormulaHidden); } // Alignment const QString verticalAlign(styleStack.property(KoXmlNS::style, "vertical-align")); if (!verticalAlign.isEmpty()) { if (verticalAlign == "automatic") setAlignment((Qt::AlignmentFlag) 0); else setAlignment(KoText::valignmentFromString(verticalAlign)); } if (styleStack.hasProperty(KoXmlNS::style, "writing-mode")) setTextDirection(KoText::directionFromString(styleStack.property(KoXmlNS::style, "writing-mode"))); } void KoTableCellStyle::copyProperties(const KoTableCellStyle *style) { Q_D(KoTableCellStyle); const KoTableCellStylePrivate *styleD = static_cast(style->d_func()); d->stylesPrivate = styleD->stylesPrivate; setName(style->name()); // make sure we emit property change d->next = styleD->next; d->parentStyle = styleD->parentStyle; } KoTableCellStyle *KoTableCellStyle::clone(QObject *parent) { KoTableCellStyle *newStyle = new KoTableCellStyle(parent); newStyle->copyProperties(this); return newStyle; } bool KoTableCellStyle::operator==(const KoTableCellStyle &other) const { Q_D(const KoTableCellStyle); const KoTableCellStylePrivate *otherD = static_cast(other.d_func()); return otherD->stylesPrivate == d->stylesPrivate; } void KoTableCellStyle::removeDuplicates(const KoTableCellStyle &other) { Q_D(KoTableCellStyle); const KoTableCellStylePrivate *otherD = static_cast(other.d_func()); d->stylesPrivate.removeDuplicates(otherD->stylesPrivate); } void KoTableCellStyle::saveOdf(KoGenStyle &style, KoShapeSavingContext &context) { Q_D(KoTableCellStyle); QList keys = d->stylesPrivate.keys(); bool donePadding = false; if (hasProperty(QTextFormat::TableCellLeftPadding) && hasProperty(QTextFormat::TableCellRightPadding) && hasProperty(QTextFormat::TableCellTopPadding) && hasProperty(QTextFormat::TableCellBottomPadding) && leftPadding() == rightPadding() && topPadding() == bottomPadding() && topPadding() == leftPadding()) { donePadding = true; style.addPropertyPt("fo:padding", leftPadding(), KoGenStyle::TableCellType); } Q_FOREACH (int key, keys) { if (key == CellBackgroundBrush) { QBrush backBrush = background(); if (backBrush.style() != Qt::NoBrush) style.addProperty("fo:background-color", backBrush.color().name(), KoGenStyle::TableCellType); else style.addProperty("fo:background-color", "transparent", KoGenStyle::TableCellType); } else if (key == VerticalAlignment) { if (propertyInt(VerticalAlignment) == 0) style.addProperty("style:vertical-align", "automatic", KoGenStyle::TableCellType); else style.addProperty("style:vertical-align", KoText::valignmentToString(alignment()), KoGenStyle::TableCellType); } else if ((key == QTextFormat::TableCellLeftPadding) && (!donePadding)) { style.addPropertyPt("fo:padding-left", leftPadding(), KoGenStyle::TableCellType); } else if ((key == QTextFormat::TableCellRightPadding) && (!donePadding)) { style.addPropertyPt("fo:padding-right", rightPadding(), KoGenStyle::TableCellType); } else if ((key == QTextFormat::TableCellTopPadding) && (!donePadding)) { style.addPropertyPt("fo:padding-top", topPadding(), KoGenStyle::TableCellType); } else if ((key == QTextFormat::TableCellBottomPadding) && (!donePadding)) { style.addPropertyPt("fo:padding-bottom", bottomPadding(), KoGenStyle::TableCellType); } else if (key == ShrinkToFit) { style.addProperty("style:shrink-to-fit", shrinkToFit(), KoGenStyle::TableCellType); } else if (key == PrintContent) { style.addProperty("style:print-content", printContent(), KoGenStyle::TableCellType); } else if (key == RepeatContent) { style.addProperty("style:repeat-content", repeatContent(), KoGenStyle::TableCellType); } else if (key == DecimalPlaces) { style.addProperty("style:decimal-places", decimalPlaces(), KoGenStyle::TableCellType); } else if (key == RotationAngle) { style.addProperty("style:rotation-angle", QString::number(rotationAngle()), KoGenStyle::TableCellType); } else if (key == Wrap) { if (wrap()) style.addProperty("fo:wrap-option", "wrap", KoGenStyle::TableCellType); else style.addProperty("fo:wrap-option", "no-wrap", KoGenStyle::TableCellType); } else if (key == Direction) { if (direction() == LeftToRight) style.addProperty("style:direction", "ltr", KoGenStyle::TableCellType); else if (direction() == TopToBottom) style.addProperty("style:direction", "ttb", KoGenStyle::TableCellType); } else if (key == CellProtection) { if (cellProtection() == NoProtection) style.addProperty("style:cell-protect", "none", KoGenStyle::TableCellType); else if (cellProtection() == HiddenAndProtected) style.addProperty("style:cell-protect", "hidden-and-protected", KoGenStyle::TableCellType); else if (cellProtection() == Protected) style.addProperty("style:cell-protect", "protected", KoGenStyle::TableCellType); else if (cellProtection() == FormulaHidden) style.addProperty("style:cell-protect", "formula-hidden", KoGenStyle::TableCellType); else if (cellProtection() == ProtectedAndFormulaHidden) style.addProperty("style:cell-protect", "protected formula-hidden", KoGenStyle::TableCellType); } else if (key == AlignFromType) { if (alignFromType()) style.addProperty("style:text-align-source", "value-type", KoGenStyle::TableCellType); else style.addProperty("style:text-align-source", "fix", KoGenStyle::TableCellType); } else if (key == RotationAlign) { style.addProperty("style:rotation-align", rotationAlignmentToString(rotationAlignment()), KoGenStyle::TableCellType); } else if (key == TextWritingMode) { style.addProperty("style:writing-mode", KoText::directionToString(textDirection()), KoGenStyle::TableCellType); } else if (key == VerticalGlyphOrientation) { if (verticalGlyphOrientation()) style.addProperty("style:glyph-orientation-vertical", "auto", KoGenStyle::TableCellType); else style.addProperty("style:glyph-orientation-vertical", "0", KoGenStyle::TableCellType); } else if (key == Borders) { borders().saveOdf(style, KoGenStyle::TableCellType); } else if (key == Shadow) { style.addProperty("style:shadow", shadow().saveOdf()); } } if (d->paragraphStyle) { d->paragraphStyle->saveOdf(style, context); } } void KoTableCellStyle::setEdge(KoBorder::BorderSide side, KoBorder::BorderStyle style, qreal width, const QColor &color) { KoBorder::BorderData edge; qreal innerWidth = 0; qreal middleWidth = 0; qreal space = 0; QVector dashes; switch (style) { case KoBorder::BorderNone: width = 0.0; break; case KoBorder::BorderDouble: innerWidth = space = width/3; //some nice default look width -= (space + innerWidth); edge.outerPen.setStyle(Qt::SolidLine); break; case KoBorder::BorderDotted: dashes << 1 << 1; edge.outerPen.setDashPattern(dashes); break; case KoBorder::BorderDashed: dashes << 4 << 1; edge.outerPen.setDashPattern(dashes); break; case KoBorder::BorderDashedLong: { dashes << 4 << 4; edge.outerPen.setDashPattern(dashes); break; } case KoBorder::BorderTriple: innerWidth = middleWidth = space = width/6; width -= (space + innerWidth); edge.outerPen.setStyle(Qt::SolidLine); break; case KoBorder::BorderDashDot: dashes << 3 << 3<< 7 << 3; edge.outerPen.setDashPattern(dashes); break; case KoBorder::BorderDashDotDot: dashes << 2 << 2<< 6 << 2 << 2 << 2; edge.outerPen.setDashPattern(dashes); break; case KoBorder::BorderWave: edge.outerPen.setStyle(Qt::SolidLine); break; case KoBorder::BorderSlash: edge.outerPen.setStyle(Qt::SolidLine); break; case KoBorder::BorderDoubleWave: innerWidth = space = width/3; //some nice default look width -= (space + innerWidth); edge.outerPen.setStyle(Qt::SolidLine); break; default: edge.outerPen.setStyle(Qt::SolidLine); break; } edge.outerPen.setColor(color); edge.outerPen.setJoinStyle(Qt::MiterJoin); edge.outerPen.setCapStyle(Qt::FlatCap); edge.outerPen.setWidthF(width); edge.spacing = space; edge.innerPen = edge.outerPen; edge.innerPen.setWidthF(innerWidth); QPen middlePen; middlePen = edge.outerPen; middlePen.setWidthF(middleWidth); setEdge(side, edge, style); } void KoTableCellStyle::setEdge(KoBorder::BorderSide side, const KoBorder::BorderData &edge, KoBorder::BorderStyle style) { KoBorder borders = this->borders(); KoBorder::BorderData edgeCopy(edge); edgeCopy.style = style; // Just for safety. borders.setBorderData(side, edgeCopy); setBorders(borders); } void KoTableCellStyle::setEdgeDoubleBorderValues(KoBorder::BorderSide side, qreal innerWidth, qreal space) { KoBorder::BorderData edge = getEdge(side); qreal totalWidth = edge.outerPen.widthF() + edge.spacing + edge.innerPen.widthF(); if (edge.innerPen.widthF() > 0.0) { edge.outerPen.setWidthF(totalWidth - innerWidth - space); edge.spacing = space; edge.innerPen.setWidthF(innerWidth); setEdge(side, edge, getBorderStyle(side)); } } bool KoTableCellStyle::hasBorders() const { return borders().hasBorder(); } qreal KoTableCellStyle::leftBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::LeftBorder); return edge.spacing + edge.innerPen.widthF() + edge.outerPen.widthF(); } qreal KoTableCellStyle::rightBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::RightBorder); return edge.spacing + edge.innerPen.widthF() + edge.outerPen.widthF(); } qreal KoTableCellStyle::topBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::TopBorder); return edge.spacing + edge.innerPen.widthF() + edge.outerPen.widthF(); } qreal KoTableCellStyle::bottomBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::BottomBorder); return edge.spacing + edge.innerPen.widthF() + edge.outerPen.widthF(); } qreal KoTableCellStyle::leftInnerBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::LeftBorder); return edge.innerPen.widthF(); } qreal KoTableCellStyle::rightInnerBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::RightBorder); return edge.innerPen.widthF(); } qreal KoTableCellStyle::topInnerBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::TopBorder); return edge.innerPen.widthF(); } qreal KoTableCellStyle::bottomInnerBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::BottomBorder); return edge.innerPen.widthF(); } qreal KoTableCellStyle::leftOuterBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::LeftBorder); return edge.outerPen.widthF(); } qreal KoTableCellStyle::rightOuterBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::RightBorder); return edge.outerPen.widthF(); } qreal KoTableCellStyle::topOuterBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::TopBorder); return edge.outerPen.widthF(); } qreal KoTableCellStyle::bottomOuterBorderWidth() const { const KoBorder::BorderData &edge = getEdge(KoBorder::BottomBorder); return edge.outerPen.widthF(); } KoBorder::BorderData KoTableCellStyle::getEdge(KoBorder::BorderSide side) const { KoBorder border = this->borders(); return border.borderData(side); } KoBorder::BorderStyle KoTableCellStyle::getBorderStyle(KoBorder::BorderSide side) const { KoBorder::BorderData edge = getEdge(side); return edge.style; } diff --git a/plugins/impex/kra/kra_converter.cpp b/plugins/impex/kra/kra_converter.cpp index 87a7b6d101..79b5976bfa 100644 --- a/plugins/impex/kra/kra_converter.cpp +++ b/plugins/impex/kra/kra_converter.cpp @@ -1,361 +1,353 @@ /* * Copyright (C) 2016 Boudewijn Rempt * * 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 "kra_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char CURRENT_DTD_VERSION[] = "2.0"; KraConverter::KraConverter(KisDocument *doc) : m_doc(doc) , m_image(doc->savingImage()) { } KraConverter::~KraConverter() { delete m_store; delete m_kraSaver; delete m_kraLoader; } KisImageBuilder_Result KraConverter::buildImage(QIODevice *io) { m_store = KoStore::createStore(io, KoStore::Read, "", KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Not a valid Krita file")); return KisImageBuilder_RESULT_FAILURE; } bool success; { if (m_store->hasFile("root") || m_store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml) -#ifdef KOXML_USE_QDOM KoXmlDocument doc; -#else - KoXmlDocument doc = KoXmlDocument(true); -#endif bool ok = oldLoadAndParse(m_store, "root", doc); if (ok) ok = loadXML(doc, m_store); if (!ok) { return KisImageBuilder_RESULT_FAILURE; } } else { errUI << "ERROR: No maindoc.xml" << endl; m_doc->setErrorMessage(i18n("Invalid document: no file 'maindoc.xml'.")); return KisImageBuilder_RESULT_FAILURE; } if (m_store->hasFile("documentinfo.xml")) { -#ifdef KOXML_USE_QDOM KoXmlDocument doc; -#else - KoXmlDocument doc = KoXmlDocument(true); -#endif if (oldLoadAndParse(m_store, "documentinfo.xml", doc)) { m_doc->documentInfo()->load(doc); } } success = completeLoading(m_store); } return success ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE; } KisImageSP KraConverter::image() { return m_image; } vKisNodeSP KraConverter::activeNodes() { return m_activeNodes; } QList KraConverter::assistants() { return m_assistants; } KisImageBuilder_Result KraConverter::buildFile(QIODevice *io) { m_store = KoStore::createStore(io, KoStore::Write, m_doc->nativeFormatMimeType(), KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Could not create the file for saving")); return KisImageBuilder_RESULT_FAILURE; } bool result = false; m_kraSaver = new KisKraSaver(m_doc); result = saveRootDocuments(m_store); if (!result) { return KisImageBuilder_RESULT_FAILURE; } result = m_kraSaver->saveKeyframes(m_store, m_doc->url().toLocalFile(), true); if (!result) { qWarning() << "saving key frames failed"; } result = m_kraSaver->saveBinaryData(m_store, m_image, m_doc->url().toLocalFile(), true, m_doc->isAutosaving()); if (!result) { qWarning() << "saving binary data failed"; } if (!m_store->finalize()) { return KisImageBuilder_RESULT_FAILURE; } if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); return KisImageBuilder_RESULT_FAILURE; } return KisImageBuilder_RESULT_OK; } bool KraConverter::saveRootDocuments(KoStore *store) { dbgFile << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { dbgUI << "saveToStream failed"; return false; } } else { m_doc->setErrorMessage(i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml"))); return false; } bool success = false; if (store->open("documentinfo.xml")) { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = m_doc->documentInfo()->save(doc); KoStoreDevice dev(store); QByteArray s = doc.toByteArray(); // this is already Utf8! success = dev.write(s.data(), s.size()); store->close(); } if (store->open("preview.png")) { // ### TODO: missing error checking (The partition could be full!) savePreview(store); (void)store->close(); } dbgUI << "Saving done of url:" << m_doc->url().toLocalFile(); // Success return success; } bool KraConverter::saveToStream(QIODevice *dev) { QDomDocument doc = createDomDocument(); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open(QIODevice::WriteOnly); int nwritten = dev->write(s.data(), s.size()); if (nwritten != (int)s.size()) { warnUI << "wrote " << nwritten << "- expected" << s.size(); } return nwritten == (int)s.size(); } QDomDocument KraConverter::createDomDocument() { QDomDocument doc = m_doc->createDomDocument("DOC", CURRENT_DTD_VERSION); QDomElement root = doc.documentElement(); root.setAttribute("editor", "Krita"); root.setAttribute("syntaxVersion", "2"); root.setAttribute("kritaVersion", KritaVersionWrapper::versionString(false)); root.appendChild(m_kraSaver->saveXML(doc, m_image)); if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); } return doc; } bool KraConverter::savePreview(KoStore *store) { QPixmap pix = m_doc->generatePreview(QSize(256, 256)); QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly)); if (preview.size() == QSize(0,0)) { QSize newSize = m_doc->savingImage()->bounds().size(); newSize.scale(QSize(256, 256), Qt::KeepAspectRatio); preview = QImage(newSize, QImage::Format_ARGB32); preview.fill(QColor(0, 0, 0, 0)); } KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) { return false; } bool ret = preview.save(&io, "PNG"); io.close(); return ret; } bool KraConverter::oldLoadAndParse(KoStore *store, const QString &filename, KoXmlDocument &xmldoc) { //dbgUI <<"Trying to open" << filename; if (!store->open(filename)) { warnUI << "Entry " << filename << " not found!"; m_doc->setErrorMessage(i18n("Could not find %1", filename)); return false; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = xmldoc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn); store->close(); if (!ok) { errUI << "Parsing error in " << filename << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; m_doc->setErrorMessage(i18n("Parsing error in %1 at line %2, column %3\nError message: %4" , filename , errorLine, errorColumn , QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0, QCoreApplication::UnicodeUTF8))); return false; } dbgUI << "File" << filename << " loaded and parsed"; return true; } bool KraConverter::loadXML(const KoXmlDocument &doc, KoStore *store) { Q_UNUSED(store); KoXmlElement root; KoXmlNode node; if (doc.doctype().name() != "DOC") { m_doc->setErrorMessage(i18n("The format is not supported or the file is corrupted")); return false; } root = doc.documentElement(); int syntaxVersion = root.attribute("syntaxVersion", "3").toInt(); if (syntaxVersion > 2) { m_doc->setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion)); return false; } if (!root.hasChildNodes()) { m_doc->setErrorMessage(i18n("The file has no layers.")); return false; } m_kraLoader = new KisKraLoader(m_doc, syntaxVersion); // Legacy from the multi-image .kra file period. for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { KoXmlElement elem = node.toElement(); if (!(m_image = m_kraLoader->loadXML(elem))) { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); } return false; } return true; } else { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("The file does not contain an image.")); } return false; } } } return false; } bool KraConverter::completeLoading(KoStore* store) { if (!m_image) { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); } return false; } m_image->blockUpdates(); m_kraLoader->loadBinaryData(store, m_image, m_doc->localFilePath(), true); m_image->unblockUpdates(); bool retval = true; if (!m_kraLoader->warningMessages().isEmpty()) { m_doc->setWarningMessage(m_kraLoader->warningMessages().join("\n")); retval = true; } if (retval) { m_activeNodes = m_kraLoader->selectedNodes(); m_assistants = m_kraLoader->assistants(); } return retval; } void KraConverter::cancel() { m_stop = true; }