diff --git a/Sankore_3.1.pro b/Sankore_3.1.pro index 4fe242a6..7f87beca 100644 --- a/Sankore_3.1.pro +++ b/Sankore_3.1.pro @@ -35,6 +35,7 @@ QT += script QT += xmlpatterns INCLUDEPATH += src +INCLUDEPATH += plugins/cffadaptor/src include($$THIRD_PARTY_PATH/libs.pri) include(src/adaptors/adaptors.pri) @@ -109,6 +110,7 @@ RCC_DIR = $$BUILD_DIR/rcc UI_DIR = $$BUILD_DIR/ui win32 { + LIBS += "-Lplugins/cffadaptor/lib/win32" "-lCFF_Adaptor" RC_FILE = resources/win/sankore.rc CONFIG += qaxcontainer exists(console):CONFIG += console @@ -129,6 +131,7 @@ win32 { } macx { + LIBS += "-Lplugins/cffadaptor/lib/mac" "-lCFF_Adaptor" LIBS += -framework Foundation LIBS += -lcrypto LIBS += -framework AppKit @@ -326,6 +329,7 @@ macx { } linux-g++ { + LIBS += "-Lplugins/cffadaptor/lib/linux" "-lCFF_Adaptor" LIBS += -lcrypto QMAKE_CFLAGS += -fopenmp QMAKE_CXXFLAGS += -fopenmp @@ -341,6 +345,7 @@ linux-g++ { } linux-g++-32 { + LIBS += "-Lplugins/cffadaptor/lib/linux" "-lCFF_Adaptor" LIBS += -lcrypto QMAKE_CFLAGS += -fopenmp QMAKE_CXXFLAGS += -fopenmp @@ -356,6 +361,7 @@ linux-g++-32 { } linux-g++-64 { + LIBS += "-Lplugins/cffadaptor/lib/linux" "-lCFF_Adaptor" LIBS += -lcrypto QMAKE_CFLAGS += -fopenmp QMAKE_CXXFLAGS += -fopenmp @@ -371,6 +377,7 @@ linux-g++-64 { } RESOURCES += resources/sankore.qrc +RESOURCES += plugins/cffadaptor/resources/resources.qrc # When adding a translation here, also add it in the macx part TRANSLATIONS = resources/i18n/sankore_en.ts \ diff --git a/plugins/cffadaptor/UBCFFAdaptor.pro b/plugins/cffadaptor/UBCFFAdaptor.pro new file mode 100644 index 00000000..78f1bf71 --- /dev/null +++ b/plugins/cffadaptor/UBCFFAdaptor.pro @@ -0,0 +1,49 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2012-02-14T13:30:14 +# +#------------------------------------------------- + +win32: SUB_DIR = win32 +macx: SUB_DIR = macx +linux-g++: SUB_DIR = linux +linux-g++-32: SUB_DIR = linux +linux-g++-64: SUB_DIR = linux + +QUAZIP_DIR = "$$PWD/../../../Sankore-ThirdParty/quazip" +ZLIB_DIR = "$$PWD/../../../Sankore-ThirdParty/zlib" + +INCLUDEPATH += src \ + "$$QUAZIP_DIR/quazip-0.3" \ + "$$ZLIB_DIR/1.2.3/include" + +LIBS += "-L$$QUAZIP_DIR/lib/$$SUB_DIR" "-lquazip" + +QT += xml xmlpatterns core +QT += gui +QT += svg + +TARGET = CFF_Adaptor +TEMPLATE = lib +win32{ + CONFIG += dll +} + +DEFINES += UBCFFADAPTOR_LIBRARY +DEFINES += NO_THIRD_PARTY_WARNINGS + +SOURCES += \ + src/UBCFFAdaptor.cpp + +HEADERS +=\ + $$PWD/../../src/globals/UBGlobals.h \ + src/UBCFFAdaptor.h \ + src/UBCFFAdaptor_global.h \ + src/UBCFFConstants.h + +OBJECTS_DIR = $$PWD/objects +MOC_DIR = $$PWD/moc +DESTDIR = $$PWD/lib/$$SUB_DIR + +RESOURCES += \ + resources/resources.qrc diff --git a/plugins/cffadaptor/resources/images/soundOn.svg b/plugins/cffadaptor/resources/images/soundOn.svg new file mode 100644 index 00000000..79d27706 --- /dev/null +++ b/plugins/cffadaptor/resources/images/soundOn.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/plugins/cffadaptor/resources/resources.qrc b/plugins/cffadaptor/resources/resources.qrc new file mode 100644 index 00000000..0a4ec471 --- /dev/null +++ b/plugins/cffadaptor/resources/resources.qrc @@ -0,0 +1,5 @@ + + + images/soundOn.svg + + diff --git a/plugins/cffadaptor/src/UBCFFAdaptor.cpp b/plugins/cffadaptor/src/UBCFFAdaptor.cpp new file mode 100644 index 00000000..6de92d92 --- /dev/null +++ b/plugins/cffadaptor/src/UBCFFAdaptor.cpp @@ -0,0 +1,1977 @@ +#include "UBCFFAdaptor.h" + +#include +#include +#include +#include +#include +#include + +#include "UBGlobals.h" +#include "UBCFFConstants.h" + +THIRD_PARTY_WARNINGS_DISABLE +#include "quazip.h" +#include "quazipfile.h" +#include "quazipfileinfo.h" +THIRD_PARTY_WARNINGS_ENABLE + +UBCFFAdaptor::UBCFFAdaptor() +{} + +bool UBCFFAdaptor::convertUBZToIWB(const QString &from, const QString &to) +{ + qDebug() << "starting converion from" << from << "to" << to; + + QString source = QString(); + if (QFileInfo(from).isDir() && QFile::exists(from)) { + qDebug() << "File specified is dir, continuing convertion"; + source = from; + } else { + source = uncompressZip(from); + if (!source.isNull()) qDebug() << "File specified is zip file. Uncompressed to tmp dir, continuing convertion"; + } + if (source.isNull()) { + qDebug() << "File specified is not a dir or a zip file, stopping covretion"; + return false; + } + + QString tmpDestination = createNewTmpDir(); + if (tmpDestination.isNull()) { + qDebug() << "can't create temp destination folder. Stopping parsing..."; + return false; + } + + UBToCFFConverter tmpConvertrer(source, tmpDestination); + if (!tmpConvertrer) { + qDebug() << "The convertrer class is invalid, stopping conversion. Error message" << tmpConvertrer.lastErrStr(); + return false; + } + if (!tmpConvertrer.parse()) { + return false; + } + + if (!compressZip(tmpDestination, to)) + qDebug() << "error in compression"; + + //Cleanning tmp souces in filesystem + if (!QFileInfo(from).isDir()) + if (!freeDir(source)) + qDebug() << "can't delete tmp directory" << QDir(source).absolutePath() << "try to delete them manually"; + + if (!freeDir(tmpDestination)) + qDebug() << "can't delete tmp directory" << QDir(tmpDestination).absolutePath() << "try to delete them manually"; + + return true; +} + +QString UBCFFAdaptor::uncompressZip(const QString &zipFile) +{ + QuaZip zip(zipFile); + + if(!zip.open(QuaZip::mdUnzip)) { + qWarning() << "Import failed. Cause zip.open(): " << zip.getZipError(); + return QString(); + } + + zip.setFileNameCodec("UTF-8"); + QuaZipFileInfo info; + QuaZipFile file(&zip); + + //create unique cff document root fodler + QString documentRootFolder = createNewTmpDir(); + + if (documentRootFolder.isNull()) { + qDebug() << "can't create tmp directory for zip file" << zipFile; + return QString(); + } + + QDir rootDir(documentRootFolder); + QFile out; + char c; + bool allOk = true; + for(bool more = zip.goToFirstFile(); more; more=zip.goToNextFile()) { + if(!zip.getCurrentFileInfo(&info)) { + qWarning() << "Import failed. Cause: getCurrentFileInfo(): " << zip.getZipError(); + allOk = false; + break; + } + if(!file.open(QIODevice::ReadOnly)) { + allOk = false; + break; + } + if(file.getZipError()!= UNZ_OK) { + qWarning() << "Import failed. Cause: file.getFileName(): " << zip.getZipError(); + allOk = false; + break; + } + + QString newFileName = documentRootFolder + "/" + file.getActualFileName(); + + QFileInfo newFileInfo(newFileName); + rootDir.mkpath(newFileInfo.absolutePath()); + + out.setFileName(newFileName); + out.open(QIODevice::WriteOnly); + + while(file.getChar(&c)) + out.putChar(c); + + out.close(); + + if(file.getZipError()!=UNZ_OK) { + qWarning() << "Import failed. Cause: " << zip.getZipError(); + allOk = false; + break; + } + if(!file.atEnd()) { + qWarning() << "Import failed. Cause: read all but not EOF"; + allOk = false; + break; + } + file.close(); + + if(file.getZipError()!=UNZ_OK) { + qWarning() << "Import failed. Cause: file.close(): " << file.getZipError(); + allOk = false; + break; + } + } + + if (!allOk) { + out.close(); + file.close(); + zip.close(); + return QString(); + } + + if(zip.getZipError()!=UNZ_OK) { + qWarning() << "Import failed. Cause: zip.close(): " << zip.getZipError(); + return QString(); + } + + return documentRootFolder; +} + +bool UBCFFAdaptor::compressZip(const QString &source, const QString &destination) +{ + QDir toDir = QFileInfo(destination).dir(); + if (!toDir.exists()) + if (!QDir().mkpath(toDir.absolutePath())) { + qDebug() << "can't create destination folder to uncompress file"; + return false; + } + + QuaZip zip(destination); + zip.setFileNameCodec("UTF-8"); + if(!zip.open(QuaZip::mdCreate)) { + qDebug("Export failed. Cause: zip.open(): %d", zip.getZipError()); + return false; + } + + QuaZipFile outZip(&zip); + + QFileInfo sourceInfo(source); + if (sourceInfo.isDir()) { + if (!compressDir(QFileInfo(source).absoluteFilePath(), "", &outZip)) + return false; + } else if (sourceInfo.isFile()) { + if (!compressFile(QFileInfo(source).absoluteFilePath(), "", &outZip)) + return false; + } + + return true; +} + +bool UBCFFAdaptor::compressDir(const QString &dirName, const QString &parentDir, QuaZipFile *outZip) +{ + QFileInfoList dirFiles = QDir(dirName).entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + QListIterator iter(dirFiles); + while (iter.hasNext()) { + QFileInfo curFile = iter.next(); + + if (curFile.isDir()) { + if (!compressDir(curFile.absoluteFilePath(), parentDir + curFile.fileName() + "/", outZip)) { + qDebug() << "error at compressing dir" << curFile.absoluteFilePath(); + return false; + } + } else if (curFile.isFile()) { + if (!compressFile(curFile.absoluteFilePath(), parentDir, outZip)) { + return false; + } + } + } + + return true; +} + +bool UBCFFAdaptor::compressFile(const QString &fileName, const QString &parentDir, QuaZipFile *outZip) +{ + QFile sourceFile(fileName); + + if(!sourceFile.open(QIODevice::ReadOnly)) { + qDebug() << "Compression of file" << sourceFile.fileName() << " failed. Cause: inFile.open(): " << sourceFile.errorString(); + return false; + } + + if(!outZip->open(QIODevice::WriteOnly, QuaZipNewInfo(parentDir + QFileInfo(fileName).fileName(), sourceFile.fileName()))) { + qDebug() << "Compression of file" << sourceFile.fileName() << " failed. Cause: outFile.open(): " << outZip->getZipError(); + sourceFile.close(); + return false; + } + + outZip->write(sourceFile.readAll()); + if(outZip->getZipError() != UNZ_OK) { + qDebug() << "Compression of file" << sourceFile.fileName() << " failed. Cause: outFile.write(): " << outZip->getZipError(); + + sourceFile.close(); + outZip->close(); + return false; + } + + if(outZip->getZipError() != UNZ_OK) + { + qWarning() << "Compression of file" << sourceFile.fileName() << " failed. Cause: outFile.close(): " << outZip->getZipError(); + + sourceFile.close(); + outZip->close(); + return false; + } + + outZip->close(); + sourceFile.close(); + + return true; +} + +QString UBCFFAdaptor::createNewTmpDir() +{ + int tmpNumber = 0; + QDir systemTmp = QDir::temp(); + + while (true) { + QString dirName = QString("CFF_adaptor_filedata_store%1.%2") + .arg(QDateTime::currentDateTime().toString("dd_MM_yyyy_HH-mm")) + .arg(tmpNumber++); + if (!systemTmp.exists(dirName)) { + if (systemTmp.mkdir(dirName)) { + QString result = systemTmp.absolutePath() + "/" + dirName; + tmpDirs.append(result); + return result; + } else { + qDebug() << "Can't create temporary dir maybe due to permissions"; + return QString(); + } + } else if (tmpNumber == 10) { + qWarning() << "Import failed. Failed to create temporary file "; + return QString(); + } + tmpNumber++; + } + + return QString(); +} +bool UBCFFAdaptor::deleteDir(const QString& pDirPath) const +{ + if (pDirPath == "" || pDirPath == "." || pDirPath == "..") + return false; + + QDir dir(pDirPath); + + if (dir.exists()) + { + foreach(QFileInfo dirContent, dir.entryInfoList(QDir::Files | QDir::Dirs + | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, QDir::Name)) + { + if (dirContent.isDir()) + { + deleteDir(dirContent.absoluteFilePath()); + } + else + { + if (!dirContent.dir().remove(dirContent.fileName())) + { + return false; + } + } + } + } + + return dir.rmdir(pDirPath); +} +bool UBCFFAdaptor::freeDir(const QString &dir) +{ + bool result = true; + if (!deleteDir(dir)) + result = false; + + tmpDirs.removeAll(QDir(dir).absolutePath()); + + return result; +} +void UBCFFAdaptor::freeTmpDirs() +{ + foreach (QString dir, tmpDirs) + freeDir(dir); +} + +UBCFFAdaptor::~UBCFFAdaptor() +{ + freeTmpDirs(); +} + +UBCFFAdaptor::UBToCFFConverter::UBToCFFConverter(const QString &source, const QString &destination) +{ + sourcePath = source; + destinationPath = destination; + + errorStr = noErrorMsg; + mDataModel = new QDomDocument; + mDocumentToWrite = new QDomDocument; + mDocumentToWrite->setContent(QString("")); + + mIWBContentWriter = new QXmlStreamWriter; + mIWBContentWriter->setAutoFormatting(true); + + iwbSVGItemsAttributes.insert(tIWBImage, iwbSVGImageAttributes); + iwbSVGItemsAttributes.insert(tIWBVideo, iwbSVGVideoAttributes); + iwbSVGItemsAttributes.insert(tIWBText, iwbSVGTextAttributes); + iwbSVGItemsAttributes.insert(tIWBTextArea, iwbSVGTextAreaAttributes); + iwbSVGItemsAttributes.insert(tIWBPolyLine, iwbSVGPolyLineAttributes); + iwbSVGItemsAttributes.insert(tIWBPolygon, iwbSVGPolygonAttributes); + iwbSVGItemsAttributes.insert(tIWBRect, iwbSVGRectAttributes); + iwbSVGItemsAttributes.insert(tIWBLine, iwbSVGLineAttributes); + iwbSVGItemsAttributes.insert(tIWBTspan, iwbSVGTspanAttributes); +} + +bool UBCFFAdaptor::UBToCFFConverter::parse() +{ + if(!isValid()) { + qDebug() << "document metadata is not valid. Can't parse"; + return false; + } + + qDebug() << "begin parsing ubz"; + + QFile outFile(contentIWBFileName()); + if (!outFile.open(QIODevice::WriteOnly| QIODevice::Text)) { + qDebug() << "can't open output file for writing"; + errorStr = "createXMLOutputPatternError"; + return false; + } + + mIWBContentWriter->setDevice(&outFile); + + mIWBContentWriter->writeStartDocument(); + mIWBContentWriter->writeStartElement(tIWBRoot); + + fillNamespaces(); + + mIWBContentWriter->writeAttribute(aIWBVersion, avIWBVersionNo); + + if (!parseMetadata()) { + if (errorStr == noErrorMsg) + errorStr = "MetadataParsingError"; + + outFile.close(); + return false; + } + + if (!parseContent()) { + if (errorStr == noErrorMsg) + errorStr = "ContentParsingError"; + outFile.close(); + return false; + } + + mIWBContentWriter->writeEndElement(); + mIWBContentWriter->writeEndDocument(); + + outFile.close(); + + qDebug() << "finished with success"; + + return true; +} +bool UBCFFAdaptor::UBToCFFConverter::parseMetadata() +{ + int errorLine, errorColumn; + QFile metaDataFile(sourcePath + "/" + fMetadata); + + if (!metaDataFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + errorStr = "can't open" + QFileInfo(sourcePath + "/" + fMetadata).absoluteFilePath(); + qDebug() << errorStr; + return false; + + } else if (!mDataModel->setContent(metaDataFile.readAll(), true, &errorStr, &errorLine, &errorColumn)) { + qWarning() << "Error:Parseerroratline" << errorLine << "," + << "column" << errorColumn << ":" << errorStr; + return false; + } + + QDomElement nextInElement = mDataModel->documentElement(); + + nextInElement = nextInElement.firstChildElement(tDescription); + if (!nextInElement.isNull()) { + + mIWBContentWriter->writeStartElement(iwbNS, tIWBMeta); + mIWBContentWriter->writeAttribute(aIWBName, aCreator); + mIWBContentWriter->writeAttribute(aIWBContent, avCreator); + mIWBContentWriter->writeEndElement(); + + mIWBContentWriter->writeStartElement(iwbNS, tIWBMeta); + mIWBContentWriter->writeAttribute(aIWBName, aOwner); + mIWBContentWriter->writeAttribute(aIWBContent, avOwner); + mIWBContentWriter->writeEndElement(); + + mIWBContentWriter->writeStartElement(iwbNS, tIWBMeta); + mIWBContentWriter->writeAttribute(aIWBName, aDescription); + mIWBContentWriter->writeAttribute(aIWBContent, avDescription); + mIWBContentWriter->writeEndElement(); + + mIWBContentWriter->writeStartElement(iwbNS, tIWBMeta); + mIWBContentWriter->writeAttribute(aIWBName, aAbout); + mIWBContentWriter->writeAttribute(aIWBContent, nextInElement.attribute(aAbout)); + mIWBContentWriter->writeEndElement(); + + nextInElement = nextInElement.firstChildElement(); + while (!nextInElement.isNull()) { + + QString textContent = nextInElement.text(); + if (!textContent.trimmed().isEmpty()) { + if (nextInElement.tagName() == tUBZSize) { //taking main viewbox rect since for CFF specificaton we have static viewbox + QSize tmpSize = getSVGDimentions(nextInElement.text()); + if (!tmpSize.isNull()) { + mSVGSize = tmpSize; + mViewbox.setRect(0,0, tmpSize.width(), tmpSize.height()); + } else { + qDebug() << "can't interpret svg section size"; + errorStr = "InterpretSvgSizeError"; + return false; + } + } else { + mIWBContentWriter->writeStartElement(iwbNS, tIWBMeta); + mIWBContentWriter->writeAttribute(aIWBName, nextInElement.tagName()); + mIWBContentWriter->writeAttribute(aIWBContent, textContent); + mIWBContentWriter->writeEndElement(); + } + + } + nextInElement = nextInElement.nextSiblingElement(); + } + } + + metaDataFile.close(); + return true; +} +bool UBCFFAdaptor::UBToCFFConverter::parseContent() { + + QDir sourceDir(sourcePath); + QStringList fileFilters; + fileFilters << QString(pageAlias + "???." + pageFileExtentionUBZ); + QStringList pageList = sourceDir.entryList(fileFilters, QDir::Files, QDir::Name | QDir::IgnoreCase); + + QDomElement svgDocumentSection = mDataModel->createElementNS(svgIWBNS, ":"+tSvg); + + if (!pageList.count()) { + qDebug() << "can't find any content file"; + errorStr = "ErrorContentFile"; + return false; + } else + { + QDomElement pageset = parsePageset(pageList); + if (pageset.isNull()) + return false; + else + svgDocumentSection.appendChild(pageset); + } + + + svgDocumentSection.setAttribute(aIWBViewBox, rectToIWBAttr(mViewbox)); + svgDocumentSection.setAttribute(aWidth, QString("%1").arg(mViewbox.width())); + svgDocumentSection.setAttribute(aHeight, QString("%1").arg(mViewbox.height())); + + + writeQDomElementToXML(svgDocumentSection); + + + if (!writeExtendedIwbSection()) { + if (errorStr == noErrorMsg) + errorStr = "writeExtendedIwbSectionError"; + return false; + } + + return true; +} + +QDomElement UBCFFAdaptor::UBToCFFConverter::parsePage(const QString &pageFileName) +{ + qDebug() << "begin parsing page" + pageFileName; + mSvgElements.clear(); //clean Svg elements map before parsing new page + + int errorLine, errorColumn; + + QFile pageFile(sourcePath + "/" + pageFileName); + if (!pageFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "can't open file" << pageFileName << "for reading"; + return QDomElement(); + } else if (!mDataModel->setContent(pageFile.readAll(), true, &errorStr, &errorLine, &errorColumn)) { + qWarning() << "Error:Parseerroratline" << errorLine << "," + << "column" << errorColumn << ":" << errorStr; + pageFile.close(); + return QDomElement(); + } + + QDomElement page; + QDomElement group; + + QDomElement nextTopElement = mDataModel->firstChildElement(); + while (!nextTopElement.isNull()) { + QString tagname = nextTopElement.tagName(); + if (tagname == tSvg) { + page = parseSvgPageSection(nextTopElement); + if (page.isNull()) { + qDebug() << "The page is empty."; + pageFile.close(); + return QDomElement(); + } + } else if (tagname == tUBZGroup) { + group = parseGroupPageSection(nextTopElement); + if (group.isNull()) { + qDebug() << "Page doesn't contains any groups."; + pageFile.close(); + return QDomElement(); + } + } + + nextTopElement = nextTopElement.nextSiblingElement(); + } + + pageFile.close(); + + return page.hasChildNodes() ? page : QDomElement(); +} + +QDomElement UBCFFAdaptor::UBToCFFConverter::parsePageset(const QStringList &pageFileNames) +{ + QMultiMap pageList; + int iPageNo = 1; + + QStringListIterator curPage(pageFileNames); + + while (curPage.hasNext()) { + + QString curPageFile = curPage.next(); + QDomElement iterElement = parsePage(curPageFile); + if (!iterElement.isNull()) + { + iterElement.setAttribute(tId, iPageNo); + addSVGElementToResultModel(iterElement, pageList, iPageNo); + iPageNo++; + } + else + return QDomElement(); + } + + + if (!pageList.count()) + return QDomElement(); + + + QDomElement svgPagesetElement = mDocumentToWrite->createElementNS(svgIWBNS,":"+ tIWBPageSet); + + QMapIterator nextSVGElement(pageList); + nextSVGElement.toFront(); + while (nextSVGElement.hasNext()) + svgPagesetElement.appendChild(nextSVGElement.next().value()); + + return svgPagesetElement.hasChildNodes() ? svgPagesetElement : QDomElement(); +} +QDomElement UBCFFAdaptor::UBToCFFConverter::parseSvgPageSection(const QDomElement &element) +{ + //we don't know about page number, so return QDomElement. + + //Parsing top level tag attributes + + //getting current page viewbox to be able to convert coordinates to global viewbox parameter + if (element.hasAttribute(aUBZViewBox)) { + setViewBox(getViewboxRect(element.attribute(aUBZViewBox))); + } + + QMultiMap svgElements; + + QDomElement svgElementPart = mDocumentToWrite->createElementNS(svgIWBNS,":"+ tIWBPage); + + if (element.hasAttribute(aDarkBackground)) { + createBackground(element, svgElements); + } + + //Parsing svg children attributes + // Elements can know about its layer, so it must add result QDomElements to ordrered list. + QDomElement nextElement = element.firstChildElement(); + while (!nextElement.isNull()) { + QString tagName = nextElement.tagName(); + if (tagName == tUBZG) parseSVGGGroup(nextElement, svgElements); + else if (tagName == tUBZImage) parseUBZImage(nextElement, svgElements); + else if (tagName == tUBZVideo) parseUBZVideo(nextElement, svgElements); + else if (tagName == tUBZAudio) parseUBZAudio(nextElement, svgElements); + else if (tagName == tUBZForeignObject) parseForeignObject(nextElement, svgElements); + else if (tagName == tUBZLine) parseUBZLine(nextElement, svgElements); + else if (tagName == tUBZPolygon) parseUBZPolygon(nextElement, svgElements); + else if (tagName == tUBZPolyline) parseUBZPolyline(nextElement, svgElements); + + nextElement = nextElement.nextSiblingElement(); + } + + if (0 == svgElements.count()) + return QDomElement(); + + // to do: + // there we must to sort elements (take elements from list and assign parent ordered like in parseSVGGGroup) + // we returns just element because we don't care about layer. + QMapIterator nextSVGElement(svgElements); + nextSVGElement.toFront(); + while (nextSVGElement.hasNext()) + svgElementPart.appendChild(nextSVGElement.next().value()); + + return svgElementPart.hasChildNodes() ? svgElementPart : QDomElement(); +} + +void UBCFFAdaptor::UBToCFFConverter::writeQDomElementToXML(const QDomNode &node) +{ + if (!node.isNull()) + if (node.isText()) + { + mIWBContentWriter->writeCharacters(node.nodeValue()); + } + else + { + mIWBContentWriter->writeStartElement(node.namespaceURI(), node.toElement().tagName()); + + for (int i = 0; i < node.toElement().attributes().count(); i++) + { + QDomAttr attr = node.toElement().attributes().item(i).toAttr(); + mIWBContentWriter->writeAttribute(attr.name(), attr.value()); + } + QDomNode child = node.firstChild(); + while(!child.isNull()) + { + writeQDomElementToXML(child); + child = child.nextSibling(); + } + + mIWBContentWriter->writeEndElement(); + } +} + +bool UBCFFAdaptor::UBToCFFConverter::writeExtendedIwbSection() +{ + if (!mExtendedElements.count()) { + qDebug() << "extended iwb content list is empty"; + errorStr = "EmptyExtendedIwbSectionContentError"; + return false; + } + QListIterator nextExtendedIwbElement(mExtendedElements); + while (nextExtendedIwbElement.hasNext()) { + writeQDomElementToXML(nextExtendedIwbElement.next()); + //TODO write iwb extended element to mIWBContentWriter + } + + return true; +} + +// extended element options +// editable, background, locked are supported for now + +QDomElement UBCFFAdaptor::UBToCFFConverter::parseGroupPageSection(const QDomElement &element) +{ +// First sankore side implementation needed. TODO in Sankore 1.5 + Q_UNUSED(element) + qDebug() << "parsing ubz group section"; + return QDomElement(); +} + +QString UBCFFAdaptor::UBToCFFConverter::getDstContentFolderName(const QString &elementType) +{ + QString sRet; + QString sDstContentFolderName; + + // widgets must be saved as .png images. + if ((tIWBImage == elementType) || (tUBZForeignObject == elementType)) + sDstContentFolderName = cfImages; + else + if (tIWBVideo == elementType) + sDstContentFolderName = cfVideos; + else + if (tIWBAudio == elementType) + sDstContentFolderName = cfAudios; + + sRet = sDstContentFolderName; + + return sRet; +} + +QString UBCFFAdaptor::UBToCFFConverter::getSrcContentFolderName(QString href) +{ + QString sRet; + + QStringList ls = href.split("/"); + for (int i = 0; i < ls.count()-1; i++) + { + QString sPart = ls.at(i); + if (ubzContentFolders.contains(sPart)) + { + sRet = sPart; + } + } + +// if (0 < ls.count()) +// sRet = ls.at(ls.count()-1); +// +// sRet = href.remove(sRet); +// +// if (sRet.endsWith("/")) +// sRet.remove("/"); + + return sRet; +} + +QString UBCFFAdaptor::UBToCFFConverter::getFileNameFromPath(const QString sPath) +{ + QString sRet; + QStringList sl = sPath.split("/",QString::SkipEmptyParts); + + if (0 < sl.count()) + { + QString name = sl.at(sl.count()-1); + QString extention = getExtentionFromFileName(name); + + if (feWgt == extention) + { + name.remove("{"); + name.remove("}"); + } + + name.remove(name.length()-extention.length(), extention.length()); + name += convertExtention(extention); + + sRet = name; + } + return sRet; +} + +QString UBCFFAdaptor::UBToCFFConverter::getExtentionFromFileName(const QString &filename) +{ + QStringList sl = filename.split("/",QString::SkipEmptyParts); + + if (0 < sl.count()) + { + QString name = sl.at(sl.count()-1); + QStringList tl = name.split("."); + return tl.at(tl.count()-1); + } + return QString(); +} + +QString UBCFFAdaptor::UBToCFFConverter::convertExtention(const QString &ext) +{ + QString sRet; + + if (feSvg == ext) + sRet = fePng; + else + if (feWgt == ext) + sRet = fePng; + else + sRet = ext; + + return sRet; +} + +QString UBCFFAdaptor::UBToCFFConverter::getElementTypeFromUBZ(const QDomElement &element) +{ + QString sRet; + if (tUBZForeignObject == element.tagName()) + { + QString sPath; + if (element.hasAttribute(aUBZType)) + { + if (avUBZText == element.attribute(aUBZType)) + sRet = tIWBTextArea; + else + sRet = element.attribute(aUBZType); + } + else + { + if (element.hasAttribute(aSrc)) + sPath = element.attribute(aSrc); + else + if (element.hasAttribute(aUBZHref)) + sPath = element.attribute(aUBZHref); + + QStringList tsl = sPath.split(".", QString::SkipEmptyParts); + if (0 < tsl.count()) + { + QString elementType = tsl.at(tsl.count()-1); + if (iwbElementImage.contains(elementType)) + sRet = tIWBImage; + else + if (iwbElementAudio.contains(elementType)) + sRet = tIWBAudio; + else + if (iwbElementVideo.contains(elementType)) + sRet = tIWBVideo; + } + } + } + else + sRet = element.tagName(); + + return sRet; +} + +int UBCFFAdaptor::UBToCFFConverter::getElementLayer(const QDomElement &element) +{ + int iRetLayer = 0; + if (element.hasAttribute(aZLayer)) + iRetLayer = (int)element.attribute(aZLayer).toDouble(); + else + iRetLayer = DEFAULT_LAYER; + + return iRetLayer; +} + +bool UBCFFAdaptor::UBToCFFConverter::itIsSupportedFormat(const QString &format) const +{ + bool bRet; + + QStringList tsl = format.split(".", QString::SkipEmptyParts); + if (0 < tsl.count()) + bRet = cffSupportedFileFormats.contains(tsl.at(tsl.count()-1)); + else + bRet = false; + + return bRet; +} + +bool UBCFFAdaptor::UBToCFFConverter::itIsFormatToConvert(const QString &format) const +{ + foreach (QString f, ubzFormatsToConvert.split(",")) + { + if (format == f) + return true; + } + return false; +} + +bool UBCFFAdaptor::UBToCFFConverter::itIsSVGElementAttribute(const QString ItemType, const QString &AttrName) +{ + QString allowedElementAttributes = iwbSVGItemsAttributes[ItemType]; + + allowedElementAttributes.remove("/t"); + allowedElementAttributes.remove(" "); + foreach(QString attr, allowedElementAttributes.split(",")) + { + if (AttrName == attr.trimmed()) + return true; + } + return false; +} + + +bool UBCFFAdaptor::UBToCFFConverter::itIsIWBAttribute(const QString &attribute) const +{ + foreach (QString attr, iwbElementAttributes.split(",")) + { + if (attribute == attr.trimmed()) + return true; + } + return false; +} + +bool UBCFFAdaptor::UBToCFFConverter::itIsUBZAttributeToConvert(const QString &attribute) const +{ + foreach (QString attr, ubzElementAttributesToConvert.split(",")) + { + if (attribute == attr.trimmed()) + return true; + } + return false; +} + +bool UBCFFAdaptor::UBToCFFConverter::ibwAddLine(int x1, int y1, int x2, int y2, QString color, int width, bool isBackground) +{ + bool bRet = true; + + QDomDocument doc; + + QDomElement svgBackgroundCrossPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":line"); + QDomElement iwbBackgroundCrossPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + QString sUUID = QUuid::createUuid().toString().remove("{").remove("}"); + + svgBackgroundCrossPart.setTagName(tIWBLine); + + svgBackgroundCrossPart.setAttribute(aX+"1", x1); + svgBackgroundCrossPart.setAttribute(aY+"1", y1); + svgBackgroundCrossPart.setAttribute(aX+"2", x2); + svgBackgroundCrossPart.setAttribute(aY+"2", y2); + + svgBackgroundCrossPart.setAttribute(aStroke, color); + svgBackgroundCrossPart.setAttribute(aStrokeWidth, width); + + svgBackgroundCrossPart.setAttribute(aID, sUUID); + + if (isBackground) + { + iwbBackgroundCrossPart.setAttribute(aRef, sUUID); + iwbBackgroundCrossPart.setAttribute(aLocked, avTrue); + + addIWBElementToResultModel(iwbBackgroundCrossPart); + } + + addSVGElementToResultModel(svgBackgroundCrossPart, mSvgElements, DEFAULT_BACKGROUND_CROSS_LAYER); + + if (!bRet) + { + qDebug() << "|error at creating crosses on background"; + errorStr = "CreatingCrossedBackgroundParsingError."; + } + + return bRet; +} + +QTransform UBCFFAdaptor::UBToCFFConverter::getTransformFromUBZ(const QDomElement &ubzElement) +{ + QTransform trRet; + + QStringList transformParameters; + + QString ubzTransform = ubzElement.attribute(aTransform); + ubzTransform.remove("matrix"); + ubzTransform.remove("("); + ubzTransform.remove(")"); + + transformParameters = ubzTransform.split(",", QString::SkipEmptyParts); + + if (6 <= transformParameters.count()) + { + QTransform *tr = NULL; + tr = new QTransform(transformParameters.at(0).toDouble(), + transformParameters.at(1).toDouble(), + transformParameters.at(2).toDouble(), + transformParameters.at(3).toDouble(), + transformParameters.at(4).toDouble(), + transformParameters.at(5).toDouble()); + + trRet = *tr; + + delete tr; + } + + if (6 <= transformParameters.count()) + { + QTransform *tr = NULL; + tr = new QTransform(transformParameters.at(0).toDouble(), + transformParameters.at(1).toDouble(), + transformParameters.at(2).toDouble(), + transformParameters.at(3).toDouble(), + transformParameters.at(4).toDouble(), + transformParameters.at(5).toDouble()); + + trRet = *tr; + + delete tr; + } + return trRet; +} + +qreal UBCFFAdaptor::UBToCFFConverter::getAngleFromTransform(const QTransform &tr) +{ + qreal angle = -(atan(tr.m21()/tr.m11())*180/PI); + if (tr.m21() > 0 && tr.m11() < 0) + angle += 180; + else + if (tr.m21() < 0 && tr.m11() < 0) + angle += 180; + return angle; +} + +void UBCFFAdaptor::UBToCFFConverter::setGeometryFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement) +{ + setCoordinatesFromUBZ(ubzElement,iwbElement); + + + +} + +void UBCFFAdaptor::UBToCFFConverter::setCoordinatesFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement) +{ + QTransform tr; + + if (QString() != ubzElement.attribute(aTransform)) + tr = getTransformFromUBZ(ubzElement); + + qreal x = ubzElement.attribute(aX).toDouble(); + qreal y = ubzElement.attribute(aY).toDouble(); + qreal height = ubzElement.attribute(aHeight).toDouble(); + qreal width = ubzElement.attribute(aWidth).toDouble(); + + qreal alpha = getAngleFromTransform(tr); + + QRectF itemRect; + QGraphicsRectItem item; + + item.setRect(0,0, width, height); + item.setTransform(tr); + + item.setTransformOriginPoint(item.boundingRect().center()); + item.setRotation(-alpha); + QMatrix sceneMatrix = item.sceneMatrix(); + + iwbElement.setAttribute(aX, x); + iwbElement.setAttribute(aY, y); + iwbElement.setAttribute(aHeight, height*sceneMatrix.m22()); + iwbElement.setAttribute(aWidth, width*sceneMatrix.m11()); + iwbElement.setAttribute(aTransform, QString("rotate(%1) translate(%2,%3)").arg(alpha) + .arg(sceneMatrix.dx()) + .arg(sceneMatrix.dy())); +} + +bool UBCFFAdaptor::UBToCFFConverter::setContentFromUBZ(const QDomElement &ubzElement, QDomElement &svgElement) +{ + bool bRet = true; + + QString srcPath; + if (tUBZForeignObject != ubzElement.tagName()) + srcPath = ubzElement.attribute(aUBZHref); + else + srcPath = ubzElement.attribute(aSrc); + + QString sSrcContentFolder = getSrcContentFolderName(srcPath); + QString sSrcFileName = sourcePath + "/" + srcPath ; + QString fileExtention = getExtentionFromFileName(sSrcFileName); + QString sDstContentFolder = getDstContentFolderName(ubzElement.tagName()); + QString sDstFileName(QString(QUuid::createUuid().toString()+"."+convertExtention(fileExtention)).remove("{").remove("}")); + + + if (itIsSupportedFormat(fileExtention)) // format is supported and we can copy src. files without changing. + { + sSrcFileName = sourcePath + "/" + sSrcContentFolder + "/" + getFileNameFromPath(srcPath); // some elements must be exported as images, so we take hes existing thumbnails. + + QFile srcFile; + srcFile.setFileName(sSrcFileName); + + QDir dstDocFolder(destinationPath); + + if (!dstDocFolder.exists(sDstContentFolder)) + bRet &= dstDocFolder.mkdir(sDstContentFolder); + + if (bRet) + { + QString dstFilePath = destinationPath+"/"+sDstContentFolder+"/"+sDstFileName; + bRet &= srcFile.copy(dstFilePath); + } + + if (bRet) + { + svgElement.setAttribute(aSVGHref, sDstContentFolder+"/"+sDstFileName); + svgElement.setAttribute(aSVGRequiredExtension, svgRequiredExtensionPrefix+convertExtention(fileExtention)); + } + } + else + if (itIsFormatToConvert(fileExtention)) // we cannot copy that source files. We need to create dst. file from src. file without copy. + { + if (feSvg == fileExtention) + { + QDir dstDocFolder(destinationPath); + + if (!dstDocFolder.exists(sDstContentFolder)) + bRet &= dstDocFolder.mkdir(sDstContentFolder); + + if (bRet) + { + if (feSvg == fileExtention) // svg images must be converted to PNG. + { + QString dstFilePath = destinationPath+"/"+sDstContentFolder+"/"+sDstFileName; + bRet &= createPngFromSvg(sSrcFileName, dstFilePath, getTransformFromUBZ(ubzElement)); + } + else + bRet = false; + } + + if (bRet) + { + svgElement.setAttribute(aSVGHref, sDstContentFolder+"/"+sDstFileName); + svgElement.setAttribute(aSVGRequiredExtension, svgRequiredExtensionPrefix+fePng); + } + } + } + + if (!bRet) + { + qDebug() << "format is not supported by CFF"; + } + + return bRet; +} + +void UBCFFAdaptor::UBToCFFConverter::setCFFTextFromHTMLTextNode(const QDomElement htmlTextNode, QDomElement &iwbElement) +{ + + QDomDocument textDoc; + + QDomElement textParentElement = iwbElement; + + QString textString; + QDomNode htmlPNode = htmlTextNode.firstChild(); + bool bTbreak = false; + + // reads HTML text strings - each string placed in separate

section + while(!htmlPNode.isNull()) + { + // add for split strings + if (bTbreak) + { + bTbreak = false; + + QDomElement tbreakNode = textDoc.createElementNS(svgIWBNS, svgIWBNSPrefix+":"+tIWBTbreak); + textParentElement.appendChild(tbreakNode.cloneNode(true)); + } + + QDomNode spanNode = htmlPNode.firstChild(); + + + while (!spanNode.isNull()) + { + if (spanNode.isText()) + { + QDomText nodeText = textDoc.createTextNode(spanNode.nodeValue()); + textParentElement.appendChild(nodeText.cloneNode(true)); + } + else + if (spanNode.isElement()) + { + QDomElement spanElement = textDoc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBTspan); + if (spanNode.hasAttributes()) + { + int attrCount = spanNode.attributes().count(); + if (0 < attrCount) + { + for (int i = 0; i < attrCount; i++) + { + // html attributes like: style="font-size:40pt; color:"red";". + QStringList cffAttributes = spanNode.attributes().item(i).nodeValue().split(";", QString::SkipEmptyParts); + { + for (int i = 0; i < cffAttributes.count(); i++) + { + QString attr = cffAttributes.at(i).trimmed(); + QStringList AttrVal = attr.split(":", QString::SkipEmptyParts); + if(1 < AttrVal.count()) + { + QString sAttr = ubzAttrNameToCFFAttrName(AttrVal.at(0)); + if (itIsSVGElementAttribute(spanElement.tagName(), sAttr)) + spanElement.setAttribute(sAttr, ubzAttrValueToCFFAttrName(AttrVal.at(1))); + } + } + } + } + } + } + QDomText nodeText = textDoc.createTextNode(spanNode.firstChild().nodeValue()); + spanElement.appendChild(nodeText); + textParentElement.appendChild(spanElement.cloneNode(true)); + } + spanNode = spanNode.nextSibling(); + } + + bTbreak = true; + htmlPNode = htmlPNode.nextSibling(); + } +} + +QString UBCFFAdaptor::UBToCFFConverter::ubzAttrNameToCFFAttrName(QString cffAttrName) +{ + QString sRet = cffAttrName; + if (QString("color") == cffAttrName) + sRet = QString("fill"); + + return sRet; +} +QString UBCFFAdaptor::UBToCFFConverter::ubzAttrValueToCFFAttrName(QString cffValue) +{ + QString sRet = cffValue; + if (QString("text") == cffValue) + sRet = QString("normal"); + + return sRet; +} + +bool UBCFFAdaptor::UBToCFFConverter::setCFFAttribute(const QString &attributeName, const QString &attributeValue, const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement) +{ + bool bRet = true; + bool bNeedsIWBSection = false; + + if (itIsIWBAttribute(attributeName)) + { + if (!((aBackground == attributeName) && (avFalse == attributeValue))) + { + iwbElement.setAttribute(attributeName, attributeValue); + bNeedsIWBSection = true; + } + } + else + if (itIsUBZAttributeToConvert(attributeName)) + { + if (aTransform == attributeName) + { + setGeometryFromUBZ(ubzElement, svgElement); + } + else + if (attributeName.contains(aUBZHref)||attributeName.contains(aSrc)) + { + bRet &= setContentFromUBZ(ubzElement, svgElement); + bNeedsIWBSection = bRet||bNeedsIWBSection; + } + } + else + if (itIsSVGElementAttribute(svgElement.tagName(),attributeName)) + { + svgElement.setAttribute(attributeName, attributeValue); + } + + if (bNeedsIWBSection) + { + if (0 < iwbElement.attributes().count()) + { + + QStringList tl = ubzElement.attribute(aSVGHref).split("/"); + QString id = tl.at(tl.count()-1); + // if element already have an ID, we use it. Else we create new id for element. + if (QString() == id) + id = QUuid::createUuid().toString().remove("{").remove("}"); + + svgElement.setAttribute(aID, id); + iwbElement.setAttribute(aRef, id); + } + } + + return bRet; +} + +bool UBCFFAdaptor::UBToCFFConverter::setCommonAttributesFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement) +{ + bool bRet = true; + + for (int i = 0; i < ubzElement.attributes().count(); i++) + { + QDomNode attribute = ubzElement.attributes().item(i); + QString attributeName = ubzAttrNameToCFFAttrName(attribute.nodeName().remove("ub:")); + + bRet &= setCFFAttribute(attributeName, ubzAttrValueToCFFAttrName(attribute.nodeValue()), ubzElement, iwbElement, svgElement); + if (!bRet) break; + } + return bRet; +} + +void UBCFFAdaptor::UBToCFFConverter::setViewBox(QRect viewbox) +{ + mViewbox |= viewbox; +} + +QDomNode UBCFFAdaptor::UBToCFFConverter::findTextNode(const QDomNode &node) +{ + QDomNode iterNode = node; + + while (!iterNode.isNull()) + { + if (iterNode.isText()) + { + if (!iterNode.isNull()) + return iterNode; + } + else + { + if (!iterNode.firstChild().isNull()) + { + QDomNode foundNode = findTextNode(iterNode.firstChild()); + if (!foundNode.isNull()) + if (foundNode.isText()) + return foundNode; + } + } + if (!iterNode.nextSibling().isNull()) + iterNode = iterNode.nextSibling(); + else + break; + } + return iterNode; +} + +QDomNode UBCFFAdaptor::UBToCFFConverter::findNodeByTagName(const QDomNode &node, QString tagName) +{ + QDomNode iterNode = node; + + while (!iterNode.isNull()) + { + QString t = iterNode.toElement().tagName(); + if (tagName == t) + return iterNode; + else + { + if (!iterNode.firstChildElement().isNull()) + { + QDomNode foundNode = findNodeByTagName(iterNode.firstChildElement(), tagName); + if (!foundNode.isNull()) + if (foundNode.isElement()) + { + if (tagName == foundNode.toElement().tagName()) + return foundNode; + } + else + break; + } + } + + if (!iterNode.nextSibling().isNull()) + iterNode = iterNode.nextSibling(); + else + break; + } + return QDomNode(); + +} + +bool UBCFFAdaptor::UBToCFFConverter::createBackground(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "|creating element background"; + + + QDomDocument doc; + + //QDomElement svgBackgroundElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tUBZImage); + QDomElement svgBackgroundElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBRect); + QDomElement iwbBackgroundElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + + QRect bckRect(mViewbox); + + if (0 <= mViewbox.topLeft().x()) + bckRect.topLeft().setX(0); + + if (0 <= mViewbox.topLeft().y()) + bckRect.topLeft().setY(0); + +// QString backgroundImagePath = createBackgroundImage(element, QSize(bckRect.width(), bckRect.height())); +// if (QString() != backgroundImagePath) + if (QRect() != bckRect) + { + QString sElementID = QUuid::createUuid().toString().remove("{").remove("}"); + + bool darkBackground = (avTrue == element.attribute(aDarkBackground)); + svgBackgroundElementPart.setAttribute(aFill, darkBackground ? "black" : "white"); + svgBackgroundElementPart.setAttribute(aID, sElementID); + svgBackgroundElementPart.setAttribute(aX, bckRect.x()); + svgBackgroundElementPart.setAttribute(aY, bckRect.y()); + svgBackgroundElementPart.setAttribute(aHeight, bckRect.height()); + svgBackgroundElementPart.setAttribute(aWidth, bckRect.width()); + + //svgBackgroundElementPart.setAttribute(aSVGHref, backgroundImagePath); + + iwbBackgroundElementPart.setAttribute(aRef, sElementID); + iwbBackgroundElementPart.setAttribute(aBackground, avTrue); + //iwbBackgroundElementPart.setAttribute(aLocked, avTrue); + + addSVGElementToResultModel(svgBackgroundElementPart, dstSvgList, DEFAULT_BACKGROUND_LAYER); + addIWBElementToResultModel(iwbBackgroundElementPart); + return true; + } + else + { + qDebug() << "|error at creating element background"; + errorStr = "CreatingElementBackgroundParsingError."; + return false; + } +} + +QString UBCFFAdaptor::UBToCFFConverter::createBackgroundImage(const QDomElement &element, QSize size) +{ + QString sRet; + + QString sDstFileName(fIWBBackground); + + bool bDirExists = true; + QDir dstDocFolder(destinationPath); + + if (!dstDocFolder.exists(cfImages)) + bDirExists &= dstDocFolder.mkdir(cfImages); + + QString dstFilePath; + if (bDirExists) + dstFilePath = destinationPath+"/"+cfImages+"/"+sDstFileName; + + if (!QFile().exists(dstFilePath)) + { + QRect rect(0,0, size.width(), size.height()); + + QImage *bckImage = new QImage(size, QImage::Format_RGB888); + + QPainter *painter = new QPainter(bckImage); + + bool darkBackground = (avTrue == element.attribute(aDarkBackground)); + + QColor bCrossColor; + + bCrossColor = darkBackground?QColor(Qt::white):QColor(Qt::blue); + int penAlpha = (int)(255/2); // default Sankore value for transform.m11 < 1 + bCrossColor.setAlpha(penAlpha); + painter->setPen(bCrossColor); + painter->setBrush(darkBackground?QColor(Qt::black):QColor(Qt::white)); + + painter->drawRect(rect); + + if (avTrue == element.attribute(aCrossedBackground)) + { + qreal firstY = ((int) (rect.y () / iCrossSize)) * iCrossSize; + + for (qreal yPos = firstY; yPos <= rect.y () + rect.height (); yPos += iCrossSize) + { + painter->drawLine (rect.x (), yPos, rect.x () + rect.width (), yPos); + } + + qreal firstX = ((int) (rect.x () / iCrossSize)) * iCrossSize; + + for (qreal xPos = firstX; xPos <= rect.x () + rect.width (); xPos += iCrossSize) + { + painter->drawLine (xPos, rect.y (), xPos, rect.y () + rect.height ()); + } + } + + painter->end(); + painter->save(); + + if (QString() != dstFilePath) + if (bckImage->save(dstFilePath)) + sRet = cfImages+"/"+sDstFileName; + + delete bckImage; + delete painter; + } + else + sRet = cfImages+"/"+sDstFileName; + + return sRet; +} + +bool UBCFFAdaptor::UBToCFFConverter::createPngFromSvg(QString &svgPath, QString &dstPath, QTransform transformation, QSize size) +{ + if (QFile().exists(svgPath)) + { + QImage i(svgPath); + + QSize iSize = (QSize() == size)?QSize(i.size().width()*transformation.m11(), i.size().height()*transformation.m22()):size; + + QImage image(iSize, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter imagePainter(&image); + QSvgRenderer renderer(svgPath); + renderer.render(&imagePainter); + + return image.save(dstPath); + + } + else + return false; +} + + +bool UBCFFAdaptor::UBToCFFConverter::parseSVGGGroup(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "|parsing g section"; + QDomElement nextElement = element.firstChildElement(); + if (nextElement.isNull()) { + qDebug() << "Empty g element"; + errorStr = "EmptyGSection"; + return false; + } + + QMultiMap svgElements; + + QDomDocument doc; + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBG); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + // Elements can know about its layer, so it must add result QDomElements to ordrered list. + while (!nextElement.isNull()) { + QString tagName = nextElement.tagName(); + if (tagName == tUBZLine) parseUBZLine(nextElement, svgElements); + else if (tagName == tUBZPolygon) parseUBZPolygon(nextElement, svgElements); + else if (tagName == tUBZPolyline) parseUBZPolyline(nextElement, svgElements); + + nextElement = nextElement.nextSiblingElement(); + } + + QList layers; + QMapIterator nextSVGElement(svgElements); + while (nextSVGElement.hasNext()) + layers << nextSVGElement.next().key(); + + qSort(layers); + int layer = layers.at(0); + + nextSVGElement.toFront(); + while (nextSVGElement.hasNext()) + svgElementPart.appendChild(nextSVGElement.next().value()); + + addSVGElementToResultModel(svgElementPart, dstSvgList, layer); + + return true; +} +bool UBCFFAdaptor::UBToCFFConverter::parseUBZImage(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "|parsing image"; + + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + addSVGElementToResultModel(svgElementPart, dstSvgList, getElementLayer(element)); + + if (0 < iwbElementPart.attributes().count()) + addIWBElementToResultModel(iwbElementPart); + return true; + } + else + { + qDebug() << "|error at image parsing"; + errorStr = "ImageParsingError"; + return false; + + } +} + +bool UBCFFAdaptor::UBToCFFConverter::parseUBZVideo(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "|parsing video"; + + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + QDomElement svgSwitchSection = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBSwitch); + svgSwitchSection.appendChild(svgElementPart); + + // if viewer cannot open that content - it must use that: + QDomElement svgText = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBTextArea); + svgText.setAttribute(aX, svgElementPart.attribute(aX)); + svgText.setAttribute(aY, svgElementPart.attribute(aY)); + svgText.setAttribute(aWidth, svgElementPart.attribute(aWidth)); + svgText.setAttribute(aHeight, svgElementPart.attribute(aHeight)); + svgText.setAttribute(aTransform, svgElementPart.attribute(aTransform)); + + QDomText text = doc.createTextNode("Cannot Open Content"); + svgText.appendChild(text); + + svgSwitchSection.appendChild(svgText); + + addSVGElementToResultModel(svgSwitchSection, dstSvgList, getElementLayer(element)); + + if (0 < iwbElementPart.attributes().count()) + addIWBElementToResultModel(iwbElementPart); + return true; + } + else + { + qDebug() << "|error at video parsing"; + errorStr = "VideoParsingError"; + return false; + } +} + +bool UBCFFAdaptor::UBToCFFConverter::parseUBZAudio(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "|parsing audio"; + + // audio file must be linked to cff item excluding video. + // to do: + // 1 add image for audio element. + // 2 set id for this element + // 3 add section with xlink:href to audio file + // 4 add shild to a section with id of the image + + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + //we must create image-containers for audio files + int audioImageDimention = qMin(svgElementPart.attribute(aWidth).toInt(), svgElementPart.attribute(aHeight).toInt()); + QString srcAudioImageFile(sAudioElementImage); + QString elementId = QString(QUuid::createUuid().toString()).remove("{").remove("}"); + QString sDstAudioImageFileName = elementId+"."+fePng; + QString dstAudioImageFilePath = destinationPath+"/"+cfImages+"/"+sDstAudioImageFileName; + QString dstAudioImageRelativePath = cfImages+"/"+sDstAudioImageFileName; + + QFile srcFile(srcAudioImageFile); + + //creating folder for audioImage + QDir dstDocFolder(destinationPath); + bool bRes = true; + if (!dstDocFolder.exists(cfImages)) + bRes &= dstDocFolder.mkdir(cfImages); + + // CFF cannot show SVG images, so we need to convert it to png. + if (bRes && createPngFromSvg(srcAudioImageFile, dstAudioImageFilePath, getTransformFromUBZ(element), QSize(audioImageDimention, audioImageDimention))) + { + QDomElement svgSwitchSection = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBSwitch); + + // first we place content + QDomElement svgASection = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBA); + svgASection.setAttribute(aSVGHref, svgElementPart.attribute(aSVGHref)); + + svgElementPart.setTagName(tIWBImage); + svgElementPart.setAttribute(aSVGHref, dstAudioImageRelativePath); + svgElementPart.setAttribute(aHeight, audioImageDimention); + svgElementPart.setAttribute(aWidth, audioImageDimention); + + svgASection.appendChild(svgElementPart); + + svgSwitchSection.appendChild(svgASection); + + // if viewer cannot open that content - it must use that: + QDomElement svgText = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + tIWBTextArea); + svgText.setAttribute(aX, svgElementPart.attribute(aX)); + svgText.setAttribute(aY, svgElementPart.attribute(aY)); + svgText.setAttribute(aWidth, svgElementPart.attribute(aWidth)); + svgText.setAttribute(aHeight, svgElementPart.attribute(aHeight)); + svgText.setAttribute(aTransform, svgElementPart.attribute(aTransform)); + + QDomText text = doc.createTextNode("Cannot Open Content"); + svgText.appendChild(text); + + svgSwitchSection.appendChild(svgText); + + addSVGElementToResultModel(svgSwitchSection, dstSvgList, getElementLayer(element)); + + if (0 < iwbElementPart.attributes().count()) + addIWBElementToResultModel(iwbElementPart); + return true; + } + return false; + } + else + { + qDebug() << "|error at audio parsing"; + errorStr = "AudioParsingError"; + return false; + } +} + +bool UBCFFAdaptor::UBToCFFConverter::parseForeignObject(const QDomElement &element, QMultiMap &dstSvgList) +{ + + if (element.attribute(aUBZType) == avUBZText) { + return parseUBZText(element, dstSvgList); + } + + qDebug() << "|parsing foreign object"; + + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + addSVGElementToResultModel(svgElementPart, dstSvgList, getElementLayer(element)); + if (0 < iwbElementPart.attributes().count()) + addIWBElementToResultModel(iwbElementPart); + return true; + } + else + { + qDebug() << "|error at parsing foreign object"; + errorStr = "ForeignObjectParsingError"; + return false; + } +} + +bool UBCFFAdaptor::UBToCFFConverter::parseUBZText(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "|parsing text"; + + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (element.hasChildNodes()) + { + QDomDocument htmlDoc; + htmlDoc.setContent(findTextNode(element).nodeValue()); + QDomNode bodyNode = findNodeByTagName(htmlDoc.firstChildElement(), "body"); + + setCFFTextFromHTMLTextNode(bodyNode.toElement(), svgElementPart); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + QString commonParams; + for (int i = 0; i < bodyNode.attributes().count(); i++) + { + commonParams += " " + bodyNode.attributes().item(i).nodeValue(); + } + commonParams.remove(" "); + commonParams.remove("'"); + + QStringList commonAttributes = commonParams.split(";", QString::SkipEmptyParts); + for (int i = 0; i < commonAttributes.count(); i++) + { + QStringList AttrVal = commonAttributes.at(i).split(":", QString::SkipEmptyParts); + if (1 < AttrVal.count()) + { + QString sAttr = ubzAttrNameToCFFAttrName(AttrVal.at(0)); + QString sVal = ubzAttrValueToCFFAttrName(AttrVal.at(1)); + + setCFFAttribute(sAttr, sVal, element, iwbElementPart, svgElementPart); + } + } + addSVGElementToResultModel(svgElementPart, dstSvgList, getElementLayer(element)); + if (0 < iwbElementPart.attributes().count()) + addIWBElementToResultModel(iwbElementPart); + return true; + } + return false; + } + else + { + qDebug() << "|error at text parsing"; + errorStr = "TextParsingError"; + return false; + } +} + +bool UBCFFAdaptor::UBToCFFConverter::parseUBZPolygon(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "||parsing polygon"; + + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + addSVGElementToResultModel(svgElementPart, dstSvgList, getElementLayer(element)); + + if (0 < iwbElementPart.attributes().count()) + { + QString id = QUuid::createUuid().toString().remove("{").remove("}"); + svgElementPart.setAttribute(aID, id); + iwbElementPart.setAttribute(aRef, id); + + addIWBElementToResultModel(iwbElementPart); + } + return true; + } + else + { + qDebug() << "||error at parsing polygon"; + errorStr = "PolygonParsingError"; + return false; + } + +} + +bool UBCFFAdaptor::UBToCFFConverter::parseUBZPolyline(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "||parsing polyline"; + QDomElement resElement; + + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + addSVGElementToResultModel(svgElementPart, dstSvgList, getElementLayer(element)); + + if (0 < iwbElementPart.attributes().count()) + { + QString id = QUuid::createUuid().toString().remove("{").remove("}"); + svgElementPart.setAttribute(aID, id); + iwbElementPart.setAttribute(aRef, id); + + addIWBElementToResultModel(iwbElementPart); + } + return true; + } + else + { + qDebug() << "||error at parsing polygon"; + errorStr = "PolylineParsingError"; + return false; + } + +} + +bool UBCFFAdaptor::UBToCFFConverter::parseUBZLine(const QDomElement &element, QMultiMap &dstSvgList) +{ + qDebug() << "||parsing line"; + QDomElement resElement; + QDomDocument doc; + + QDomElement svgElementPart = doc.createElementNS(svgIWBNS,svgIWBNSPrefix + ":" + getElementTypeFromUBZ(element)); + QDomElement iwbElementPart = doc.createElementNS(iwbNS,iwbNsPrefix + ":" + tElement); + + if (setCommonAttributesFromUBZ(element, iwbElementPart, svgElementPart)) + { + addSVGElementToResultModel(svgElementPart, dstSvgList, getElementLayer(element)); + + if (0 < iwbElementPart.attributes().count()) + { + QString id = QUuid::createUuid().toString().remove("{").remove("}"); + svgElementPart.setAttribute(aID, id); + iwbElementPart.setAttribute(aRef, id); + + addIWBElementToResultModel(iwbElementPart); + } + } + else + { + qDebug() << "||error at parsing polygon"; + errorStr = "LineParsingError"; + return false; + } + return true; +} + +void UBCFFAdaptor::UBToCFFConverter::addSVGElementToResultModel(const QDomElement &element, QMultiMap &dstList, int layer) +{ + int elementLayer = (DEFAULT_LAYER == layer) ? DEFAULT_LAYER : layer; + dstList.setInsertInOrder(true); + QDomElement rootElement = element.cloneNode(true).toElement(); + mDocumentToWrite->firstChildElement().appendChild(rootElement); + dstList.insert(elementLayer, rootElement); +} + +void UBCFFAdaptor::UBToCFFConverter::addIWBElementToResultModel(const QDomElement &element) +{ + QDomElement rootElement = element.cloneNode(true).toElement(); + mDocumentToWrite->firstChildElement().appendChild(rootElement); + mExtendedElements.append(rootElement); +} + +UBCFFAdaptor::UBToCFFConverter::~UBToCFFConverter() +{ + if (mDataModel) + delete mDataModel; + if (mIWBContentWriter) + delete mIWBContentWriter; +} +bool UBCFFAdaptor::UBToCFFConverter::isValid() const +{ + bool result = QFileInfo(sourcePath).exists() + && QFileInfo(sourcePath).isDir() + && errorStr == noErrorMsg; + + if (!result) { + qDebug() << "specified data is not valid"; + errorStr = "ValidateDataError"; + } + + return result; +} + +void UBCFFAdaptor::UBToCFFConverter::fillNamespaces() +{ + mIWBContentWriter->writeDefaultNamespace(svgUBZNS); + mIWBContentWriter->writeNamespace(iwbNS, iwbNsPrefix); + mIWBContentWriter->writeNamespace(svgIWBNS, svgIWBNSPrefix); + mIWBContentWriter->writeNamespace(xlinkNS, xlinkNSPrefix); +} + +QString UBCFFAdaptor::UBToCFFConverter::digitFileFormat(int digit) const +{ + return QString("%1").arg(digit, 3, 10, QLatin1Char('0')); +} +QString UBCFFAdaptor::UBToCFFConverter::contentIWBFileName() const +{ + return destinationPath + "/" + fIWBContent; +} + +//setting SVG dimenitons +QSize UBCFFAdaptor::UBToCFFConverter::getSVGDimentions(const QString &element) +{ + + QStringList dimList; + + dimList = element.split(dimensionsDelimiter1, QString::KeepEmptyParts); + if (dimList.count() != 2) // row unlike 0x0 + return QSize(); + + bool ok; + + int width = dimList.takeFirst().toInt(&ok); + if (!ok || !width) + return QSize(); + + int height = dimList.takeFirst().toInt(&ok); + if (!ok || !height) + return QSize(); + + return QSize(width, height); +} + +//Setting viewbox rectangle +QRect UBCFFAdaptor::UBToCFFConverter::getViewboxRect(const QString &element) const +{ + QStringList dimList; + + dimList = element.split(dimensionsDelimiter2, QString::KeepEmptyParts); + if (dimList.count() != 4) // row unlike 0 0 0 0 + return QRect(); + + bool ok = false; + + int x = dimList.takeFirst().toInt(&ok); + if (!ok || !x) + return QRect(); + + int y = dimList.takeFirst().toInt(&ok); + if (!ok || !y) + return QRect(); + + int width = dimList.takeFirst().toInt(&ok); + if (!ok || !width) + return QRect(); + + int height = dimList.takeFirst().toInt(&ok); + if (!ok || !height) + return QRect(); + + return QRect(x, y, width, height); +} + +QString UBCFFAdaptor::UBToCFFConverter::rectToIWBAttr(const QRect &rect) const +{ + if (rect.isNull()) return QString(); + + return QString("%1 %2 %3 %4").arg(rect.topLeft().x()) + .arg(rect.topLeft().y()) + .arg(rect.width()) + .arg(rect.height()); +} + +UBCFFAdaptor::UBToUBZConverter::UBToUBZConverter() +{ + +} diff --git a/plugins/cffadaptor/src/UBCFFAdaptor.h b/plugins/cffadaptor/src/UBCFFAdaptor.h new file mode 100644 index 00000000..385df3cc --- /dev/null +++ b/plugins/cffadaptor/src/UBCFFAdaptor.h @@ -0,0 +1,146 @@ +#ifndef UBCFFADAPTOR_H +#define UBCFFADAPTOR_H + +#include "UBCFFAdaptor_global.h" + +#include + +class QTransform; +class QDomDocument; +class QDomElement; +class QDomNode; +class QuaZipFile; + +class UBCFFADAPTORSHARED_EXPORT UBCFFAdaptor { + class UBToCFFConverter; + +public: + UBCFFAdaptor(); + ~UBCFFAdaptor(); + + bool convertUBZToIWB(const QString &from, const QString &to); + bool deleteDir(const QString& pDirPath) const; + +private: + QString uncompressZip(const QString &zipFile); + bool compressZip(const QString &source, const QString &destination); + bool compressDir(const QString &dirName, const QString &parentDir, QuaZipFile *outZip); + bool compressFile(const QString &fileName, const QString &parentDir, QuaZipFile *outZip); + + QString createNewTmpDir(); + bool freeDir(const QString &dir); + void freeTmpDirs(); + +private: + QStringList tmpDirs; + +private: + + class UBToCFFConverter { + + static const int DEFAULT_LAYER = -100000; + + public: + UBToCFFConverter(const QString &source, const QString &destination); + ~UBToCFFConverter(); + bool isValid() const; + QString lastErrStr() const {return errorStr;} + bool parse(); + + private: + void fillNamespaces(); + + bool parseMetadata(); + bool parseContent(); + QDomElement parsePageset(const QStringList &pageFileNames); + QDomElement parsePage(const QString &pageFileName); + QDomElement parseSvgPageSection(const QDomElement &element); + void writeQDomElementToXML(const QDomNode &node); + bool writeExtendedIwbSection(); + QDomElement parseGroupPageSection(const QDomElement &element); + + bool createBackground(const QDomElement &element, QMultiMap &dstSvgList); + QString createBackgroundImage(const QDomElement &element, QSize size); + bool createPngFromSvg(QString &svgPath, QString &dstPath, QTransform transformation, QSize size = QSize()); + + bool parseSVGGGroup(const QDomElement &element, QMultiMap &dstSvgList); + bool parseUBZImage(const QDomElement &element, QMultiMap &dstSvgList); + bool parseUBZVideo(const QDomElement &element, QMultiMap &dstSvgList); + bool parseUBZAudio(const QDomElement &element, QMultiMap &dstSvgList); + bool parseForeignObject(const QDomElement &element, QMultiMap &dstSvgList); + bool parseUBZText(const QDomElement &element, QMultiMap &dstSvgList); + + bool parseUBZPolygon(const QDomElement &element, QMultiMap &dstSvgList); + bool parseUBZPolyline(const QDomElement &element, QMultiMap &dstSvgList); + bool parseUBZLine(const QDomElement &element, QMultiMap &dstSvgList); + void addSVGElementToResultModel(const QDomElement &element, QMultiMap &dstList, int layer = DEFAULT_LAYER); + void addIWBElementToResultModel(const QDomElement &element); + + qreal getAngleFromTransform(const QTransform &tr); + QString getDstContentFolderName(const QString &elementType); + QString getSrcContentFolderName(QString href); + QString getFileNameFromPath(QString sPath); + QString getExtentionFromFileName(const QString &filename); + QString convertExtention(const QString &ext); + QString getElementTypeFromUBZ(const QDomElement &element); + + int getElementLayer(const QDomElement &element); + + bool itIsSupportedFormat(const QString &format) const; + bool itIsFormatToConvert(const QString &format) const; + bool itIsSVGElementAttribute(const QString ItemType, const QString &AttrName); + bool itIsIWBAttribute(const QString &attribute) const; + bool itIsUBZAttributeToConvert(const QString &attribute) const; + + bool ibwAddLine(int x1, int y1, int x2, int y2, QString color=QString(), int width=1, bool isBackground=false); + + QTransform getTransformFromUBZ(const QDomElement &ubzElement); + void setGeometryFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement); + void setCoordinatesFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement); + bool setContentFromUBZ(const QDomElement &ubzElement, QDomElement &svgElement); + void setCFFTextFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement); + void setCFFTextFromHTMLTextNode(const QDomElement htmlTextNode, QDomElement &iwbElement); + QString ubzAttrNameToCFFAttrName(QString cffAttrName); + QString ubzAttrValueToCFFAttrName(QString cffAttrValue); + + bool setCFFAttribute(const QString &attributeName, const QString &attributeValue, const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement); + bool setCommonAttributesFromUBZ(const QDomElement &ubzElement, QDomElement &iwbElement, QDomElement &svgElement); + void setViewBox(QRect viewbox); + + QDomNode findTextNode(const QDomNode &node); + QDomNode findNodeByTagName(const QDomNode &node, QString tagName); + + QSize getSVGDimentions(const QString &element); + + inline QRect getViewboxRect(const QString &element) const; + inline QString rectToIWBAttr(const QRect &rect) const; + inline QString digitFileFormat(int num) const; + inline bool strToBool(const QString &in) const {return in == "true";} + QString contentIWBFileName() const; + + private: + QMap iwbSVGItemsAttributes; + QDomDocument *mDataModel; //model for reading indata + QXmlStreamWriter *mIWBContentWriter; //stream to write outdata + QSize mSVGSize; //svg page size + QRect mViewbox; //Main viewbox parameter for CFF + QString sourcePath; // dir with unpacked source data (ubz) + QString destinationPath; //dir with unpacked destination data (iwb) + QDomDocument *mDocumentToWrite; //document for saved QDomElements from mSvgElements and mExtendedElements + QMultiMap mSvgElements; //Saving svg elements to have a sorted by z order list of elements to write; + QList mExtendedElements; //Saving extended options of elements to be able to add them to the end of result iwb document; + mutable QString errorStr; // last error string message + + public: + operator bool() const {return isValid();} + }; + + class UBToUBZConverter { + public: + UBToUBZConverter(); + }; + + +}; + +#endif // UBCFFADAPTOR_H diff --git a/plugins/cffadaptor/src/UBCFFAdaptor_global.h b/plugins/cffadaptor/src/UBCFFAdaptor_global.h new file mode 100644 index 00000000..2fbf871f --- /dev/null +++ b/plugins/cffadaptor/src/UBCFFAdaptor_global.h @@ -0,0 +1,12 @@ +#ifndef UBCFFADAPTOR_GLOBAL_H +#define UBCFFADAPTOR_GLOBAL_H + +#include + +#if defined(UBCFFADAPTOR_LIBRARY) +# define UBCFFADAPTORSHARED_EXPORT Q_DECL_EXPORT +#else +# define UBCFFADAPTORSHARED_EXPORT Q_DECL_IMPORT +#endif + +#endif // UBCFFADAPTOR_GLOBAL_H diff --git a/plugins/cffadaptor/src/UBCFFConstants.h b/plugins/cffadaptor/src/UBCFFConstants.h new file mode 100644 index 00000000..9d7707ee --- /dev/null +++ b/plugins/cffadaptor/src/UBCFFConstants.h @@ -0,0 +1,378 @@ +#ifndef UBCFFCONSTANTS_H +#define UBCFFCONSTANTS_H + +#define PI 3.1415926535 + +const int DEFAULT_BACKGROUND_LAYER = -20000002; +const int DEFAULT_BACKGROUND_CROSS_LAYER = -20000001; + +// Constant names. Use only them instead const char* in each function + +// Constant fileNames; +const QString fMetadata = "metadata.rdf"; +const QString fIWBContent = "content.xml"; +const QString fIWBBackground = "background.png"; +const QString sAudioElementImage = ":images/soundOn.svg"; + +// Constant messages; +const QString noErrorMsg = "NoError"; + +// Tag names +const QString tDescription = "Description"; +const QString tIWBRoot = "iwb"; +const QString tIWBMeta = "meta"; +const QString tUBZSize = "size"; +const QString tSvg = "svg"; +const QString tIWBPage = "page"; +const QString tIWBPageSet = "pageset"; +const QString tId = "id"; +const QString tElement = "element"; +const QString tUBZGroup = "group"; +const QString tUBZG = "g"; +const QString tUBZPolygon = "polygon"; +const QString tUBZPolyline = "polyline"; +const QString tUBZLine = "line"; +const QString tUBZAudio = "audio"; +const QString tUBZVideo = "video"; +const QString tUBZImage = "image"; +const QString tUBZForeignObject = "foreignObject"; +const QString tUBZTextContent = "itemTextContent"; + +const QString tIWBA = "a"; +const QString tIWBG = "g"; +const QString tIWBSwitch = "switch"; +const QString tIWBImage = "image"; +const QString tIWBVideo = "video"; +const QString tIWBAudio = "audio"; +const QString tIWBText = "text"; +const QString tIWBTextArea = "textarea"; +const QString tIWBPolyLine = "polyline"; +const QString tIWBPolygon = "polygon"; +const QString tIWBFlash = "video"; +const QString tIWBRect = "rect"; +const QString tIWBLine = "line"; +const QString tIWBTbreak = "tbreak"; +const QString tIWBTspan = "tspan"; + +// Attributes names +const QString aIWBVersion = "version"; +const QString aOwner = "owner"; +const QString aDescription = "description"; +const QString aCreator = "creator"; +const QString aAbout = "about"; +const QString aIWBViewBox = "viewbox"; +const QString aUBZViewBox = "viewBox"; +const QString aDarkBackground = "dark-background"; +const QString aBackground = "background"; +const QString aCrossedBackground = "crossed-background"; +const QString aUBZType = "type"; +const QString aUBZUuid = "uuid"; +const QString aFill = "fill"; // IWB attribute contans color to fill + +const QString aID = "id"; // ID of any svg element can be placed in to iwb section +const QString aRef = "ref"; // as reference for applying additional attributes +const QString aSVGHref = "xlink:href"; // reference to file +const QString aIWBHref = "ref"; // reference to element ID +const QString aUBZHref = "href"; +const QString aSrc = "src"; +const QString aSVGRequiredExtension = "requiredExtensions"; + +const QString aX = "x"; +const QString aY = "y"; +const QString aWidth = "width"; +const QString aHeight = "height"; +const QString aStroke = "stroke"; +const QString aStrokeWidth = "stroke-width"; +const QString aPoints = "points"; +const QString aZLayer = "z-value"; +const QString aLayer = "layer"; +const QString aTransform = "transform"; +const QString aLocked = "locked"; +const QString aIWBName = "name"; +const QString aIWBContent = "content"; + + +// Attribute values +const QString avIWBVersionNo = "1.0"; +const QString avUBZText = "text"; +const QString avFalse = "false"; +const QString avTrue = "true"; + +// Namespaces and prefixes +const QString svgRequiredExtensionPrefix = "http://www.imsglobal.org/iwb/"; +const QString dcNS = "http://purl.org/dc/elements/1.1/"; +const QString ubNS = "http://uniboard.mnemis.com/document"; +const QString svgUBZNS = "http://www.imsglobal.org/xsd/iwb_v1p0"; +const QString svgIWBNS = "http://www.w3.org/2000/svg"; +const QString xlinkNS = "http://www.w3.org/1999/xlink"; +const QString iwbNS = "http://www.becta.org.uk/iwb"; +const QString xsiNS = "http://www.w3.org/2001/XMLSchema-instance"; +const QString xsiShemaLocation = "\ +http://www.imsglobal.org/xsd/iwb_v1p0 \ +http://www.imsglobal.org/profile/iwb/iwbv1p0_v1p0.xsd \ +http://www.w3.org/2000/svg http://www.imsglobal.org/profile/iwb/svgsubsetv1p0_v1p0.xsd \ +http://www.w3.org/1999/xlink http://www.imsglobal.org/xsd/w3/1999/xlink.xsd"; +const QString dcNSPrefix = "dc"; +const QString ubNSPrefix = "ub"; +const QString svgIWBNSPrefix = "svg"; +const QString xlinkNSPrefix = "xlink"; +const QString iwbNsPrefix = "iwb"; +const QString xsiPrefix = "xsi"; +const QString xsiSchemaLocationPrefix = "schemaLocation"; + +const QString avOwner = ""; +const QString avCreator = ""; +const QString avDescription = ""; + +//constant symbols and words etc +const QString dimensionsDelimiter1 = "x"; +const QString dimensionsDelimiter2 = " "; +const QString pageAlias = "page"; +const QString pageFileExtentionUBZ = "svg"; + +//content folder names +const QString cfImages = "images"; +const QString cfVideos = "video"; +const QString cfAudios = "audio"; +const QString cfFlash = "flash"; + +//known file extentions +const QString feSvg = "svg"; +const QString feWgt = "wgt"; +const QString fePng = "png"; + +const int iCrossSize = 32; +const int iCrossWidth = 1; + +// Image formats supported by CFF exclude wgt. Wgt is Sankore widget, which is considered as a .png preview. +const QString iwbElementImage(" \ +wgt, \ +jpeg, \ +jpg, \ +bmp, \ +gif, \ +wmf, \ +emf, \ +png, \ +tif, \ +tiff \ +"); + +// Video formats supported by CFF +const QString iwbElementVideo(" \ +mpg, \ +mpeg, \ +swf, \ +"); + +// Audio formats supported by CFF +const QString iwbElementAudio(" \ +mp3, \ +wav \ +"); + +const QString cffSupportedFileFormats(iwbElementImage + iwbElementVideo + iwbElementAudio); +const QString ubzFormatsToConvert("svg"); + + +const QString iwbSVGImageAttributes(" \ +id, \ +xlink:href, \ +x, \ +y, \ +height, \ +width, \ +fill-opacity, \ +requiredExtentions, \ +transform \ +"); + + +const QString iwbSVGAudioAttributes(" \ +id, \ +xlink:href, \ +x, \ +y, \ +height, \ +width, \ +fill-opacity, \ +requiredExtentions, \ +transform \ +"); + +const QString iwbSVGVideoAttributes(" \ +id, \ +xlink:href, \ +x, \ +y, \ +height, \ +width, \ +fill-opacity, \ +requiredExtentions, \ +transform \ +"); + +const QString iwbSVGRectAttributes(" \ +id, \ +x, \ +y, \ +height, \ +width, \ +fill, \ +fill-opacity, \ +stroke, \ +stroke-dasharray, \ +stroke-linecap, \ +stroke-linejoin, \ +stroke-opacity, \ +stroke-width, \ +transform \ +"); + + + +const QString iwbSVGTextAttributes(" \ +id, \ +x, \ +y, \ +fill, \ +font-family, \ +font-size, \ +font-style, \ +font-weight, \ +font-stretch, \ +transform \ +"); + +const QString iwbSVGTextAreaAttributes(" \ +id, \ +x, \ +y, \ +height, \ +width, \ +fill, \ +font-family, \ +font-size, \ +font-style, \ +font-weight, \ +font-stretch, \ +text-align, \ +transform \ +"); + +const QString iwbSVGTspanAttributes(" \ +id, \ +fill, \ +font-family, \ +font-size, \ +font-style, \ +font-weight, \ +font-stretch, \ +text-align, \ +"); + +const QString iwbSVGLineAttributes(" \ +id, \ +x1, \ +y1, \ +x2, \ +y2, \ +stroke, \ +stroke-dasharray, \ +stroke-width, \ +stroke-opacity, \ +stroke-linecap, \ +transform \ +"); + +const QString iwbSVGPolyLineAttributes(" \ +id, \ +points, \ +stroke, \ +stroke-width, \ +stroke-dasharray, \ +stroke-opacity, \ +stroke-linecap, \ +transform \ +"); + +const QString iwbSVGPolygonAttributes(" \ +id, \ +points, \ +fill, \ +fill-opacity, \ +stroke, \ +stroke-dasharray, \ +stroke-width, \ +stroke-linecap, \ +stroke-linejoin, \ +stroke-opacity, \ +stroke-width, \ +transform \ +"); + +// 1 to 1 copy to SVG section +const QString iwbElementAttributes(" \ +background, \ +background-fill, \ +background-posture, \ +flip, \ +freehand, \ +highlight, \ +highlight-fill, \ +list-style-type, \ +list-style-type-fill, \ +locked, \ +replicate, \ +revealer, \ +stroke-lineshape-start, \ +stroke-lineshape-end \ +"); + +// cannot be copied 1 to 1 to SVG section +const QString ubzElementAttributesToConvert(" \ +xlink:href, \ +src, \ +transform \ +"); + +// additional attributes. Have references in SVG section. +const QString svgElementAttributes(" \ +points, \ +fill, \ +fill-opacity, \ +stroke, \ +stroke-dasharray, \ +stroke-linecap, \ +stroke-opacity, \ +stroke-width, \ +stroke_linejoin, \ +requiredExtensions, \ +viewbox, \ +x, \ +y, \ +x1, \ +y1, \ +x2, \ +y2, \ +height, \ +width, \ +font-family, \ +font-size, \ +font-style, \ +font-weight, \ +font-stretch, \ +text-align \ +"); + +const QString ubzContentFolders("audios,videos,images,widgets"); + +struct UBItemLayerType +{ + enum Enum + { + FixedBackground = -2000, Object = -1000, Graphic = 0, Tool = 1000, Control = 2000 + }; +}; + +#endif // UBCFFCONSTANTS_H \ No newline at end of file diff --git a/plugins/cffadaptor/src/UBGlobals.h b/plugins/cffadaptor/src/UBGlobals.h new file mode 100644 index 00000000..adfc677d --- /dev/null +++ b/plugins/cffadaptor/src/UBGlobals.h @@ -0,0 +1,47 @@ +#ifndef UBGLOBALS_H +#define UBGLOBALS_H + +#define DELETEPTR(ptr) if(NULL != ptr){ \ + delete ptr; \ + ptr = NULL; \ + } + +#ifdef Q_WS_WIN + +#define WARNINGS_DISABLE __pragma(warning(push, 0)); +#define WARNINGS_ENABLE __pragma(warning(pop)); + +#ifdef NO_THIRD_PARTY_WARNINGS +// disabling warning level to 0 and save old state +#define THIRD_PARTY_WARNINGS_DISABLE WARNINGS_DISABLE +#else +// just save old state (needs for not empty define) +#define THIRD_PARTY_WARNINGS_DISABLE __pragma(warning(push)); +#endif //#ifdef NO_THIRD_PARTY_WARNINGS +// anyway on WIN +#define THIRD_PARTY_WARNINGS_ENABLE WARNINGS_ENABLE + +#else //#ifdef Q_WS_WIN + +#define WARNINGS_DISABLE _Pragma("GCC diagnostic push"); \ +_Pragma("GCC diagnostic ignored \"-Wunused-parameter\""); \ +_Pragma("GCC diagnostic ignored \"-Wunused-variable\""); \ +_Pragma("GCC diagnostic ignored \"-Wsign-compare\""); + +#define WARNINGS_ENABLE _Pragma("GCC diagnostic pop"); + +#ifdef NO_THIRD_PARTY_WARNINGS +//disabling some warnings +#define THIRD_PARTY_WARNINGS_DISABLE WARNINGS_DISABLE + +#define THIRD_PARTY_WARNINGS_ENABLE WARNINGS_ENABLE +#else +// just save old state (needs for not empty define) +#define THIRD_PARTY_WARNINGS_ENABLE WARNINGS_ENABLE + +#endif //#ifdef NO_THIRD_PARTY_WARNINGS + +#endif //#ifdef Q_WS_WIN + +#endif // UBGLOBALS_H + diff --git a/src/adaptors/UBExportCFF.cpp b/src/adaptors/UBExportCFF.cpp new file mode 100644 index 00000000..741eb871 --- /dev/null +++ b/src/adaptors/UBExportCFF.cpp @@ -0,0 +1,60 @@ +#include "UBExportCFF.h" +#include "UBCFFAdaptor.h" +#include "document/UBDocumentProxy.h" +#include "core/UBDocumentManager.h" +#include "core/UBApplication.h" + + +UBExportCFF::UBExportCFF(QObject *parent) +: UBExportAdaptor(parent) +{ + +} + +UBExportCFF::~UBExportCFF() +{ + +} +QString UBExportCFF::exportName() +{ + return tr("Export to IWB"); +} + +QString UBExportCFF::exportExtention() +{ + return QString(".iwb"); +} + +void UBExportCFF::persist(UBDocumentProxy* pDocument) +{ + QString src = pDocument->persistencePath(); + + if (!pDocument) + return; + + QString filename = askForFileName(pDocument, tr("Export as IWB File")); + + if (filename.length() > 0) + { + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + if (mIsVerbose) + UBApplication::showMessage(tr("Exporting document...")); + + UBCFFAdaptor toIWBExporter; + if (toIWBExporter.convertUBZToIWB(src, filename)) + { + if (mIsVerbose) + UBApplication::showMessage(tr("Export successful.")); + } + else + if (mIsVerbose) + UBApplication::showMessage(tr("Export failed.")); + + + QApplication::restoreOverrideCursor(); + + } + + +} \ No newline at end of file diff --git a/src/adaptors/UBExportCFF.h b/src/adaptors/UBExportCFF.h new file mode 100644 index 00000000..6c7012d6 --- /dev/null +++ b/src/adaptors/UBExportCFF.h @@ -0,0 +1,39 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef UBExportCFF_H_ +#define UBExportCFF_H_ + +#include + +#include "UBExportAdaptor.h" + +#include "frameworks/UBFileSystemUtils.h" + +class UBDocumentProxy; + +class UBExportCFF : public UBExportAdaptor +{ + Q_OBJECT; + +public: + UBExportCFF(QObject *parent = 0); + virtual ~UBExportCFF(); + + virtual QString exportName(); + virtual QString exportExtention(); + virtual void persist(UBDocumentProxy* pDocument); +}; + +#endif /* UBExportCFF_H_ */ \ No newline at end of file diff --git a/src/adaptors/adaptors.pri b/src/adaptors/adaptors.pri index b69d434f..4fc92ce9 100644 --- a/src/adaptors/adaptors.pri +++ b/src/adaptors/adaptors.pri @@ -14,6 +14,7 @@ HEADERS += src/adaptors/UBExportAdaptor.h\ src/adaptors/UBExportWeb.h \ src/adaptors/UBWebPublisher.h \ src/adaptors/UBImportCFF.h \ + src/adaptors/UBExportCFF.h \ src/adaptors/UBCFFSubsetAdaptor.h HEADERS += src/adaptors/publishing/UBDocumentPublisher.h \ @@ -36,6 +37,7 @@ SOURCES += src/adaptors/UBExportAdaptor.cpp\ src/adaptors/UBExportWeb.cpp \ src/adaptors/UBWebPublisher.cpp \ src/adaptors/UBImportCFF.cpp \ + src/adaptors/UBExportCFF.cpp \ src/adaptors/UBCFFSubsetAdaptor.cpp \ src/adaptors/publishing/UBDocumentPublisher.cpp diff --git a/src/core/UBDocumentManager.cpp b/src/core/UBDocumentManager.cpp index 48dd0352..fbe315e2 100644 --- a/src/core/UBDocumentManager.cpp +++ b/src/core/UBDocumentManager.cpp @@ -21,6 +21,7 @@ #include "adaptors/UBExportFullPDF.h" #include "adaptors/UBExportDocument.h" #include "adaptors/UBExportWeb.h" +#include "adaptors/UBExportCFF.h" #include "adaptors/UBWebPublisher.h" #include "adaptors/UBImportDocument.h" #include "adaptors/UBImportPDF.h" @@ -62,6 +63,8 @@ UBDocumentManager::UBDocumentManager(QObject *parent) QString dummyObjects = tr("objects"); QString dummyWidgets = tr("widgets"); + UBExportCFF* cffExporter = new UBExportCFF(this); + mExportAdaptors.append(cffExporter); UBExportFullPDF* exportFullPdf = new UBExportFullPDF(this); mExportAdaptors.append(exportFullPdf); UBExportDocument* exportDocument = new UBExportDocument(this);