diff --git a/utilities/assistants/htmlgallery/generator/gallerygenerator.cpp b/utilities/assistants/htmlgallery/generator/gallerygenerator.cpp index 6934e3d9c2..e901d026dd 100644 --- a/utilities/assistants/htmlgallery/generator/gallerygenerator.cpp +++ b/utilities/assistants/htmlgallery/generator/gallerygenerator.cpp @@ -1,650 +1,635 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-04-04 * Description : a tool to generate HTML image galleries * * Copyright (C) 2006-2010 by Aurelien Gateau * Copyright (C) 2012-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #include "gallerygenerator.h" // Qt includes #include -#include -#include #include #include #include #include #include #include #include #include // KDE includes #include // libxslt includes #include #include #include #include // Local includes #include "digikam_debug.h" #include "abstractthemeparameter.h" #include "galleryelement.h" #include "galleryelementfunctor.h" #include "galleryinfo.h" #include "gallerytheme.h" #include "galleryxmlutils.h" #include "htmlwizard.h" #include "dprogresswdg.h" #include "dhistoryview.h" #include "dfileoperations.h" namespace Digikam { typedef QMap XsltParameterMap; class GalleryGenerator::Private { public: // Url => local temp path typedef QHash RemoteUrlHash; public: explicit Private() : that(0), info(0), warnings(false), cancel(false), pview(0), pbar(0) { } GalleryGenerator* that; GalleryInfo* info; GalleryTheme::Ptr theme; // State info bool warnings; QString xmlFileName; bool cancel; DHistoryView* pview; DProgressWdg* pbar; public: bool init() { cancel = false; theme = GalleryTheme::findByInternalName(info->theme()); if (!theme) { logError( i18n("Could not find theme in '%1'", info->theme()) ); return false; } pview->setVisible(true); pbar->setVisible(true); return true; } bool createDir(const QString& dirName) { logInfo(i18n("Create directories")); if (!QDir().mkpath(dirName)) { logError(i18n("Could not create folder '%1'", QDir::toNativeSeparators(dirName))); return false; } return true; } bool copyTheme() { logInfo(i18n("Copying theme")); QUrl srcUrl = QUrl::fromLocalFile(theme->directory()); QUrl destUrl = info->destUrl().adjusted(QUrl::StripTrailingSlash); QDir themeDir(destUrl.toLocalFile() + QLatin1Char('/') + srcUrl.fileName()); if (themeDir.exists()) { themeDir.removeRecursively(); } bool ok = DFileOperations::copyFolderRecursively(srcUrl.toLocalFile(), destUrl.toLocalFile()); if (!ok) { logError(i18n("Could not copy theme")); return false; } return true; } bool generateImagesAndXML() { logInfo(i18n("Generate images and XML files")); QString baseDestDir = info->destUrl().toLocalFile(); if (!createDir(baseDestDir)) return false; xmlFileName = baseDestDir + QLatin1String("/gallery.xml"); XMLWriter xmlWriter; if (!xmlWriter.open(xmlFileName)) { logError(i18n("Could not create gallery.xml")); return false; } XMLElement collectionsX(xmlWriter, QLatin1String("collections")); if (info->m_getOption == GalleryInfo::ALBUMS) { // Loop over albums selection DInfoInterface::DAlbumIDs::ConstIterator albumIt = info->m_albumList.constBegin(); DInfoInterface::DAlbumIDs::ConstIterator albumEnd = info->m_albumList.constEnd(); for (; albumIt != albumEnd ; ++albumIt) { int id = *albumIt; DInfoInterface::DInfoMap inf; if (info->m_iface) inf = info->m_iface->albumInfo(id); DAlbumInfo anf(inf); QString title = anf.title(); QString collectionFileName = webifyFileName(title); QString destDir = baseDestDir + QLatin1Char('/') + collectionFileName; if (!createDir(destDir)) { return false; } XMLElement collectionX(xmlWriter, QLatin1String("collection")); xmlWriter.writeElement("name", title); xmlWriter.writeElement("fileName", collectionFileName); xmlWriter.writeElement("comment", anf.caption()); // Gather image element list QList imageList; if (info->m_iface) { imageList = info->m_iface->albumsItems(DInfoInterface::DAlbumIDs() << id); } if (!processImages(xmlWriter, imageList, title, destDir)) return false; } } else { QString title = info->imageSelectionTitle(); QString collectionFileName = webifyFileName(title); QString destDir = baseDestDir + QLatin1Char('/') + collectionFileName; if (!createDir(destDir)) { return false; } XMLElement collectionX(xmlWriter, QLatin1String("collection")); xmlWriter.writeElement("name", title); xmlWriter.writeElement("fileName", collectionFileName); if (!processImages(xmlWriter, info->m_imageList, title, destDir)) return false; } return true; } bool processImages(XMLWriter& xmlWriter, const QList& imageList, const QString& title, const QString& destDir) { RemoteUrlHash remoteUrlHash; if (!downloadRemoteUrls(title, imageList, &remoteUrlHash)) { return false; } QList imageElementList; Q_FOREACH(const QUrl& url, imageList) { const QString path = remoteUrlHash.value(url, url.toLocalFile()); if (path.isEmpty()) { continue; } DInfoInterface::DInfoMap inf; if (info->m_iface) inf = info->m_iface->itemInfo(url); GalleryElement element = GalleryElement(inf); element.m_path = remoteUrlHash.value(url, url.toLocalFile()); imageElementList << element; } // Generate images logInfo(i18n("Generating files for \"%1\"", title)); GalleryElementFunctor functor(that, info, destDir); QFuture future = QtConcurrent::map(imageElementList, functor); QFutureWatcher watcher; watcher.setFuture(future); connect(&watcher, SIGNAL(progressValueChanged(int)), pbar, SLOT(setValue(int))); pbar->setMaximum(imageElementList.count()); while (!future.isFinished()) { qApp->processEvents(); if (cancel) { future.cancel(); future.waitForFinished(); return false; } } // Generate xml Q_FOREACH(const GalleryElement& element, imageElementList) { element.appendToXML(xmlWriter, info->copyOriginalImage()); } return true; } bool generateHTML() { logInfo(i18n("Generating HTML files")); QString xsltFileName = theme->directory() + QLatin1String("/template.xsl"); CWrapper xslt = xsltParseStylesheetFile((const xmlChar*) - QDir::toNativeSeparators(xsltFileName).toLocal8Bit().data()); + QDir::toNativeSeparators(xsltFileName).toUtf8().data()); if (!xslt) { logError(i18n("Could not load XSL file '%1'", xsltFileName)); return false; } CWrapper xmlGallery = - xmlParseFile(QDir::toNativeSeparators(xmlFileName).toLocal8Bit().data() ); + xmlParseFile(QDir::toNativeSeparators(xmlFileName).toUtf8().data() ); if (!xmlGallery) { logError(i18n("Could not load XML file '%1'", xmlFileName)); return false; } // Prepare parameters XsltParameterMap map; addI18nParameters(map); addThemeParameters(map); const char** params = new const char*[map.size()*2+1]; XsltParameterMap::Iterator it = map.begin(); XsltParameterMap::Iterator end = map.end(); const char** ptr = params; for ( ; it != end ; ++it) { *ptr = it.key().data(); ++ptr; *ptr = it.value().data(); ++ptr; } *ptr = 0; // Move to the destination dir, so that external documents get correctly // produced QString oldCD = QDir::currentPath(); QDir::setCurrent(info->destUrl().toLocalFile()); CWrapper xmlOutput = xsltApplyStylesheet(xslt, xmlGallery, params); QDir::setCurrent(oldCD); //delete []params; if (!xmlOutput) { logError(i18n("Error processing XML file")); return false; } QString destFileName = QDir::toNativeSeparators(info->destUrl().toLocalFile() + QLatin1String("/index.html")); -#ifdef Q_CC_MSVC - if (xsltSaveResultToFilename(destFileName.toLocal8Bit().data(), xmlOutput, xslt, 0) == -1) + if (xsltSaveResultToFilename(destFileName.toUtf8().data(), xmlOutput, xslt, 0) == -1) { logError(i18n("Could not open '%1' for writing", destFileName)); return false; } -#else - FILE* const file = fopen(destFileName.toLocal8Bit().data(), "w"); - - if (!file) - { - logError(i18n("Could not open '%1' for writing", destFileName)); - return false; - } - - xsltSaveResultToFile(file, xmlOutput, xslt); - fclose(file); -#endif return true; } bool downloadRemoteUrls(const QString& collectionName, const QList& _list, RemoteUrlHash* const hash) { Q_ASSERT(hash); QList list; Q_FOREACH(const QUrl& url, _list) { if (!url.isLocalFile()) { list << url; } } if (list.count() == 0) { return true; } logInfo(i18n("Downloading remote files for \"%1\"", collectionName)); pbar->setMaximum(list.count()); int count = 0; Q_FOREACH(const QUrl& url, list) { if (cancel) { return false; } QTemporaryFile tempFile; tempFile.setFileTemplate(QLatin1String("htmlexport-")); if (!tempFile.open()) { logError(i18n("Could not open temporary file")); return false; } QTemporaryFile tempPath; tempPath.setFileTemplate(tempFile.fileName()); tempPath.setAutoRemove(false); if (tempPath.open() && DFileOperations::copyFiles(QStringList() << url.toLocalFile(), tempPath.fileName())) { hash->insert(url, tempFile.fileName()); } else { logWarning(i18n("Could not download %1", url.toDisplayString())); hash->insert(url, QString()); } tempPath.close(); tempFile.close(); ++count; pbar->setValue(count); } return true; } /** * Add to map all the i18n parameters. */ void addI18nParameters(XsltParameterMap& map) { map["i18nPrevious"] = makeXsltParam(i18n("Previous")); map["i18nNext"] = makeXsltParam(i18n("Next")); map["i18nCollectionList"] = makeXsltParam(i18n("Album List")); map["i18nOriginalImage"] = makeXsltParam(i18n("Original Image")); map["i18nUp"] = makeXsltParam(i18n("Go Up")); // Exif Tag map["i18nexifimagemake"] = makeXsltParam(i18n("Make")); map["i18nexifimagemodel"] = makeXsltParam(i18n("Model")); map["i18nexifimageorientation"] = makeXsltParam(i18n("Image Orientation")); map["i18nexifimagexresolution"] = makeXsltParam(i18n("Image X Resolution")); map["i18nexifimageyresolution"] = makeXsltParam(i18n("Image Y Resolution")); map["i18nexifimageresolutionunit"] = makeXsltParam(i18n("Image Resolution Unit")); map["i18nexifimagedatetime"] = makeXsltParam(i18n("Image Date Time")); map["i18nexifimageycbcrpositioning"] = makeXsltParam(i18n("YCBCR Positioning")); map["i18nexifphotoexposuretime"] = makeXsltParam(i18n("Exposure Time")); map["i18nexifphotofnumber"] = makeXsltParam(i18n("F Number")); map["i18nexifphotoexposureprogram"] = makeXsltParam(i18n("Exposure Index")); map["i18nexifphotoisospeedratings"] = makeXsltParam(i18n("ISO Speed Ratings")); map["i18nexifphotoshutterspeedvalue"] = makeXsltParam(i18n("Shutter Speed Value")); map["i18nexifphotoaperturevalue"] = makeXsltParam(i18n("Aperture Value")); map["i18nexifphotofocallength"] = makeXsltParam(i18n("Focal Length")); map["i18nexifgpsaltitude"] = makeXsltParam(i18n("GPS Altitude")); map["i18nexifgpslatitude"] = makeXsltParam(i18n("GPS Latitude")); map["i18nexifgpslongitude"] = makeXsltParam(i18n("GPS Longitude")); } /** * Add to map all the theme parameters, as specified by the user. */ void addThemeParameters(XsltParameterMap& map) { GalleryTheme::ParameterList parameterList = theme->parameterList(); QString themeInternalName = theme->internalName(); GalleryTheme::ParameterList::ConstIterator it = parameterList.constBegin(); GalleryTheme::ParameterList::ConstIterator end = parameterList.constEnd(); for (; it != end ; ++it) { AbstractThemeParameter* const themeParameter = *it; QByteArray internalName = themeParameter->internalName(); QString value = info->getThemeParameterValue(themeInternalName, QString::fromLatin1(internalName), themeParameter->defaultValue()); map[internalName] = makeXsltParam(value); } } /** * Prepare an XSLT param, managing quote mess. * abc => 'abc' * a"bc => 'a"bc' * a'bc => "a'bc" * a"'bc => concat('a"', "'", 'bc') */ QByteArray makeXsltParam(const QString& txt) { QString param; static const char apos = '\''; static const char quote = '"'; if (txt.indexOf(QLatin1Char(apos)) == -1) { // First or second case: no apos param = QLatin1Char(apos) + txt + QLatin1Char(apos); } else if (txt.indexOf(QLatin1Char(quote)) == -1) { // Third case: only apos, no quote param = QLatin1Char(quote) + txt + QLatin1Char(quote); } else { // Forth case: both apos and quote :-( const QStringList lst = txt.split(QLatin1Char(apos), QString::KeepEmptyParts); QStringList::ConstIterator it = lst.constBegin(); QStringList::ConstIterator end = lst.constEnd(); param = QLatin1String("concat("); param += QLatin1Char(apos) + *it + QLatin1Char(apos); ++it; for (; it != end ; ++it) { param += QLatin1String(", \"'\", "); param += QLatin1Char(apos) + *it + QLatin1Char(apos); } param += QLatin1Char(')'); } //qCDebug(DIGIKAM_GENERAL_LOG) << "param: " << txt << " => " << param; return param.toUtf8(); } void logInfo(const QString& msg) { pview->addEntry(msg, DHistoryView::ProgressEntry); } void logError(const QString& msg) { pview->addEntry(msg, DHistoryView::ErrorEntry); } void logWarning(const QString& msg) { pview->addEntry(msg, DHistoryView::WarningEntry); warnings = true; } }; // ---------------------------------------------------------------------- GalleryGenerator::GalleryGenerator(GalleryInfo* const info) : QObject(), d(new Private) { d->that = this; d->info = info; d->warnings = false; connect(this, SIGNAL(logWarningRequested(QString)), SLOT(logWarning(QString)), Qt::QueuedConnection); } GalleryGenerator::~GalleryGenerator() { delete d; } bool GalleryGenerator::run() { if (!d->init()) return false; QString destDir = d->info->destUrl().toLocalFile(); qCDebug(DIGIKAM_GENERAL_LOG) << destDir; if (!d->createDir(destDir)) return false; if (!d->copyTheme()) return false; if (!d->generateImagesAndXML()) return false; exsltRegisterAll(); bool result = d->generateHTML(); xsltCleanupGlobals(); xmlCleanupParser(); return result; } bool GalleryGenerator::warnings() const { return d->warnings; } void GalleryGenerator::logWarning(const QString& text) { d->logWarning(text); } void GalleryGenerator::slotCancel() { d->cancel = true; } void GalleryGenerator::setProgressWidgets(DHistoryView* const pView, DProgressWdg* const pBar) { d->pview = pView; d->pbar = pBar; connect(d->pbar, SIGNAL(signalProgressCanceled()), this, SLOT(slotCancel())); } QString GalleryGenerator::webifyFileName(const QString& fname) { QString fileName = fname.toLower(); // Remove potentially troublesome chars return fileName.replace(QRegExp(QLatin1String("[^-0-9a-z]+")), QLatin1String("_")); } } // namespace Digikam diff --git a/utilities/assistants/htmlgallery/generator/galleryxmlutils.cpp b/utilities/assistants/htmlgallery/generator/galleryxmlutils.cpp index ac141fb973..d797d30068 100644 --- a/utilities/assistants/htmlgallery/generator/galleryxmlutils.cpp +++ b/utilities/assistants/htmlgallery/generator/galleryxmlutils.cpp @@ -1,111 +1,111 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-04-04 * Description : a tool to generate HTML image galleries * * Copyright (C) 2006-2010 by Aurelien Gateau * Copyright (C) 2012-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #include "galleryxmlutils.h" namespace Digikam { bool XMLWriter::open(const QString& name) { - xmlTextWriterPtr ptr = xmlNewTextWriterFilename(name.toLocal8Bit().constData(), 0); + xmlTextWriterPtr ptr = xmlNewTextWriterFilename(name.toUtf8().constData(), 0); if (!ptr) { return false; } m_writer.assign(ptr); int rc = xmlTextWriterStartDocument(ptr, NULL, "UTF-8", NULL); if (rc < 0) { m_writer.assign(0); return false; } xmlTextWriterSetIndent(ptr, 1); return true; } XMLWriter::operator xmlTextWriterPtr() const { return m_writer; } void XMLWriter::writeElement(const char* element, const QString& value) { xmlTextWriterWriteElement(m_writer, BAD_CAST element, BAD_CAST value.toUtf8().data()); } void XMLWriter::writeElement(const char* element, int value) { writeElement(element, QString::number(value)); } // ------------------------------------------------------ void XMLAttributeList::write(XMLWriter& writer) const { Map::const_iterator it = m_map.begin(); Map::const_iterator end = m_map.end(); for (; it != end ; ++it) { xmlTextWriterWriteAttribute(writer, BAD_CAST it.key().toLatin1().data(), BAD_CAST it.value().toLatin1().data()); } } void XMLAttributeList::append(const QString& key, const QString& value) { m_map[key] = value; } void XMLAttributeList::append(const QString& key, int value) { m_map[key] = QString::number(value); } // ------------------------------------------------------ XMLElement::XMLElement(XMLWriter& writer, const QString& element, const XMLAttributeList* attributeList) : m_writer(writer) { xmlTextWriterStartElement(writer, BAD_CAST element.toLatin1().data()); if (attributeList) { attributeList->write(writer); } } XMLElement::~XMLElement() { xmlTextWriterEndElement(m_writer); } } // namespace Digikam