Remove CHM generator; disabled for 4 months

This commit is contained in:
Sune Vuorela 2024-04-15 16:41:51 +02:00 committed by Albert Astals Cid
parent 4435325e5a
commit 460cf6123e
40 changed files with 0 additions and 5958 deletions

View file

@ -21,10 +21,6 @@ endif(LIBSPECTRE_FOUND)
add_subdirectory( kimgio )
if(FALSE)
add_subdirectory( chm )
endif()
if(DJVULIBRE_FOUND)
add_subdirectory(djvu)
endif(DJVULIBRE_FOUND)

View file

@ -1,38 +0,0 @@
remove_definitions(-DTRANSLATION_DOMAIN="okular")
add_definitions(-DTRANSLATION_DOMAIN="okular_chm")
add_subdirectory( kio-msits )
########### next target ###############
set(okularGenerator_chmlib_SRCS
lib/ebook_chm.cpp
lib/ebook_epub.cpp
lib/ebook.cpp
lib/ebook_chm_encoding.cpp
lib/ebook_search.cpp
lib/helper_entitydecoder.cpp
lib/helper_search_index.cpp
lib/helperxmlhandler_epubcontainer.cpp
lib/helperxmlhandler_epubcontent.cpp
lib/helperxmlhandler_epubtoc.cpp
generator_chm.cpp
)
okular_add_generator(okularGenerator_chmlib ${okularGenerator_chmlib_SRCS})
target_include_directories(okularGenerator_chmlib PRIVATE ${CHM_INCLUDE_DIR} ${LIBZIP_INCLUDE_DIR})
target_link_libraries(okularGenerator_chmlib okularcore ${CHM_LIBRARY} ${LIBZIP_LIBRARY} KF6::KHtml)
########### autotests ###############
add_definitions( -DKDESRCDIR="${CMAKE_CURRENT_SOURCE_DIR}/" )
ecm_add_test(autotests/chmgeneratortest.cpp
TEST_NAME "chmgeneratortest"
LINK_LIBRARIES Qt6::Test KF6::CoreAddons okularcore
)
target_compile_definitions(chmgeneratortest PRIVATE -DGENERATOR_PATH="$<TARGET_FILE:okularGenerator_chmlib>")
########### install files ###############
install( PROGRAMS okularApplication_chm.desktop org.kde.mobile.okular_chm.desktop DESTINATION ${KDE_INSTALL_APPDIR} )
install( FILES org.kde.okular-chm.metainfo.xml DESTINATION ${KDE_INSTALL_METAINFODIR} )

View file

@ -1,2 +0,0 @@
#!/bin/sh
$XGETTEXT $(find . -name "*.cpp" -o -name "*.h") -o $podir/okular_chm.pot

View file

@ -1,99 +0,0 @@
/*
SPDX-FileCopyrightText: 2017 Gilbert Assaf <gassaf@gmx.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QMimeDatabase>
#include <QTest>
#include "core/document.h"
#include "core/page.h"
#include "core/textpage.h"
#include "settings_core.h"
class ChmGeneratorTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testDocumentStructure();
void testDocumentContent();
void cleanupTestCase();
private:
Okular::Document *m_document;
};
void ChmGeneratorTest::initTestCase()
{
Okular::SettingsCore::instance(QStringLiteral("ChmGeneratorTest"));
m_document = new Okular::Document(nullptr);
const QString testFile = QStringLiteral(KDESRCDIR "autotests/data/test.chm");
QMimeDatabase db;
const QMimeType mime = db.mimeTypeForFile(testFile);
QCOMPARE(m_document->openDocument(testFile, QUrl(), mime), Okular::Document::OpenSuccess);
}
void ChmGeneratorTest::cleanupTestCase()
{
m_document->closeDocument();
delete m_document;
}
void ChmGeneratorTest::testDocumentStructure()
{
unsigned int expectedPageNr = 6;
QCOMPARE(m_document->pages(), expectedPageNr);
QCOMPARE(m_document->metaData(QStringLiteral("DocumentTitle")).toString(), QStringLiteral("okular test chm"));
const Okular::DocumentSynopsis *docSyn = m_document->documentSynopsis();
QDomElement heading1 = docSyn->documentElement();
QCOMPARE(heading1.tagName(), QStringLiteral("Heading 1"));
QDomElement topic1 = heading1.firstChildElement();
QCOMPARE(topic1.tagName(), QStringLiteral("Topic 1"));
QDomElement heading1_1 = topic1.nextSiblingElement();
QCOMPARE(heading1_1.tagName(), QStringLiteral("Heading 1.1"));
QDomElement topic1_1 = heading1_1.firstChildElement();
QCOMPARE(topic1_1.tagName(), QStringLiteral("Topic 1.1"));
QDomElement heading2 = heading1.nextSiblingElement();
QCOMPARE(heading2.tagName(), QStringLiteral("Heading 2"));
}
void ChmGeneratorTest::testDocumentContent()
{
const Okular::Page *page0 = m_document->page(0);
QCOMPARE(page0->number(), 0);
m_document->requestTextPage(page0->number());
QVERIFY(page0->hasTextPage());
QCOMPARE(page0->text(), QStringLiteral("Heading 1This is an example Text."));
const Okular::Page *page1 = m_document->page(1);
QCOMPARE(page1->number(), 1);
m_document->requestTextPage(page1->number());
QVERIFY(page1->hasTextPage());
QCOMPARE(page1->text(), QStringLiteral("Topic 1This is an example Text."));
const Okular::Page *page2 = m_document->page(2);
QCOMPARE(page2->number(), 2);
m_document->requestTextPage(page2->number());
QVERIFY(page2->hasTextPage());
QCOMPARE(page2->text(), QStringLiteral("Heading 1.1With html title."));
// Test page, who doesn't have an TOC entry, but one in chm index
const Okular::Page *indexPage1 = m_document->page(5);
QCOMPARE(indexPage1->number(), 5);
m_document->requestTextPage(indexPage1->number());
QVERIFY(indexPage1->hasTextPage());
QCOMPARE(indexPage1->text(), QStringLiteral("Index 1This is an example Text."));
}
QTEST_MAIN(ChmGeneratorTest)
#include "chmgeneratortest.moc"
/* kate: replace-tabs on; tab-width 4; */

View file

@ -1,408 +0,0 @@
/*
SPDX-FileCopyrightText: 2005 Piotr Szymański <niedakh@gmail.com>
SPDX-FileCopyrightText: 2008 Albert Astals Cid <aacid@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "generator_chm.h"
#include <QDomElement>
#include <QEventLoop>
#include <QMutex>
#include <QPainter>
#include <KAboutData>
#include <KHTMLView>
#include <KLocalizedString>
#include <QUrl>
#include <dom/dom_html.h>
#include <dom/dom_node.h>
#include <dom/html_misc.h>
#include <khtml_part.h>
#include <core/action.h>
#include <core/page.h>
#include <core/textpage.h>
#include <core/utils.h>
OKULAR_EXPORT_PLUGIN(CHMGenerator, "libokularGenerator_chmlib.json")
static QString absolutePath(const QString &baseUrl, const QString &path)
{
QString absPath;
if (path.startsWith(QLatin1Char('/'))) {
// already absolute
absPath = path;
} else {
QUrl url = QUrl::fromLocalFile(baseUrl).adjusted(QUrl::RemoveFilename);
url.setPath(url.path() + path);
absPath = url.toLocalFile();
}
return absPath;
}
CHMGenerator::CHMGenerator(QObject *parent, const QVariantList &args)
: Okular::Generator(parent, args)
{
setFeature(TextExtraction);
m_syncGen = nullptr;
m_file = nullptr;
m_request = nullptr;
}
CHMGenerator::~CHMGenerator()
{
delete m_syncGen;
}
bool CHMGenerator::loadDocument(const QString &fileName, QVector<Okular::Page *> &pagesVector)
{
m_file = EBook::loadFile(fileName);
if (!m_file) {
return false;
}
m_fileName = fileName;
QList<EBookTocEntry> topics;
m_file->getTableOfContents(topics);
// fill m_docSyn
QMap<int, QDomElement> lastIndentElement;
QMap<QString, int> tmpPageList;
int pageNum = 0;
for (const EBookTocEntry &e : std::as_const(topics)) {
QDomElement item = m_docSyn.createElement(e.name);
if (!e.url.isEmpty()) {
QString url = e.url.toString();
item.setAttribute(QStringLiteral("ViewportName"), url);
if (!tmpPageList.contains(url)) { // add a page only once
tmpPageList.insert(url, pageNum);
pageNum++;
}
}
item.setAttribute(QStringLiteral("Icon"), e.iconid);
if (e.indent == 0) {
m_docSyn.appendChild(item);
} else {
lastIndentElement[e.indent - 1].appendChild(item);
}
lastIndentElement[e.indent] = item;
}
// fill m_urlPage and m_pageUrl
QList<QUrl> pageList;
m_file->enumerateFiles(pageList);
const QUrl home = m_file->homeUrl();
if (home.path() != QLatin1String("/")) {
pageList.prepend(home);
}
m_pageUrl.resize(pageNum);
for (const QUrl &qurl : std::as_const(pageList)) {
QString url = qurl.toString();
const QString urlLower = url.toLower();
if (!urlLower.endsWith(QLatin1String(".html")) && !urlLower.endsWith(QLatin1String(".htm"))) {
continue;
}
int pos = url.indexOf(QLatin1Char(('#')));
// insert the url into the maps, but insert always the variant without the #ref part
QString tmpUrl = pos == -1 ? url : url.left(pos);
// url already there, abort insertion
if (m_urlPage.contains(tmpUrl)) {
continue;
}
int foundPage = tmpPageList.value(tmpUrl, -1);
if (foundPage != -1) {
m_urlPage.insert(tmpUrl, foundPage);
m_pageUrl[foundPage] = tmpUrl;
} else {
// add pages not present in toc
m_urlPage.insert(tmpUrl, pageNum);
m_pageUrl.append(tmpUrl);
pageNum++;
}
}
pagesVector.resize(m_pageUrl.count());
m_textpageAddedList.fill(false, pagesVector.count());
m_rectsGenerated.fill(false, pagesVector.count());
if (!m_syncGen) {
m_syncGen = new KHTMLPart();
}
disconnect(m_syncGen, nullptr, this, nullptr);
for (int i = 0; i < m_pageUrl.count(); ++i) {
preparePageForSyncOperation(m_pageUrl.at(i));
pagesVector[i] = new Okular::Page(i, m_syncGen->view()->contentsWidth(), m_syncGen->view()->contentsHeight(), Okular::Rotation0);
}
connect(m_syncGen, QOverload<>::of(&KHTMLPart::completed), this, &CHMGenerator::slotCompleted);
connect(m_syncGen, &KParts::ReadOnlyPart::canceled, this, &CHMGenerator::slotCompleted);
return true;
}
bool CHMGenerator::doCloseDocument()
{
// delete the document information of the old document
delete m_file;
m_file = nullptr;
m_textpageAddedList.clear();
m_rectsGenerated.clear();
m_urlPage.clear();
m_pageUrl.clear();
m_docSyn.clear();
if (m_syncGen) {
m_syncGen->closeUrl();
}
return true;
}
void CHMGenerator::preparePageForSyncOperation(const QString &url)
{
QString pAddress = QStringLiteral("ms-its:") + m_fileName + QStringLiteral("::") + m_file->urlToPath(QUrl(url));
m_chmUrl = url;
m_syncGen->openUrl(QUrl(pAddress));
m_syncGen->view()->layout();
QEventLoop loop;
connect(m_syncGen, QOverload<>::of(&KHTMLPart::completed), &loop, &QEventLoop::quit);
connect(m_syncGen, &KParts::ReadOnlyPart::canceled, &loop, &QEventLoop::quit);
// discard any user input, otherwise it breaks the "synchronicity" of this
// function
loop.exec(QEventLoop::ExcludeUserInputEvents);
}
void CHMGenerator::slotCompleted()
{
if (!m_request) {
return;
}
QImage image(m_request->width(), m_request->height(), QImage::Format_ARGB32);
image.fill(Qt::white);
QPainter p(&image);
QRect r(0, 0, m_request->width(), m_request->height());
bool moreToPaint;
m_syncGen->paint(&p, r, 0, &moreToPaint);
p.end();
if (!m_textpageAddedList.at(m_request->pageNumber())) {
additionalRequestData();
m_textpageAddedList[m_request->pageNumber()] = true;
}
m_syncGen->closeUrl();
m_chmUrl = QString();
userMutex()->unlock();
Okular::PixmapRequest *req = m_request;
m_request = nullptr;
if (!req->page()->isBoundingBoxKnown()) {
updatePageBoundingBox(req->page()->number(), Okular::Utils::imageBoundingBox(&image));
}
req->page()->setPixmap(req->observer(), new QPixmap(QPixmap::fromImage(image)));
signalPixmapRequestDone(req);
}
Okular::DocumentInfo CHMGenerator::generateDocumentInfo(const QSet<Okular::DocumentInfo::Key> &keys) const
{
Okular::DocumentInfo docInfo;
if (keys.contains(Okular::DocumentInfo::MimeType)) {
docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("application/x-chm"));
}
if (keys.contains(Okular::DocumentInfo::Title)) {
docInfo.set(Okular::DocumentInfo::Title, m_file->title());
}
return docInfo;
}
const Okular::DocumentSynopsis *CHMGenerator::generateDocumentSynopsis()
{
return &m_docSyn;
}
bool CHMGenerator::canGeneratePixmap() const
{
bool isLocked = true;
if (userMutex()->tryLock()) {
userMutex()->unlock();
isLocked = false;
}
return !isLocked;
}
void CHMGenerator::generatePixmap(Okular::PixmapRequest *request)
{
int requestWidth = request->width();
int requestHeight = request->height();
userMutex()->lock();
QString url = m_pageUrl[request->pageNumber()];
QString pAddress = QStringLiteral("ms-its:") + m_fileName + QStringLiteral("::") + m_file->urlToPath(QUrl(url));
m_chmUrl = url;
m_syncGen->view()->resizeContents(requestWidth, requestHeight);
m_request = request;
// will Q_EMIT openURL without problems
m_syncGen->openUrl(QUrl(pAddress));
}
void CHMGenerator::recursiveExploreNodes(DOM::Node node, Okular::TextPage *tp)
{
if (node.nodeType() == DOM::Node::TEXT_NODE && !node.getRect().isNull()) {
QString nodeText = node.nodeValue().string();
QRect r = node.getRect();
int vWidth = m_syncGen->view()->width();
int vHeight = m_syncGen->view()->height();
Okular::NormalizedRect *nodeNormRect;
#define NOEXP
#ifndef NOEXP
int x, y, height;
int x_next, y_next, height_next;
int nodeTextLength = nodeText.length();
if (nodeTextLength == 1) {
nodeNormRect = new Okular::NormalizedRect(r, vWidth, vHeight);
tp->append(nodeText, nodeNormRect /*, nodeNormRect->bottom, 0, (nodeText == "\n")*/);
} else {
for (int i = 0; i < nodeTextLength; i++) {
node.getCursor(i, x, y, height);
if (i == 0)
// i is 0, use left rect boundary
{
// if (nodeType[i+1]
node.getCursor(i + 1, x_next, y_next, height_next);
nodeNormRect = new Okular::NormalizedRect(QRect(x, y, x_next - x - 1, height), vWidth, vHeight);
} else if (i < nodeTextLength - 1)
// i is between zero and the last element
{
node.getCursor(i + 1, x_next, y_next, height_next);
nodeNormRect = new Okular::NormalizedRect(QRect(x, y, x_next - x - 1, height), vWidth, vHeight);
} else
// the last element use right rect boundary
{
node.getCursor(i - 1, x_next, y_next, height_next);
}
}
}
#else
nodeNormRect = new Okular::NormalizedRect(r, vWidth, vHeight);
tp->append(nodeText, nodeNormRect /*,0*/);
#endif
}
DOM::Node child = node.firstChild();
while (!child.isNull()) {
recursiveExploreNodes(child, tp);
child = child.nextSibling();
}
}
void CHMGenerator::additionalRequestData()
{
Okular::Page *page = m_request->page();
const bool genObjectRects = !m_rectsGenerated.at(m_request->page()->number());
const bool genTextPage = !m_request->page()->hasTextPage() && genObjectRects;
if (genObjectRects || genTextPage) {
DOM::HTMLDocument domDoc = m_syncGen->htmlDocument();
// only generate object info when generating a full page not a thumbnail
if (genObjectRects) {
QList<Okular::ObjectRect *> objRects;
int xScale = m_syncGen->view()->width();
int yScale = m_syncGen->view()->height();
// getting links
DOM::HTMLCollection coll = domDoc.links();
DOM::Node n;
QRect r;
if (!coll.isNull()) {
int size = coll.length();
for (int i = 0; i < size; i++) {
n = coll.item(i);
if (!n.isNull()) {
QString url = n.attributes().getNamedItem("href").nodeValue().string();
r = n.getRect();
// there is no way for us to support javascript properly
if (url.startsWith(QLatin1String("JavaScript:")), Qt::CaseInsensitive) {
continue;
} else if (url.contains(QStringLiteral(":"))) {
objRects.push_back(new Okular::ObjectRect(Okular::NormalizedRect(r, xScale, yScale), false, Okular::ObjectRect::Action, new Okular::BrowseAction(QUrl(url))));
} else {
Okular::DocumentViewport viewport(metaData(QStringLiteral("NamedViewport"), absolutePath(m_chmUrl, url)).toString());
objRects.push_back(new Okular::ObjectRect(Okular::NormalizedRect(r, xScale, yScale), false, Okular::ObjectRect::Action, new Okular::GotoAction(QString(), viewport)));
}
}
}
}
// getting images
coll = domDoc.images();
if (!coll.isNull()) {
int size = coll.length();
for (int i = 0; i < size; i++) {
n = coll.item(i);
if (!n.isNull()) {
objRects.push_back(new Okular::ObjectRect(Okular::NormalizedRect(n.getRect(), xScale, yScale), false, Okular::ObjectRect::Image, nullptr));
}
}
}
m_request->page()->setObjectRects(objRects);
m_rectsGenerated[m_request->page()->number()] = true;
}
if (genTextPage) {
Okular::TextPage *tp = new Okular::TextPage();
recursiveExploreNodes(domDoc, tp);
page->setTextPage(tp);
}
}
}
Okular::TextPage *CHMGenerator::textPage(Okular::TextRequest *request)
{
userMutex()->lock();
const Okular::Page *page = request->page();
m_syncGen->view()->resize(page->width(), page->height());
preparePageForSyncOperation(m_pageUrl[page->number()]);
Okular::TextPage *tp = new Okular::TextPage();
recursiveExploreNodes(m_syncGen->htmlDocument(), tp);
userMutex()->unlock();
return tp;
}
QVariant CHMGenerator::metaData(const QString &key, const QVariant &option) const
{
if (key == QLatin1String("NamedViewport") && !option.toString().isEmpty()) {
const int pos = option.toString().indexOf(QLatin1Char('#'));
QString tmpUrl = pos == -1 ? option.toString() : option.toString().left(pos);
Okular::DocumentViewport viewport;
QMap<QString, int>::const_iterator it = m_urlPage.find(tmpUrl);
if (it != m_urlPage.end()) {
viewport.pageNumber = it.value();
return viewport.toString();
}
} else if (key == QLatin1String("DocumentTitle")) {
return m_file->title();
}
return QVariant();
}
/* kate: replace-tabs on; tab-width 4; */
#include "generator_chm.moc"

View file

@ -1,70 +0,0 @@
/*
SPDX-FileCopyrightText: 2005 Piotr Szymański <niedakh@gmail.com>
SPDX-FileCopyrightText: 2008 Albert Astals Cid <aacid@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef _OKULAR_GENERATOR_CHM_H_
#define _OKULAR_GENERATOR_CHM_H_
#include <core/document.h>
#include <core/generator.h>
#include "lib/ebook_chm.h"
#include <QBitArray>
class KHTMLPart;
namespace Okular
{
class TextPage;
}
namespace DOM
{
class Node;
}
class CHMGenerator : public Okular::Generator
{
Q_OBJECT
Q_INTERFACES(Okular::Generator)
public:
CHMGenerator(QObject *parent, const QVariantList &args);
~CHMGenerator() override;
bool loadDocument(const QString &fileName, QVector<Okular::Page *> &pagesVector) override;
Okular::DocumentInfo generateDocumentInfo(const QSet<Okular::DocumentInfo::Key> &keys) const override;
const Okular::DocumentSynopsis *generateDocumentSynopsis() override;
bool canGeneratePixmap() const override;
void generatePixmap(Okular::PixmapRequest *request) override;
QVariant metaData(const QString &key, const QVariant &option) const override;
public Q_SLOTS:
void slotCompleted();
protected:
bool doCloseDocument() override;
Okular::TextPage *textPage(Okular::TextRequest *request) override;
private:
void additionalRequestData();
void recursiveExploreNodes(DOM::Node node, Okular::TextPage *tp);
void preparePageForSyncOperation(const QString &url);
QMap<QString, int> m_urlPage;
QVector<QString> m_pageUrl;
Okular::DocumentSynopsis m_docSyn;
EBook *m_file;
KHTMLPart *m_syncGen;
QString m_fileName;
QString m_chmUrl;
Okular::PixmapRequest *m_request;
QBitArray m_textpageAddedList;
QBitArray m_rectsGenerated;
};
#endif

View file

@ -1,13 +0,0 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../../..
${CMAKE_CURRENT_SOURCE_DIR}/../lib
${CHM_INCLUDE_DIR}
)
########### next target ###############
set(kio_msits_PART_SRCS msits.cpp kio_mits_debug.cpp)
kcoreaddons_add_plugin(kio_msits SOURCES ${kio_msits_PART_SRCS} INSTALL_NAMESPACE "kf5/kio")
target_link_libraries(kio_msits KF6::KIOCore Qt6::Core ${CHM_LIBRARY} Qt6::Network)

View file

@ -1,8 +0,0 @@
/* This file is part of the KDE project
SPDX-FileCopyrightText: 2014 Laurent Montel <montel@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kio_mits_debug.h"
Q_LOGGING_CATEGORY(KIO_MITS_LOG, "org.kde.kio.msits", QtWarningMsg)

View file

@ -1,13 +0,0 @@
/* This file is part of the KDE project
SPDX-FileCopyrightText: 2014 Laurent Montel <montel@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KIO_MITS_DEBUG_H
#define KIO_MITS_DEBUG_H
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(KIO_MITS_LOG)
#endif

View file

@ -1,18 +0,0 @@
{
"KDE-KIO-Protocols": {
"ms-its": {
"Icon": "help",
"defaultMimetype": "text/html",
"exec": "kio_msits",
"input": "none",
"listing": [
"Name",
"Type",
"Size"
],
"output": "filesystem",
"protocol": "ms-its",
"reading": true
}
}
}

View file

@ -1,292 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "kio_mits_debug.h"
#include <QCoreApplication>
#include <QMimeDatabase>
#include <QMimeType>
#include <QBitArray>
#include <QDir>
#include <QFile>
#include <QVector>
#include "libchmurlfactory.h"
#include "msits.h"
using namespace KIO;
// Pseudo plugin class to embed meta data
class KIOPluginForMetaData : public QObject
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.kde.kio.slave.ms-its.json" FILE "ms-its.json")
};
extern "C" {
int Q_DECL_EXPORT kdemain(int argc, char **argv)
{
qCDebug(KIO_MITS_LOG) << "*** kio_msits Init";
QCoreApplication app(argc, argv);
app.setApplicationName(QStringLiteral("kio_msits"));
if (argc != 4) {
qCDebug(KIO_MITS_LOG) << "Usage: kio_msits protocol domain-socket1 domain-socket2";
exit(-1);
}
ProtocolMSITS slave(argv[2], argv[3]);
slave.dispatchLoop();
qCDebug(KIO_MITS_LOG) << "*** kio_msits Done";
return 0;
}
}
ProtocolMSITS::ProtocolMSITS(const QByteArray &pool_socket, const QByteArray &app_socket)
: SlaveBase("kio_msits", pool_socket, app_socket)
{
m_chmFile = nullptr;
}
ProtocolMSITS::~ProtocolMSITS()
{
if (!m_chmFile) {
return;
}
chm_close(m_chmFile);
m_chmFile = nullptr;
}
// A simple stat() wrapper
static bool isDirectory(const QString &filename)
{
return filename.endsWith(QLatin1Char('/'));
}
void ProtocolMSITS::get(const QUrl &url)
{
QString htmdata, fileName;
chmUnitInfo ui;
QByteArray buf;
qCDebug(KIO_MITS_LOG) << "kio_msits::get() " << url.path();
if (!parseLoadAndLookup(url, fileName)) {
return; // error() has been called by parseLoadAndLookup
}
qCDebug(KIO_MITS_LOG) << "kio_msits::get: parseLoadAndLookup returned " << fileName;
if (LCHMUrlFactory::handleFileType(url.path(), htmdata)) {
buf = htmdata.toUtf8();
qCDebug(KIO_MITS_LOG) << "Using special handling for image pages: " << htmdata;
} else {
if (isDirectory(fileName)) {
error(KIO::ERR_IS_DIRECTORY, url.toString());
return;
}
if (!ResolveObject(fileName, &ui)) {
qCDebug(KIO_MITS_LOG) << "kio_msits::get: could not resolve filename " << fileName;
error(KIO::ERR_DOES_NOT_EXIST, url.toString());
return;
}
buf.resize(ui.length);
if (RetrieveObject(&ui, (unsigned char *)buf.data(), 0, ui.length) == 0) {
qCDebug(KIO_MITS_LOG) << "kio_msits::get: could not retrieve filename " << fileName;
error(KIO::ERR_NO_CONTENT, url.toString());
return;
}
}
totalSize(buf.size());
QMimeDatabase db;
QMimeType result = db.mimeTypeForFileNameAndData(fileName, buf);
qCDebug(KIO_MITS_LOG) << "Emitting mimetype " << result.name();
mimeType(result.name());
data(buf);
processedSize(buf.size());
finished();
}
bool ProtocolMSITS::parseLoadAndLookup(const QUrl &url, QString &abspath)
{
qCDebug(KIO_MITS_LOG) << "ProtocolMSITS::parseLoadAndLookup (const KUrl&) " << url.path();
int pos = url.path().indexOf(QStringLiteral("::"));
if (pos == -1) {
error(KIO::ERR_MALFORMED_URL, url.toString());
return false;
}
QString filename = url.path().left(pos);
abspath = url.path().mid(pos + 2); // skip ::
// Some buggy apps add ms-its:/ to the path as well
if (abspath.startsWith(QLatin1String("ms-its:"))) {
abspath = abspath.mid(7);
}
qCDebug(KIO_MITS_LOG) << "ProtocolMSITS::parseLoadAndLookup: filename " << filename << ", path " << abspath;
if (filename.isEmpty()) {
error(KIO::ERR_MALFORMED_URL, url.toString());
return false;
}
// If the file has been already loaded, nothing to do.
if (m_chmFile && filename == m_openedFile) {
return true;
}
qCDebug(KIO_MITS_LOG) << "Opening a new CHM file " << QFile::encodeName(QDir::toNativeSeparators(filename));
// First try to open a temporary file
chmFile *tmpchm;
if ((tmpchm = chm_open(QFile::encodeName(QDir::toNativeSeparators(filename)).constData())) == nullptr) {
error(KIO::ERR_CANNOT_READ, url.toString());
return false;
}
// Replace an existing file by a new one
if (m_chmFile) {
chm_close(m_chmFile);
}
m_chmFile = tmpchm;
m_openedFile = filename;
qCDebug(KIO_MITS_LOG) << "A CHM file " << filename << " has beed opened successfully";
return true;
}
/*
* Shamelessly stolen from a KDE KIO tutorial
*/
static void app_entry(UDSEntry &e, unsigned int uds, const QString &str)
{
e.fastInsert(uds, str);
}
// appends an int with the UDS-ID uds
static void app_entry(UDSEntry &e, unsigned int uds, long l)
{
e.fastInsert(uds, l);
}
// internal function
// fills a directory item with its name and size
static void app_dir(UDSEntry &e, const QString &name)
{
e.clear();
app_entry(e, KIO::UDSEntry::UDS_NAME, name);
app_entry(e, KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
app_entry(e, KIO::UDSEntry::UDS_SIZE, 1);
}
// internal function
// fills a file item with its name and size
static void app_file(UDSEntry &e, const QString &name, size_t size)
{
e.clear();
app_entry(e, KIO::UDSEntry::UDS_NAME, name);
app_entry(e, KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
app_entry(e, KIO::UDSEntry::UDS_SIZE, size);
}
void ProtocolMSITS::stat(const QUrl &url)
{
QString fileName;
chmUnitInfo ui;
qCDebug(KIO_MITS_LOG) << "kio_msits::stat (const KUrl& url) " << url.path();
if (!parseLoadAndLookup(url, fileName)) {
return; // error() has been called by parseLoadAndLookup
}
if (!ResolveObject(fileName, &ui)) {
error(KIO::ERR_DOES_NOT_EXIST, url.toString());
return;
}
qCDebug(KIO_MITS_LOG) << "kio_msits::stat: adding an entry for " << fileName;
UDSEntry entry;
if (isDirectory(fileName)) {
app_dir(entry, fileName);
} else {
app_file(entry, fileName, ui.length);
}
statEntry(entry);
finished();
}
// A local CHMLIB enumerator
static int chmlib_enumerator(struct chmFile *, struct chmUnitInfo *ui, void *context)
{
((QVector<QString> *)context)->push_back(QString::fromLocal8Bit(ui->path));
return CHM_ENUMERATOR_CONTINUE;
}
void ProtocolMSITS::listDir(const QUrl &url)
{
QString filepath;
qCDebug(KIO_MITS_LOG) << "kio_msits::listDir (const KUrl& url) " << url.path();
if (!parseLoadAndLookup(url, filepath)) {
return; // error() has been called by parseLoadAndLookup
}
filepath += QLatin1Char('/');
if (!isDirectory(filepath)) {
error(KIO::ERR_CANNOT_ENTER_DIRECTORY, url.path());
return;
}
qCDebug(KIO_MITS_LOG) << "kio_msits::listDir: enumerating directory " << filepath;
QVector<QString> listing;
if (chm_enumerate_dir(m_chmFile, filepath.toLocal8Bit().constData(), CHM_ENUMERATE_NORMAL | CHM_ENUMERATE_FILES | CHM_ENUMERATE_DIRS, chmlib_enumerator, &listing) != 1) {
error(KIO::ERR_CANNOT_ENTER_DIRECTORY, url.path());
return;
}
UDSEntry entry;
int striplength = filepath.length();
for (const QString &iListing : std::as_const(listing)) {
// Strip the directory name
const QString ename = iListing.mid(striplength);
if (isDirectory(ename)) {
app_dir(entry, ename);
} else {
app_file(entry, ename, 0);
}
}
finished();
}
#include "msits.moc"

View file

@ -1,57 +0,0 @@
/*
SPDX-FileCopyrightText: 2005 Georgy Yunaev <tim@krasnogorsk.ru>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef MSITS_H
#define MSITS_H
#include <QUrl>
#include <kio/slavebase.h>
#include <QByteArray>
#include <QString>
#include "chm_lib.h"
class ProtocolMSITS : public KIO::SlaveBase
{
public:
ProtocolMSITS(const QByteArray &, const QByteArray &);
~ProtocolMSITS() override;
ProtocolMSITS(const ProtocolMSITS &) = delete;
ProtocolMSITS &operator=(const ProtocolMSITS &) = delete;
void get(const QUrl &) override;
void listDir(const QUrl &url) override;
void stat(const QUrl &url) override;
private:
// This function does next thing:
// - parses the URL to get a file name and URL inside the file;
// - loads a new CHM file, if needed;
// - returns the parsed URL inside the file;
bool parseLoadAndLookup(const QUrl &, QString &abspath);
// Resolve an object inside a CHM file
inline bool ResolveObject(const QString &fileName, chmUnitInfo *ui)
{
return m_chmFile != nullptr && ::chm_resolve_object(m_chmFile, fileName.toUtf8().constData(), ui) == CHM_RESOLVE_SUCCESS;
}
// Retrieve an object from the CHM file
inline size_t RetrieveObject(chmUnitInfo *ui, unsigned char *buffer, LONGUINT64 fileOffset, LONGINT64 bufferSize)
{
return ::chm_retrieve_object(m_chmFile, ui, buffer, fileOffset, bufferSize);
}
// An opened file name, if presend
QString m_openedFile;
// a CHM structure file pointer (from chmlib)
chmFile *m_chmFile;
};
#endif /* MSITS_H */

View file

@ -1,151 +0,0 @@
/*
SPDX-FileCopyrightText: 2003 Razvan Cojocaru <razvanco@gmx.net>
Most of the code in this file is a modified version of code from
Pabs' GPL chmdeco project, credits and thanks go to him.
SPDX-License-Identifier: GPL-2.0-or-later
*/
inline unsigned short UINT16ARRAY(const void *x)
{
unsigned char *p = (unsigned char *)x;
return p[0] | (p[1] << 8);
}
inline unsigned int UINT32ARRAY(const void *x)
{
unsigned char *p = (unsigned char *)x;
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
inline int INT32ARRAY(const void *x)
{
char *p = (char *)x;
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
inline unsigned int get_int32_le(void *addr)
{
unsigned char *p = (unsigned char *)addr;
return (unsigned int)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
}
inline quint64 be_encint(unsigned char *buffer, size_t &length)
{
quint64 result = 0;
int shift = 0;
length = 0;
do {
result |= ((*buffer) & 0x7f) << shift;
shift += 7;
++length;
} while (*(buffer++) & 0x80);
return result;
}
/*
Finds the first unset bit in memory. Returns the number of set bits found.
Returns -1 if the buffer runs out before we find an unset bit.
*/
inline int ffus(unsigned char *byte, int *bit, size_t &length)
{
int bits = 0;
length = 0;
while (*byte & (1 << *bit)) {
if (*bit) {
--(*bit);
} else {
++byte;
++length;
*bit = 7;
}
++bits;
}
if (*bit) {
--(*bit);
} else {
++length;
*bit = 7;
}
return bits;
}
inline quint64 sr_int(unsigned char *byte, int *bit, unsigned char s, unsigned char r, size_t &length)
{
quint64 ret;
unsigned char mask;
int n, n_bits, num_bits, base, count;
length = 0;
size_t fflen;
if (!bit || *bit > 7 || s != 2) {
return ~(quint64)0;
}
ret = 0;
count = ffus(byte, bit, fflen);
length += fflen;
byte += length;
n_bits = n = r + (count ? count - 1 : 0);
while (n > 0) {
num_bits = n > *bit ? *bit : n - 1;
base = n > *bit ? 0 : *bit - (n - 1);
switch (num_bits) {
case 0:
mask = 1;
break;
case 1:
mask = 3;
break;
case 2:
mask = 7;
break;
case 3:
mask = 0xf;
break;
case 4:
mask = 0x1f;
break;
case 5:
mask = 0x3f;
break;
case 6:
mask = 0x7f;
break;
case 7:
mask = 0xff;
break;
default:
mask = 0xff;
break;
}
mask <<= base;
ret = (ret << (num_bits + 1)) | (quint64)((*byte & mask) >> base);
if (n > *bit) {
++byte;
++length;
n -= *bit + 1;
*bit = 7;
} else {
*bit -= n;
n = 0;
}
}
if (count) {
ret |= (quint64)1 << n_bits;
}
return ret;
}

View file

@ -1,38 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "ebook.h"
#include "ebook_chm.h"
#include "ebook_epub.h"
EBook::EBook()
{
}
EBook::~EBook()
{
}
EBook *EBook::loadFile(const QString &archiveName)
{
EBook_CHM *cbook = new EBook_CHM();
if (cbook->load(archiveName)) {
return cbook;
}
delete cbook;
EBook_EPUB *ebook = new EBook_EPUB();
if (ebook->load(archiveName)) {
return ebook;
}
delete ebook;
return nullptr;
}

View file

@ -1,219 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef INCLUDE_EBOOK_H
#define INCLUDE_EBOOK_H
#include <QList>
#include <QString>
#include <QUrl>
//! Stores a single table of content entry
class EBookTocEntry
{
public:
//! Content TOC icon indexes for CHM books (epub books contain no icons)
enum Icon {
IMAGE_NONE = -1,
IMAGE_AUTO = -2,
MAX_BUILTIN_ICONS = 42
};
//! Entry name
QString name;
//! Entry URL.
QUrl url;
//! Associated image number. Used for TOC only; indexes does not have the image.
//! If IMAGE_NONE, no icon is associated. Otherwise use getBookIconPixmap() to get associated pixmap icon.
Icon iconid;
//! Indentation level for this entry.
int indent;
};
//! Stores a single index entry
class EBookIndexEntry
{
public:
//! Entry name
QString name;
//! Entry URLs. The index entry could have several URLs
QList<QUrl> urls;
//! Whether this is a 'see also' index type, and its value
QString seealso;
//! Indentation level for this entry.
int indent;
};
//! Universal ebook files processor supporting both CHM and EPUB. Abstract.
class EBook
{
public:
enum Feature {
FEATURE_TOC, // has table of contents
FEATURE_INDEX, // has index
FEATURE_ENCODING // Could be encoded with different encodings
};
//! Default constructor and destructor.
EBook();
virtual ~EBook();
EBook(const EBook &) = delete;
EBook &operator=(const EBook &) = delete;
/*!
* \brief Attempts to load chm or epub file.
* \param archiveName filename.
* \return EBook object on success, NULL on failure.
*
* Loads a CHM or epub file. For CHM files it could internally load more than one file,
* if files linked to this one are present locally (like MSDN).
* \ingroup init
*/
static EBook *loadFile(const QString &archiveName);
/*!
* \brief Closes all the files, and frees the appropriate data.
* \ingroup init
*/
virtual void close() = 0;
/*!
* \brief Gets the title name of the opened ebook.
* \return The name of the opened document, or an empty string if no ebook has been loaded.
* \ingroup information
*/
virtual QString title() const = 0;
/*!
* \brief Gets the default URL of the e-book which should be opened when the book it first open
*
* \return The home page name, with a '/' added in front and relative to
* the root of the archive filesystem. If no book has been opened, returns "/".
* \ingroup information
*/
virtual QUrl homeUrl() const = 0;
/*!
* \brief Checks whether the specific feature is present in this file.
* \return true if it is available; false otherwise.
* \ingroup information
*/
virtual bool hasFeature(Feature code) const = 0;
/*!
* \brief Parses and fills up the Table of Contents (TOC)
* \param topics A pointer to the container which will store the parsed results.
* Will be cleaned before parsing.
* \return true if the tree is present and parsed successfully, false otherwise.
* The parser is built to be error-prone, however it still can abort with qFatal()
* by really buggy files; please report a bug if the file is opened ok under Windows.
* \ingroup fileparsing
*/
virtual bool getTableOfContents(QList<EBookTocEntry> &toc) const = 0;
/*!
* \brief Parses the index table
* \param indexes A pointer to the container which will store the parsed results.
* Will be cleaned before parsing.
* \return true if the tree is present and parsed successfully, false otherwise.
* The parser is built to be error-prone, however it still can abort with qFatal()
* by really buggy chm file; so far it never happened on indexes.
* \ingroup fileparsing
*/
virtual bool getIndex(QList<EBookIndexEntry> &index) const = 0;
/*!
* \brief Retrieves the content associated with the url from the current ebook as QString.
* \param str A string where the retreived content should be stored.
* \param url An URL in chm file to retreive content from. Must be absolute.
* \return true if the content is successfully received; false otherwise. Note content may be an empty string.
*
* This function retreives the file content (mostly for HTML pages) from the ebook. Because the content
* in chm file might not be stored in Unicode, it will be recoded according to current encoding.
* Do not use for binary data.
*
* \sa setCurrentEncoding() currentEncoding() getFileContentAsBinary()
* \ingroup dataretrieve
*/
virtual bool getFileContentAsString(QString &str, const QUrl &url) const = 0;
/*!
* \brief Retrieves the content from url in current chm file to QByteArray.
* \param data A data array where the retreived content should be stored.
* \param url An URL in chm file to retreive content from. Must be absolute.
* \return true if the content is successfully received; false otherwise.
*
* This function retreives the file content from the chm archive opened by load()
* function. The content is not encoded.
*
* \sa getFileContentAsString()
* \ingroup dataretrieve
*/
virtual bool getFileContentAsBinary(QByteArray &data, const QUrl &url) const = 0;
/*!
* \brief Obtains the list of all the files (URLs) in current ebook archive. This is used in search
* and to dump the e-book content.
* \param files An array to store list of URLs present in chm archive.
* \return true if the enumeration succeed; false otherwise (I could hardly imagine a reason).
*
* \ingroup dataretrieve
*/
virtual bool enumerateFiles(QList<QUrl> &files) = 0;
/*!
* \brief Gets the Title of the page referenced by url.
* \param url An URL in ebook file to get title from. Must be absolute.
* \return The title, or QString() if the URL cannot be found or not a HTML page.
*
* \ingroup dataretrieve
*/
virtual QString getTopicByUrl(const QUrl &url) = 0;
/*!
* \brief Gets the current ebook encoding (set or autodetected) as qtcodec name. Must be implemented,
* even if the book doesn't support change of encoding (then it should return a default encoding)
* \return The current encoding.
*
* \ingroup encoding
*/
virtual QString currentEncoding() const = 0;
/*!
* \brief Sets the ebook encoding to use for TOC and content
* \param encoding An encoding to use.
*
* \ingroup encoding
*/
virtual bool setCurrentEncoding(const char *encoding) = 0;
/*!
* \brief Checks if this kind of URL is supported by the ebook format (i.e. could be passed to ebook functions)
* \param url The url to check
*/
virtual bool isSupportedUrl(const QUrl &url) = 0;
// Converts the string to the ebook-specific URL format
virtual QUrl pathToUrl(const QString &link) const = 0;
// Extracts the path component from the URL
virtual QString urlToPath(const QUrl &link) const = 0;
protected:
// Loads the file; returns true if loaded, false otherwise
virtual bool load(const QString &archiveName) = 0;
};
#endif // INCLUDE_LIBCHMFILE_H

File diff suppressed because it is too large Load diff

View file

@ -1,324 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef EBOOK_CHM_H
#define EBOOK_CHM_H
#include <QMap>
#include <QTextCodec>
// Enable Unicode use in libchm
#if defined(WIN32)
#define PPC_BSTR
#endif
#include <chm_lib.h>
#include "ebook.h"
#include "helper_entitydecoder.h"
class EBook_CHM : public EBook
{
public:
EBook_CHM();
~EBook_CHM() override;
/*!
* \brief Attempts to load chm file.
* \param archiveName filename.
* \return EBook object on success, NULL on failure.
*
* Loads a CHM file. For CHM files it could internally load more than one file,
* if files linked to this one are present locally (like MSDN).
* \ingroup init
*/
bool load(const QString &archiveName) override;
/*!
* \brief Closes all the files, and frees the appropriate data.
* \ingroup init
*/
void close() override;
/*!
* \brief Gets the title name of the opened ebook.
* \return The name of the opened document, or an empty string if no ebook has been loaded.
* \ingroup information
*/
QString title() const override;
/*!
* \brief Gets the default URL of the e-book which should be opened when the book it first open
*
* \return The home page name, with a '/' added in front and relative to
* the root of the archive filesystem. If no book has been opened, returns "/".
* \ingroup information
*/
QUrl homeUrl() const override;
/*!
* \brief Checks whether the specific feature is present in this file.
* \return true if it is available; false otherwise.
* \ingroup information
*/
bool hasFeature(Feature code) const override;
/*!
* \brief Parses and fills up the Table of Contents (TOC)
* \param topics A pointer to the container which will store the parsed results.
* Will be cleaned before parsing.
* \return true if the tree is present and parsed successfully, false otherwise.
* The parser is built to be error-prone, however it still can abort with qFatal()
* by really buggy files; please report a bug if the file is opened ok under Windows.
* \ingroup fileparsing
*/
bool getTableOfContents(QList<EBookTocEntry> &toc) const override;
/*!
* \brief Parses the index table
* \param indexes A pointer to the container which will store the parsed results.
* Will be cleaned before parsing.
* \return true if the tree is present and parsed successfully, false otherwise.
* The parser is built to be error-prone, however it still can abort with qFatal()
* by really buggy chm file; so far it never happened on indexes.
* \ingroup fileparsing
*/
bool getIndex(QList<EBookIndexEntry> &index) const override;
/*!
* \brief Retrieves the content associated with the url from the current ebook as QString.
* \param str A string where the retreived content should be stored.
* \param url An URL in chm file to retreive content from. Must be absolute.
* \return true if the content is successfully received; false otherwise. Note content may be an empty string.
*
* This function retreives the file content (mostly for HTML pages) from the ebook. Because the content
* in chm file might not be stored in Unicode, it will be recoded according to current encoding.
* Do not use for binary data.
*
* \sa setCurrentEncoding() currentEncoding() getFileContentAsBinary()
* \ingroup dataretrieve
*/
bool getFileContentAsString(QString &str, const QUrl &url) const override;
/*!
* \brief Retrieves the content from url in current chm file to QByteArray.
* \param data A data array where the retreived content should be stored.
* \param url An URL in chm file to retreive content from. Must be absolute.
* \return true if the content is successfully received; false otherwise.
*
* This function retreives the file content from the chm archive opened by load()
* function. The content is not encoded.
*
* \sa getFileContentAsString()
* \ingroup dataretrieve
*/
bool getFileContentAsBinary(QByteArray &data, const QUrl &url) const override;
/*!
* \brief Retrieves the content size.
* \param url An URL in ebook file to retreive content from. Must be absolute.
* \return the size; -1 in case of error.
*
* \ingroup dataretrieve
*/
virtual int getContentSize(const QString &url);
/*!
* \brief Obtains the list of all the files (URLs) in current ebook archive. This is used in search
* and to dump the e-book content.
* \param files An array to store list of URLs (file names) present in chm archive.
* \return true if the enumeration succeed; false otherwise (I could hardly imagine a reason).
*
* \ingroup dataretrieve
*/
bool enumerateFiles(QList<QUrl> &files) override;
/*!
* \brief Gets the Title of the page referenced by url.
* \param url An URL in ebook file to get title from. Must be absolute.
* \return The title, or QString() if the URL cannot be found or not a HTML page.
*
* \ingroup dataretrieve
*/
QString getTopicByUrl(const QUrl &url) override;
/*!
* \brief Gets the current ebook encoding (set or autodetected) as qtcodec
* \return The current encoding.
*
* \ingroup encoding
*/
QString currentEncoding() const override;
/*!
* \brief Sets the ebook encoding to use for TOC and content
* \param encoding An encoding to use.
*
* \ingroup encoding
*/
bool setCurrentEncoding(const char *encoding) override;
/*!
* \brief Checks if this kind of URL is supported by the ebook format (i.e. could be passed to ebook functions)
* \param url The url to check
*/
bool isSupportedUrl(const QUrl &url) override;
// Converts the string to the ebook-specific URL format
QUrl pathToUrl(const QString &link) const override;
// Extracts the path component from the URL
QString urlToPath(const QUrl &link) const override;
private:
// Used in local parser
class ParsedEntry
{
public:
ParsedEntry();
QString name;
QList<QUrl> urls;
int iconid;
int indent;
QString seealso;
};
//! Looks up fileName in the archive.
bool hasFile(const QString &fileName) const;
//! Looks up fileName in the archive.
bool ResolveObject(const QString &fileName, chmUnitInfo *ui) const;
//! Retrieves an uncompressed chunk of a file in the .chm.
size_t RetrieveObject(const chmUnitInfo *ui, unsigned char *buffer, LONGUINT64 fileOffset, LONGINT64 bufferSize) const;
//! Encode the string with the currently selected text codec, if possible. Or return as-is, if not.
inline QString encodeWithCurrentCodec(const QByteArray &str) const
{
return (m_textCodec ? m_textCodec->toUnicode(str.constData()) : QString::fromUtf8(str));
}
//! Encode the string with the currently selected text codec, if possible. Or return as-is, if not.
inline QString encodeWithCurrentCodec(const char *str) const
{
return (m_textCodec ? m_textCodec->toUnicode(str) : QString::fromUtf8(str));
}
//! Encode the string from internal files with the currently selected text codec, if possible.
//! Or return as-is, if not.
inline QString encodeInternalWithCurrentCodec(const QString &str) const
{
return (m_textCodecForSpecialFiles ? m_textCodecForSpecialFiles->toUnicode(qPrintable(str)) : str);
}
//! Encode the string from internal files with the currently selected text codec, if possible.
//! Or return as-is, if not.
inline QString encodeInternalWithCurrentCodec(const char *str) const
{
return (m_textCodecForSpecialFiles ? m_textCodecForSpecialFiles->toUnicode(str) : QString::fromUtf8(str));
}
//! Helper. Translates from Win32 encodings to generic wxWidgets ones.
const char *GetFontEncFromCharSet(const QString &font) const;
//! Parse the HHC or HHS file, and fill the context (asIndex is false) or index (asIndex is true) array.
bool parseFileAndFillArray(const QString &file, QList<ParsedEntry> &data, bool asIndex) const;
bool getBinaryContent(QByteArray &data, const QString &url) const;
bool getTextContent(QString &str, const QString &url, bool internal_encoding = false) const;
/*!
* Parse binary TOC
*/
bool parseBinaryTOC(QList<EBookTocEntry> &toc) const;
//! btree string parser
QString getBtreeString(const QByteArray &btidx, unsigned long *offset, unsigned short *spaceLeft) const;
/*!
* Recursively parse and fill binary TOC
*/
bool RecurseLoadBTOC(const QByteArray &tocidx, const QByteArray &topics, const QByteArray &urltbl, const QByteArray &urlstr, const QByteArray &strings, int offset, QList<EBookTocEntry> &entries, int level) const;
/*!
* Helper procedure in TOC parsing, decodes the string between the quotes (first or last) with decoding HTML
* entities like &iacute;
*/
int findStringInQuotes(const QString &tag, int offset, QString &value, bool firstquote, bool decodeentities) const;
bool getInfoFromWindows();
bool getInfoFromSystem();
bool changeFileEncoding(const QString &qtencoding);
bool guessTextEncoding();
void fillTopicsUrlMap();
bool hasOption(const QString &name) const;
// Members
//! Pointer to the chmlib structure
chmFile *m_chmFile;
//! Opened file name
QString m_filename;
//! Home url, got from CHM file
QByteArray m_home;
//! Context tree filename. Got from CHM file
QByteArray m_topicsFile;
//! Index filename. Got from CHM file
QByteArray m_indexFile;
//! Chm Title. Got from CHM file
QByteArray m_title;
// Localization stuff
//! LCID from CHM file, used in encoding detection
short m_detectedLCID;
//! font charset from CHM file, used in encoding detection
QString m_font;
//! Chosen text codec
QTextCodec *m_textCodec;
QTextCodec *m_textCodecForSpecialFiles;
//! Current encoding
QString m_currentEncoding;
//! TRUE if /#TOPICS, /#STRINGS, /#URLTBL and /#URLSTR are resolved, and the members below are valid
bool m_lookupTablesValid;
//! pointer to /#TOPICS
chmUnitInfo m_chmTOPICS;
//! pointer to /#STRINGS
chmUnitInfo m_chmSTRINGS;
//! pointer to /#URLTBL
chmUnitInfo m_chmURLTBL;
//! pointer to /#URLSTR
chmUnitInfo m_chmURLSTR;
//! Indicates whether TOC, either binary or text, is available.
bool m_tocAvailable;
//! Indicates whether index, either binary or text, is available.
bool m_indexAvailable;
//! Map url->topic
QMap<QUrl, QString> m_url2topics;
//! KCHMViewer debug options from environment
QString m_envOptions;
//! HTML entity decoder
HelperEntityDecoder m_htmlEntityDecoder;
};
#endif // EBOOK_CHM_H

View file

@ -1,132 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "ebook_chm_encoding.h"
typedef struct {
const char *qtcodec;
const short *lcids;
} EbookChmTextEncodingEntry;
// Do not try to embed those in the text_encoding_table, it does not work - at least with gcc.
static short lcid_arabic[] = {
0x1401, 0x3C01, 0x0C01, 0x0801, 0x2C01, 0x3401, 0x3001, 0x1001, 0x1801, 0x2001, 0x4001, 0x0401, 0x2801, 0x1C01, 0x3801, 0x2401, 0x0429, 0x0420, 0,
};
static short lcid_baltic[] = {0x0425, 0x0426, 0x0427, 0};
static short lcid_centralEuropean[] = {0x041C, 0x041A, 0x0405, 0x040E, 0x0415, 0x0418, 0x081A, 0x041B, 0x0424, 0};
static short lcid_ChineseSimplifiedGB18030[] = {0x0804, 0};
static short lcid_ChineseSimplifiedGBK[] = {0x0804, 0};
static short lcid_ChineseSimplifiedGB2313[] = {0x1004, 0};
static short lcid_ChineseTraditionalBig5[] = {0x0404, 0x1404, 0};
static short lcid_ChineseTraditionalBigHKSCS[] = {0x0C04, 0};
static short lcid_CyrillicCP1251[] = {0x082C, 0x0423, 0x0402, 0x042F, 0x0419, 0x0C1A, 0x0444, 0x0422, 0x0843, 0};
static short lcid_CyrillicKOI8R[] = {0x7001, // artifical LCID
0};
static short lcid_Greek[] = {0x0408, 0};
static short lcid_Hebrew[] = {0x040D, 0};
static short lcid_Japanese_eucJP[] = {0x0411, 0};
static short lcid_Japanese_JIS7[] = {0x0411, 0};
static short lcid_Japanese_ShiftJIS[] = {0x0411, 0};
static short lcid_Korean_eucKR[] = {0x0412, 0};
static short lcid_TamilTSCII[] = {0x0449, 0};
static short lcid_ThaiTIS[] = {0x041E, 0};
static short lcid_UkrainianKOI[] = {0x7006, 0};
static short lcid_Turkish[] = {0x042C, 0x041F, 0x0443, 0};
static short lcid_Vietnamese[] = {0x042A, 0};
static short lcid_UnicodeUTF8[] = {0x7004, // artifical LCID
0};
static short lcid_UnicodeUTF16[] = {0x7005, // artifical LCID
0};
static short lcid_Western[] = {0x0436, 0x042D, 0x0403, 0x0406, 0x0813, 0x0413, 0x0C09, 0x2809, 0x1009, 0x2409, 0x1809, 0x2009, 0x1409, 0x3409, 0x1C09, 0x2C09, 0x0809, 0x0409, 0x0438, 0x040B, 0x080C, 0x0C0C, 0x040C, 0x140C, 0x100C,
0x0C07, 0x0407, 0x1407, 0x1007, 0x0807, 0x040F, 0x0421, 0x0410, 0x0810, 0x083E, 0x043E, 0x0414, 0x0814, 0x0416, 0x0816, 0x0432, 0x2C0A, 0x400A, 0x340A, 0x240A, 0x140A, 0x1C0A, 0x300A, 0x440A, 0x100A,
0x480A, 0x080A, 0x4C0A, 0x180A, 0x3C0A, 0x280A, 0x500A, 0x0C0A, 0x380A, 0x200A, 0x0441, 0x081D, 0x041D, 0x0434, 0x0435, 0x042B, 0x042C, 0x0439, 0x043A, 0x044E, 0x044F, 0x081A, 0x0443, 0};
static const EbookChmTextEncodingEntry text_encoding_table[] = {{"CP1256", lcid_arabic},
{"CP1257", lcid_baltic},
{"CP1250", lcid_centralEuropean},
{"GB18030", lcid_ChineseSimplifiedGB18030},
{"GBK", lcid_ChineseSimplifiedGBK},
{"GB2313", lcid_ChineseSimplifiedGB2313},
{"Big5", lcid_ChineseTraditionalBig5},
{"Big5-HKSCS", lcid_ChineseTraditionalBigHKSCS},
{"CP1251", lcid_CyrillicCP1251},
{"KOI8-R", lcid_CyrillicKOI8R},
{"CP1253", lcid_Greek},
{"CP1255", lcid_Hebrew},
{"Shift-JIS", lcid_Japanese_ShiftJIS},
{"eucJP", lcid_Japanese_eucJP},
{"JIS7", lcid_Japanese_JIS7},
{"eucKR", lcid_Korean_eucKR},
{"TSCII", lcid_TamilTSCII},
{"TIS-620", lcid_ThaiTIS},
{"KOI8-U", lcid_UkrainianKOI},
{"CP1254", lcid_Turkish},
{"CP1258", lcid_Vietnamese},
{"UTF-8", lcid_UnicodeUTF8},
{"UTF-16", lcid_UnicodeUTF16},
{"CP1252", lcid_Western},
{nullptr, nullptr}};
QString Ebook_CHM_Encoding::guessByLCID(unsigned short lcid)
{
for (const EbookChmTextEncodingEntry *t = text_encoding_table; t->qtcodec; ++t) {
for (const short *lcids = t->lcids; *lcids; lcids++) {
if (*lcids == lcid) {
return QString::fromLatin1(t->qtcodec);
}
}
}
return QStringLiteral("UTF-8");
}

View file

@ -1,19 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef EBOOK_CHM_ENCODING_H
#define EBOOK_CHM_ENCODING_H
#include <QString>
class Ebook_CHM_Encoding
{
public:
static QString guessByLCID(unsigned short lcid);
};
#endif // EBOOK_CHM_ENCODING_H

View file

@ -1,364 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#if defined(WIN32)
#include <io.h> // dup
#else
#include <unistd.h>
#endif
#include <KLocalizedString>
#include <QMessageBox>
#include <QXmlSimpleReader>
#include "ebook_epub.h"
#include "helperxmlhandler_epubcontainer.h"
#include "helperxmlhandler_epubcontent.h"
#include "helperxmlhandler_epubtoc.h"
#define URL_SCHEME_EPUB QStringLiteral("epub")
EBook_EPUB::EBook_EPUB()
: EBook()
{
m_zipFile = nullptr;
}
EBook_EPUB::~EBook_EPUB()
{
close();
}
bool EBook_EPUB::load(const QString &archiveName)
{
close();
// We use QFile and zip_fdopen instead of zip_open because latter does not support Unicode file names
m_epubFile.setFileName(archiveName);
if (!m_epubFile.open(QIODevice::ReadOnly)) {
qWarning("Could not open file %s: %s", qPrintable(archiveName), qPrintable(m_epubFile.errorString()));
return false;
}
// Open the ZIP archive: http://www.nih.at/libzip/zip_fdopen.html
// Note that zip_fdopen takes control over the passed descriptor,
// so we need to pass a duplicate of it for this to work correctly
int fdcopy = dup(m_epubFile.handle());
if (fdcopy < 0) {
qWarning("Could not duplicate descriptor");
return false;
}
int errcode;
m_zipFile = zip_fdopen(fdcopy, 0, &errcode);
if (!m_zipFile) {
qWarning("Could not open file %s: error %d", qPrintable(archiveName), errcode);
return false;
}
// Parse the book descriptor file
if (!parseBookinfo()) {
return false;
}
return true;
}
void EBook_EPUB::close()
{
if (m_zipFile) {
zip_close(m_zipFile);
m_zipFile = nullptr;
}
// if ( m_epubFile.isOpen() )
// m_epubFile.close();
}
bool EBook_EPUB::getFileContentAsString(QString &str, const QUrl &url) const
{
return getFileAsString(str, urlToPath(url));
}
bool EBook_EPUB::getFileContentAsBinary(QByteArray &data, const QUrl &url) const
{
return getFileAsBinary(data, urlToPath(url));
}
bool EBook_EPUB::enumerateFiles(QList<QUrl> &files)
{
files = m_ebookManifest;
return true;
}
QString EBook_EPUB::title() const
{
return m_title;
}
QUrl EBook_EPUB::homeUrl() const
{
return m_tocEntries[0].url;
}
bool EBook_EPUB::hasFeature(EBook::Feature code) const
{
switch (code) {
case FEATURE_TOC:
return true;
case FEATURE_INDEX:
return false;
case FEATURE_ENCODING:
return false;
}
return false;
}
bool EBook_EPUB::getTableOfContents(QList<EBookTocEntry> &toc) const
{
toc = m_tocEntries;
return true;
}
bool EBook_EPUB::getIndex(QList<EBookIndexEntry> &) const
{
return false;
}
QString EBook_EPUB::getTopicByUrl(const QUrl &url)
{
if (m_urlTitleMap.contains(url)) {
return m_urlTitleMap[url];
}
return QLatin1String("");
}
QString EBook_EPUB::currentEncoding() const
{
return QStringLiteral("UTF-8");
}
bool EBook_EPUB::setCurrentEncoding(const char *)
{
abort();
}
bool EBook_EPUB::isSupportedUrl(const QUrl &url)
{
return url.scheme() == URL_SCHEME_EPUB;
}
bool EBook_EPUB::parseXML(const QString &uri, QXmlDefaultHandler *parser)
{
QByteArray container;
if (!getFileAsBinary(container, uri)) {
qDebug("Failed to retrieve XML file %s", qPrintable(uri));
return false;
}
// Use it as XML source
QXmlInputSource source;
source.setData(container);
// Init the reader
QXmlSimpleReader reader;
reader.setContentHandler(parser);
reader.setErrorHandler(parser);
return reader.parse(source);
}
bool EBook_EPUB::parseBookinfo()
{
// Parse the container.xml to find the content descriptor
HelperXmlHandler_EpubContainer container_parser;
if (!parseXML(QStringLiteral("META-INF/container.xml"), &container_parser) || container_parser.contentPath.isEmpty()) {
return false;
}
// Parse the content.opf
HelperXmlHandler_EpubContent content_parser;
if (!parseXML(container_parser.contentPath, &content_parser)) {
return false;
}
// At least title and the TOC must be present
if (!content_parser.metadata.contains(QStringLiteral("title")) || content_parser.tocname.isEmpty()) {
return false;
}
// All the files, including TOC, are relative to the container_parser.contentPath
m_documentRoot.clear();
int sep = container_parser.contentPath.lastIndexOf(QLatin1Char('/'));
if (sep != -1) {
m_documentRoot = container_parser.contentPath.left(sep + 1); // Keep the trailing slash
}
// Parse the TOC
HelperXmlHandler_EpubTOC toc_parser(this);
if (!parseXML(content_parser.tocname, &toc_parser)) {
return false;
}
// Get the data
m_title = content_parser.metadata[QStringLiteral("title")];
// Move the manifest entries into the list
for (const QString &f : std::as_const(content_parser.manifest)) {
m_ebookManifest.push_back(pathToUrl(f));
}
// Copy the manifest information and fill up the other maps if we have it
if (!toc_parser.entries.isEmpty()) {
for (const EBookTocEntry &e : std::as_const(toc_parser.entries)) {
// Add into url-title map
m_urlTitleMap[e.url] = e.name;
m_tocEntries.push_back(e);
}
} else {
// Copy them from spine
for (QString url : std::as_const(content_parser.spine)) {
EBookTocEntry e;
if (content_parser.manifest.contains(url)) {
url = content_parser.manifest[url];
}
e.name = url;
e.url = pathToUrl(url);
e.iconid = EBookTocEntry::IMAGE_NONE;
e.indent = 0;
// Add into url-title map
m_urlTitleMap[pathToUrl(url)] = url;
m_tocEntries.push_back(e);
}
}
// EPub with an empty TOC is not valid
if (m_tocEntries.isEmpty()) {
return false;
}
return true;
}
QUrl EBook_EPUB::pathToUrl(const QString &link) const
{
QUrl url;
url.setScheme(URL_SCHEME_EPUB);
url.setHost(URL_SCHEME_EPUB);
// Does the link contain the fragment as well?
int off = link.indexOf(QLatin1Char('#'));
QString path;
if (off != -1) {
path = link.left(off);
url.setFragment(link.mid(off + 1));
} else {
path = link;
}
if (!path.startsWith(QLatin1Char('/'))) {
path.prepend(QLatin1Char('/'));
}
url.setPath(QUrl::fromPercentEncoding(path.toUtf8()));
return url;
}
QString EBook_EPUB::urlToPath(const QUrl &link) const
{
if (link.scheme() == URL_SCHEME_EPUB) {
return link.path();
}
return QLatin1String("");
}
bool EBook_EPUB::getFileAsString(QString &str, const QString &path) const
{
QByteArray data;
if (!getFileAsBinary(data, path)) {
return false;
}
// I have never seen yet an UTF16 epub
if (data.startsWith("<?xml")) {
int endxmltag = data.indexOf("?>");
int utf16 = data.indexOf("UTF-16");
if (utf16 > 0 && utf16 < endxmltag) {
QMessageBox::critical(nullptr, i18n("Unsupported encoding"), i18n("The encoding of this ebook is not supported yet. Please open a bug in https://bugs.kde.org for support to be added"));
return false;
}
}
str = QString::fromUtf8(data);
return true;
}
bool EBook_EPUB::getFileAsBinary(QByteArray &data, const QString &path) const
{
// Retrieve the file size
struct zip_stat fileinfo;
QString completeUrl;
if (!path.isEmpty() && path[0] == QLatin1Char('/')) {
completeUrl = m_documentRoot + path.mid(1);
} else {
completeUrl = m_documentRoot + path;
}
// qDebug("URL requested: %s (%s)", qPrintable(path), qPrintable(completeUrl));
// http://www.nih.at/libzip/zip_stat.html
if (zip_stat(m_zipFile, completeUrl.toUtf8().constData(), 0, &fileinfo) != 0) {
qDebug("File %s is not found in the archive", qPrintable(completeUrl));
return false;
}
// Make sure the size field is valid
if ((fileinfo.valid & ZIP_STAT_SIZE) == 0 || (fileinfo.valid & ZIP_STAT_INDEX) == 0) {
return false;
}
// Open the file
struct zip_file *file = zip_fopen_index(m_zipFile, fileinfo.index, 0);
if (!file) {
return false;
}
// Allocate the memory and read the file
data.resize(fileinfo.size);
// Could it return a positive number but not fileinfo.size???
int ret = zip_fread(file, data.data(), fileinfo.size);
if (ret != (int)fileinfo.size) {
zip_fclose(file);
return false;
}
zip_fclose(file);
return true;
}

View file

@ -1,194 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef EBOOK_EPUB_H
#define EBOOK_EPUB_H
#include <QFile>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QUrl>
#include "ebook.h"
#include "zip.h"
class QXmlDefaultHandler;
class EBook_EPUB : public EBook
{
public:
EBook_EPUB();
~EBook_EPUB() override;
/*!
* \brief Attempts to load epub file.
* \param archiveName filename.
* \return EBook object on success, NULL on failure.
*
* Loads a epub file.
* \ingroup init
*/
bool load(const QString &archiveName) override;
/*!
* \brief Closes all the files, and frees the appropriate data.
* \ingroup init
*/
void close() override;
/*!
* \brief Gets the title name of the opened ebook.
* \return The name of the opened document, or an empty string if no ebook has been loaded.
* \ingroup information
*/
QString title() const override;
/*!
* \brief Gets the default URL of the e-book which should be opened when the book it first open
*
* \return The home page name, with a '/' added in front and relative to
* the root of the archive filesystem. If no book has been opened, returns "/".
* \ingroup information
*/
QUrl homeUrl() const override;
/*!
* \brief Checks whether the specific feature is present in this file.
* \return true if it is available; false otherwise.
* \ingroup information
*/
bool hasFeature(Feature code) const override;
/*!
* \brief Parses and fills up the Table of Contents (TOC)
* \param topics A pointer to the container which will store the parsed results.
* Will be cleaned before parsing.
* \return true if the tree is present and parsed successfully, false otherwise.
* The parser is built to be error-prone, however it still can abort with qFatal()
* by really buggy files; please report a bug if the file is opened ok under Windows.
* \ingroup fileparsing
*/
bool getTableOfContents(QList<EBookTocEntry> &toc) const override;
/*!
* \brief Parses the index table
* \param indexes A pointer to the container which will store the parsed results.
* Will be cleaned before parsing.
* \return true if the tree is present and parsed successfully, false otherwise.
* The parser is built to be error-prone, however it still can abort with qFatal()
* by really buggy chm file; so far it never happened on indexes.
* \ingroup fileparsing
*/
bool getIndex(QList<EBookIndexEntry> &index) const override;
/*!
* \brief Retrieves the content associated with the url from the current ebook as QString.
* \param str A string where the retreived content should be stored.
* \param url An URL in chm file to retreive content from. Must be absolute.
* \return true if the content is successfully received; false otherwise. Note content may be an empty string.
*
* This function retreives the file content (mostly for HTML pages) from the ebook. Because the content
* in chm file might not be stored in Unicode, it will be recoded according to current encoding.
* Do not use for binary data.
*
* \sa setCurrentEncoding() currentEncoding() getFileContentAsBinary()
* \ingroup dataretrieve
*/
bool getFileContentAsString(QString &str, const QUrl &url) const override;
/*!
* \brief Retrieves the content from url in current chm file to QByteArray.
* \param data A data array where the retreived content should be stored.
* \param url An URL in chm file to retreive content from. Must be absolute.
* \return true if the content is successfully received; false otherwise.
*
* This function retreives the file content from the chm archive opened by load()
* function. The content is not encoded.
*
* \sa getFileContentAsString()
* \ingroup dataretrieve
*/
bool getFileContentAsBinary(QByteArray &data, const QUrl &url) const override;
/*!
* \brief Obtains the list of all the files (URLs) in current ebook archive. This is used in search
* and to dump the e-book content.
* \param files An array to store list of URLs (file names) present in chm archive.
* \return true if the enumeration succeed; false otherwise (I could hardly imagine a reason).
*
* \ingroup dataretrieve
*/
bool enumerateFiles(QList<QUrl> &files) override;
/*!
* \brief Gets the Title of the page referenced by url.
* \param url An URL in ebook file to get title from. Must be absolute.
* \return The title, or QString() if the URL cannot be found or not a HTML page.
*
* \ingroup dataretrieve
*/
QString getTopicByUrl(const QUrl &url) override;
/*!
* \brief Gets the current ebook encoding (set or autodetected) as qtcodec
* \return The current encoding.
*
* \ingroup encoding
*/
QString currentEncoding() const override;
/*!
* \brief Sets the ebook encoding to use for TOC and content
* \param encoding An encoding to use.
*
* \ingroup encoding
*/
bool setCurrentEncoding(const char *encoding) override;
/*!
* \brief Checks if this kind of URL is supported by the ebook format (i.e. could be passed to ebook functions)
* \param url The url to check
*/
bool isSupportedUrl(const QUrl &url) override;
// Converts the string to the ebook-specific URL format
QUrl pathToUrl(const QString &link) const override;
// Extracts the path component from the URL
QString urlToPath(const QUrl &link) const override;
private:
// Parses the XML file using a specified parser
bool parseXML(const QString &uri, QXmlDefaultHandler *parser);
// Parses the book description file. Fills up the ebook info
bool parseBookinfo();
// Get file content from path
bool getFileAsString(QString &str, const QString &path) const;
bool getFileAsBinary(QByteArray &data, const QString &path) const;
// ZIP archive fd and structs
QFile m_epubFile;
struct zip *m_zipFile;
// Ebook info
QString m_title;
QString m_documentRoot;
// List of files in the ebook
QList<QUrl> m_ebookManifest;
// Table of contents
QList<EBookTocEntry> m_tocEntries;
// Map of URL-Title
QMap<QUrl, QString> m_urlTitleMap;
};
#endif // EBOOK_EPUB_H

View file

@ -1,213 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <QApplication>
#include "ebook.h"
#include "ebook_search.h"
// Helper class to simplicity state management and data keeping
class SearchDataKeeper
{
public:
SearchDataKeeper()
{
m_inPhrase = false;
}
void beginPhrase()
{
phrase_terms.clear();
m_inPhrase = true;
}
void endPhrase()
{
m_inPhrase = false;
phrasewords += phrase_terms;
phrases.push_back(phrase_terms.join(QStringLiteral(" ")));
}
bool isInPhrase() const
{
return m_inPhrase;
}
void addTerm(const QString &term)
{
if (!term.isEmpty()) {
terms.push_back(term);
if (m_inPhrase) {
phrase_terms.push_back(term);
}
}
}
// Should contain all the search terms present in query, includind those from phrases. One element - one term .
QStringList terms;
// Should contain phrases present in query without quotes. One element - one phrase.
QStringList phrases;
// Should contain all the terms present in all the phrases (but not outside).
QStringList phrasewords;
private:
bool m_inPhrase;
QStringList phrase_terms;
};
EBookSearch::EBookSearch()
{
m_Index = nullptr;
}
EBookSearch::~EBookSearch()
{
delete m_Index;
}
bool EBookSearch::loadIndex(QDataStream &stream)
{
delete m_Index;
m_Index = new QtAs::Index();
return m_Index->readDict(stream);
}
bool EBookSearch::generateIndex(EBook *ebookFile, QDataStream &stream)
{
QList<QUrl> documents;
QList<QUrl> alldocuments;
Q_EMIT progressStep(0, QStringLiteral("Generating the list of documents"));
processEvents();
// Enumerate the documents
if (!ebookFile->enumerateFiles(alldocuments)) {
return false;
}
if (m_Index) {
delete m_Index;
}
m_Index = new QtAs::Index();
connect(m_Index, &QtAs::Index::indexingProgress, this, &EBookSearch::updateProgress);
// Process the list of files in CHM archive and keep only HTML document files from there
for (const QUrl &allDocumentsI : std::as_const(alldocuments)) {
const QString docpath = allDocumentsI.path();
if (docpath.endsWith(QLatin1String(".html"), Qt::CaseInsensitive) || docpath.endsWith(QLatin1String(".htm"), Qt::CaseInsensitive) || docpath.endsWith(QLatin1String(".xhtml"), Qt::CaseInsensitive)) {
documents.push_back(allDocumentsI);
}
}
if (!m_Index->makeIndex(documents, ebookFile)) {
delete m_Index;
m_Index = nullptr;
return false;
}
m_Index->writeDict(stream);
m_keywordDocuments.clear();
return true;
}
void EBookSearch::cancelIndexGeneration()
{
m_Index->setLastWinClosed();
}
void EBookSearch::updateProgress(int value, const QString &stepName)
{
Q_EMIT progressStep(value, stepName);
}
void EBookSearch::processEvents()
{
// Do it up to ten times; some events generate other events
for (int i = 0; i < 10; i++) {
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
bool EBookSearch::searchQuery(const QString &query, QList<QUrl> *results, EBook *ebookFile, unsigned int limit)
{
// We should have index
if (!m_Index) {
return false;
}
// Characters which split the words. We need to make them separate tokens
QString splitChars = m_Index->getCharsSplit();
// Characters which are part of the word. We should keep them apart.
QString partOfWordChars = m_Index->getCharsPartOfWord();
// Variables to store current state
SearchDataKeeper keeper;
QString term;
for (const QChar &iChar : query) {
const QChar ch = iChar.toLower();
// a quote either begins or ends the phrase
if (ch == QLatin1Char('"')) {
keeper.addTerm(term);
if (keeper.isInPhrase()) {
keeper.endPhrase();
} else {
keeper.beginPhrase();
}
continue;
}
// If new char does not stop the word, add ot and continue
if (ch.isLetterOrNumber() || partOfWordChars.indexOf(ch) != -1) {
term.append(ch);
continue;
}
// If it is a split char, add this term and split char as separate term
if (splitChars.indexOf(ch) != -1) {
// Add existing term if present
keeper.addTerm(term);
// Change the term variable, so it will be added when we exit this block
term = ch;
}
// Just add the word; it is most likely a space or terminated by tokenizer.
keeper.addTerm(term);
term = QString();
}
keeper.addTerm(term);
if (keeper.isInPhrase()) {
return false;
}
QList<QUrl> foundDocs = m_Index->query(keeper.terms, keeper.phrases, keeper.phrasewords, ebookFile);
for (QList<QUrl>::iterator it = foundDocs.begin(); it != foundDocs.end() && limit > 0; ++it, limit--) {
results->push_back(*it);
}
return true;
}
bool EBookSearch::hasIndex() const
{
return m_Index != nullptr;
}

View file

@ -1,72 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef EBookSearch_H
#define EBookSearch_H
#include "helper_search_index.h"
#include <QDataStream>
class EBook;
class EBookSearch : public QObject
{
Q_OBJECT
public:
EBookSearch();
~EBookSearch() override;
//! Loads the search index from the data stream \param stream.
//! The index should be previously saved with generateIndex().
bool loadIndex(QDataStream &stream);
//! Generates the search index from the opened CHM file \param chmFile,
//! and saves it to the data stream \param stream which should be writeable.
//!
//! To show the progress, this procedure emits a progressStep() signal periodically
//! with the value showing current progress in percentage (i.e. from 0 to 100)
//! After signal emission, the following event processing function will be called:
//! qApp->processEvents( QEventLoop::ExcludeUserInputEvents )
//! to make sure the dialogs (if any) are properly updated.
//!
//! If \param progressDls is not null, it will be used to display progress.
//! Returns true if the index has been generated and saved, or false if internal
//! error occurs, or (most likely) the cancelIndexGeneration() slot has been called.
bool generateIndex(EBook *ebook, QDataStream &stream);
//! Executes the search query. The \param query is a string like <i>"C++ language" class</i>,
//! \param results is a pointer to QStringList, and \param limit limits the number of
//! results in case the query is too generic (like \a "a" ).
//! The \param ebookFile is used to get the current encoding information.
//! The return value is false only if the index is not generated, or if a closing quote character
//! is missing. Call hasIndex() to clarify. If search returns no results, the return value is
//! true, but the \param results list will be empty.
//!
//! Note that the function does not clear \param results before adding search results, so if you are
//! not merging search results, make sure it's empty.
bool searchQuery(const QString &query, QList<QUrl> *results, EBook *ebookFile, unsigned int limit = 100);
//! Returns true if a valid search index is present, and therefore search could be executed
bool hasIndex() const;
Q_SIGNALS:
void progressStep(int value, const QString &stepName);
public Q_SLOTS:
void cancelIndexGeneration();
private Q_SLOTS:
void updateProgress(int value, const QString &stepName);
void processEvents();
private:
QStringList m_keywordDocuments;
QtAs::Index *m_Index;
};
#endif

View file

@ -1,13 +0,0 @@
#ifndef EBOOK_URL_H
#define EBOOK_URL_H
#include <QUrl>
class EbookURL : public QUrl
{
public:
EbookURL();
EbookURL(const QString &url);
};
#endif // EBOOK_URL_H

View file

@ -1,211 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <QTextCodec>
#include "helper_entitydecoder.h"
HelperEntityDecoder::HelperEntityDecoder(QTextCodec *encoder)
{
changeEncoding(encoder);
}
static inline QString encodeWithCodec(QTextCodec *encoder, const QByteArray &str)
{
return (encoder ? encoder->toUnicode(str.constData()) : QString::fromUtf8(str));
}
void HelperEntityDecoder::changeEncoding(QTextCodec *encoder)
{
// Set up m_entityDecodeMap characters according to current textCodec
m_entityDecodeMap.clear();
m_entityDecodeMap[QStringLiteral("AElig")] = encodeWithCodec(encoder, "\306"); // capital AE diphthong (ligature)
m_entityDecodeMap[QStringLiteral("Aacute")] = encodeWithCodec(encoder, "\301"); // capital A, acute accent
m_entityDecodeMap[QStringLiteral("Acirc")] = encodeWithCodec(encoder, "\302"); // capital A, circumflex accent
m_entityDecodeMap[QStringLiteral("Agrave")] = encodeWithCodec(encoder, "\300"); // capital A, grave accent
m_entityDecodeMap[QStringLiteral("Aring")] = encodeWithCodec(encoder, "\305"); // capital A, ring
m_entityDecodeMap[QStringLiteral("Atilde")] = encodeWithCodec(encoder, "\303"); // capital A, tilde
m_entityDecodeMap[QStringLiteral("Auml")] = encodeWithCodec(encoder, "\304"); // capital A, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("Ccedil")] = encodeWithCodec(encoder, "\307"); // capital C, cedilla
m_entityDecodeMap[QStringLiteral("Dstrok")] = encodeWithCodec(encoder, "\320"); // whatever
m_entityDecodeMap[QStringLiteral("ETH")] = encodeWithCodec(encoder, "\320"); // capital Eth, Icelandic
m_entityDecodeMap[QStringLiteral("Eacute")] = encodeWithCodec(encoder, "\311"); // capital E, acute accent
m_entityDecodeMap[QStringLiteral("Ecirc")] = encodeWithCodec(encoder, "\312"); // capital E, circumflex accent
m_entityDecodeMap[QStringLiteral("Egrave")] = encodeWithCodec(encoder, "\310"); // capital E, grave accent
m_entityDecodeMap[QStringLiteral("Euml")] = encodeWithCodec(encoder, "\313"); // capital E, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("Iacute")] = encodeWithCodec(encoder, "\315"); // capital I, acute accent
m_entityDecodeMap[QStringLiteral("Icirc")] = encodeWithCodec(encoder, "\316"); // capital I, circumflex accent
m_entityDecodeMap[QStringLiteral("Igrave")] = encodeWithCodec(encoder, "\314"); // capital I, grave accent
m_entityDecodeMap[QStringLiteral("Iuml")] = encodeWithCodec(encoder, "\317"); // capital I, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("Ntilde")] = encodeWithCodec(encoder, "\321"); // capital N, tilde
m_entityDecodeMap[QStringLiteral("Oacute")] = encodeWithCodec(encoder, "\323"); // capital O, acute accent
m_entityDecodeMap[QStringLiteral("Ocirc")] = encodeWithCodec(encoder, "\324"); // capital O, circumflex accent
m_entityDecodeMap[QStringLiteral("Ograve")] = encodeWithCodec(encoder, "\322"); // capital O, grave accent
m_entityDecodeMap[QStringLiteral("Oslash")] = encodeWithCodec(encoder, "\330"); // capital O, slash
m_entityDecodeMap[QStringLiteral("Otilde")] = encodeWithCodec(encoder, "\325"); // capital O, tilde
m_entityDecodeMap[QStringLiteral("Ouml")] = encodeWithCodec(encoder, "\326"); // capital O, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("THORN")] = encodeWithCodec(encoder, "\336"); // capital THORN, Icelandic
m_entityDecodeMap[QStringLiteral("Uacute")] = encodeWithCodec(encoder, "\332"); // capital U, acute accent
m_entityDecodeMap[QStringLiteral("Ucirc")] = encodeWithCodec(encoder, "\333"); // capital U, circumflex accent
m_entityDecodeMap[QStringLiteral("Ugrave")] = encodeWithCodec(encoder, "\331"); // capital U, grave accent
m_entityDecodeMap[QStringLiteral("Uuml")] = encodeWithCodec(encoder, "\334"); // capital U, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("Yacute")] = encodeWithCodec(encoder, "\335"); // capital Y, acute accent
m_entityDecodeMap[QStringLiteral("OElig")] = encodeWithCodec(encoder, "\338"); // capital Y, acute accent
m_entityDecodeMap[QStringLiteral("oelig")] = encodeWithCodec(encoder, "\339"); // capital Y, acute accent
m_entityDecodeMap[QStringLiteral("aacute")] = encodeWithCodec(encoder, "\341"); // small a, acute accent
m_entityDecodeMap[QStringLiteral("acirc")] = encodeWithCodec(encoder, "\342"); // small a, circumflex accent
m_entityDecodeMap[QStringLiteral("aelig")] = encodeWithCodec(encoder, "\346"); // small ae diphthong (ligature)
m_entityDecodeMap[QStringLiteral("agrave")] = encodeWithCodec(encoder, "\340"); // small a, grave accent
m_entityDecodeMap[QStringLiteral("aring")] = encodeWithCodec(encoder, "\345"); // small a, ring
m_entityDecodeMap[QStringLiteral("atilde")] = encodeWithCodec(encoder, "\343"); // small a, tilde
m_entityDecodeMap[QStringLiteral("auml")] = encodeWithCodec(encoder, "\344"); // small a, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("ccedil")] = encodeWithCodec(encoder, "\347"); // small c, cedilla
m_entityDecodeMap[QStringLiteral("eacute")] = encodeWithCodec(encoder, "\351"); // small e, acute accent
m_entityDecodeMap[QStringLiteral("ecirc")] = encodeWithCodec(encoder, "\352"); // small e, circumflex accent
m_entityDecodeMap[QStringLiteral("Scaron")] = encodeWithCodec(encoder, "\352"); // small e, circumflex accent
m_entityDecodeMap[QStringLiteral("egrave")] = encodeWithCodec(encoder, "\350"); // small e, grave accent
m_entityDecodeMap[QStringLiteral("eth")] = encodeWithCodec(encoder, "\360"); // small eth, Icelandic
m_entityDecodeMap[QStringLiteral("euml")] = encodeWithCodec(encoder, "\353"); // small e, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("iacute")] = encodeWithCodec(encoder, "\355"); // small i, acute accent
m_entityDecodeMap[QStringLiteral("icirc")] = encodeWithCodec(encoder, "\356"); // small i, circumflex accent
m_entityDecodeMap[QStringLiteral("igrave")] = encodeWithCodec(encoder, "\354"); // small i, grave accent
m_entityDecodeMap[QStringLiteral("iuml")] = encodeWithCodec(encoder, "\357"); // small i, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("ntilde")] = encodeWithCodec(encoder, "\361"); // small n, tilde
m_entityDecodeMap[QStringLiteral("oacute")] = encodeWithCodec(encoder, "\363"); // small o, acute accent
m_entityDecodeMap[QStringLiteral("ocirc")] = encodeWithCodec(encoder, "\364"); // small o, circumflex accent
m_entityDecodeMap[QStringLiteral("ograve")] = encodeWithCodec(encoder, "\362"); // small o, grave accent
m_entityDecodeMap[QStringLiteral("oslash")] = encodeWithCodec(encoder, "\370"); // small o, slash
m_entityDecodeMap[QStringLiteral("otilde")] = encodeWithCodec(encoder, "\365"); // small o, tilde
m_entityDecodeMap[QStringLiteral("ouml")] = encodeWithCodec(encoder, "\366"); // small o, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("szlig")] = encodeWithCodec(encoder, "\337"); // small sharp s, German (sz ligature)
m_entityDecodeMap[QStringLiteral("thorn")] = encodeWithCodec(encoder, "\376"); // small thorn, Icelandic
m_entityDecodeMap[QStringLiteral("uacute")] = encodeWithCodec(encoder, "\372"); // small u, acute accent
m_entityDecodeMap[QStringLiteral("ucirc")] = encodeWithCodec(encoder, "\373"); // small u, circumflex accent
m_entityDecodeMap[QStringLiteral("ugrave")] = encodeWithCodec(encoder, "\371"); // small u, grave accent
m_entityDecodeMap[QStringLiteral("uuml")] = encodeWithCodec(encoder, "\374"); // small u, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("yacute")] = encodeWithCodec(encoder, "\375"); // small y, acute accent
m_entityDecodeMap[QStringLiteral("yuml")] = encodeWithCodec(encoder, "\377"); // small y, dieresis or umlaut mark
m_entityDecodeMap[QStringLiteral("iexcl")] = encodeWithCodec(encoder, "\241");
m_entityDecodeMap[QStringLiteral("cent")] = encodeWithCodec(encoder, "\242");
m_entityDecodeMap[QStringLiteral("pound")] = encodeWithCodec(encoder, "\243");
m_entityDecodeMap[QStringLiteral("curren")] = encodeWithCodec(encoder, "\244");
m_entityDecodeMap[QStringLiteral("yen")] = encodeWithCodec(encoder, "\245");
m_entityDecodeMap[QStringLiteral("brvbar")] = encodeWithCodec(encoder, "\246");
m_entityDecodeMap[QStringLiteral("sect")] = encodeWithCodec(encoder, "\247");
m_entityDecodeMap[QStringLiteral("uml")] = encodeWithCodec(encoder, "\250");
m_entityDecodeMap[QStringLiteral("ordf")] = encodeWithCodec(encoder, "\252");
m_entityDecodeMap[QStringLiteral("laquo")] = encodeWithCodec(encoder, "\253");
m_entityDecodeMap[QStringLiteral("not")] = encodeWithCodec(encoder, "\254");
m_entityDecodeMap[QStringLiteral("shy")] = encodeWithCodec(encoder, "\255");
m_entityDecodeMap[QStringLiteral("macr")] = encodeWithCodec(encoder, "\257");
m_entityDecodeMap[QStringLiteral("deg")] = encodeWithCodec(encoder, "\260");
m_entityDecodeMap[QStringLiteral("plusmn")] = encodeWithCodec(encoder, "\261");
m_entityDecodeMap[QStringLiteral("sup1")] = encodeWithCodec(encoder, "\271");
m_entityDecodeMap[QStringLiteral("sup2")] = encodeWithCodec(encoder, "\262");
m_entityDecodeMap[QStringLiteral("sup3")] = encodeWithCodec(encoder, "\263");
m_entityDecodeMap[QStringLiteral("acute")] = encodeWithCodec(encoder, "\264");
m_entityDecodeMap[QStringLiteral("micro")] = encodeWithCodec(encoder, "\265");
m_entityDecodeMap[QStringLiteral("para")] = encodeWithCodec(encoder, "\266");
m_entityDecodeMap[QStringLiteral("middot")] = encodeWithCodec(encoder, "\267");
m_entityDecodeMap[QStringLiteral("cedil")] = encodeWithCodec(encoder, "\270");
m_entityDecodeMap[QStringLiteral("ordm")] = encodeWithCodec(encoder, "\272");
m_entityDecodeMap[QStringLiteral("raquo")] = encodeWithCodec(encoder, "\273");
m_entityDecodeMap[QStringLiteral("frac14")] = encodeWithCodec(encoder, "\274");
m_entityDecodeMap[QStringLiteral("frac12")] = encodeWithCodec(encoder, "\275");
m_entityDecodeMap[QStringLiteral("frac34")] = encodeWithCodec(encoder, "\276");
m_entityDecodeMap[QStringLiteral("iquest")] = encodeWithCodec(encoder, "\277");
m_entityDecodeMap[QStringLiteral("times")] = encodeWithCodec(encoder, "\327");
m_entityDecodeMap[QStringLiteral("divide")] = encodeWithCodec(encoder, "\367");
m_entityDecodeMap[QStringLiteral("copy")] = encodeWithCodec(encoder, "\251"); // copyright sign
m_entityDecodeMap[QStringLiteral("reg")] = encodeWithCodec(encoder, "\256"); // registered sign
m_entityDecodeMap[QStringLiteral("nbsp")] = encodeWithCodec(encoder, "\240"); // non breaking space
m_entityDecodeMap[QStringLiteral("fnof")] = QChar((unsigned short)402);
m_entityDecodeMap[QStringLiteral("Delta")] = QChar((unsigned short)916);
m_entityDecodeMap[QStringLiteral("Pi")] = QChar((unsigned short)928);
m_entityDecodeMap[QStringLiteral("Sigma")] = QChar((unsigned short)931);
m_entityDecodeMap[QStringLiteral("beta")] = QChar((unsigned short)946);
m_entityDecodeMap[QStringLiteral("gamma")] = QChar((unsigned short)947);
m_entityDecodeMap[QStringLiteral("delta")] = QChar((unsigned short)948);
m_entityDecodeMap[QStringLiteral("eta")] = QChar((unsigned short)951);
m_entityDecodeMap[QStringLiteral("theta")] = QChar((unsigned short)952);
m_entityDecodeMap[QStringLiteral("lambda")] = QChar((unsigned short)955);
m_entityDecodeMap[QStringLiteral("mu")] = QChar((unsigned short)956);
m_entityDecodeMap[QStringLiteral("nu")] = QChar((unsigned short)957);
m_entityDecodeMap[QStringLiteral("pi")] = QChar((unsigned short)960);
m_entityDecodeMap[QStringLiteral("rho")] = QChar((unsigned short)961);
m_entityDecodeMap[QStringLiteral("lsquo")] = QChar((unsigned short)8216);
m_entityDecodeMap[QStringLiteral("rsquo")] = QChar((unsigned short)8217);
m_entityDecodeMap[QStringLiteral("rdquo")] = QChar((unsigned short)8221);
m_entityDecodeMap[QStringLiteral("bdquo")] = QChar((unsigned short)8222);
m_entityDecodeMap[QStringLiteral("trade")] = QChar((unsigned short)8482);
m_entityDecodeMap[QStringLiteral("ldquo")] = QChar((unsigned short)8220);
m_entityDecodeMap[QStringLiteral("ndash")] = QChar((unsigned short)8211);
m_entityDecodeMap[QStringLiteral("mdash")] = QChar((unsigned short)8212);
m_entityDecodeMap[QStringLiteral("bull")] = QChar((unsigned short)8226);
m_entityDecodeMap[QStringLiteral("hellip")] = QChar((unsigned short)8230);
m_entityDecodeMap[QStringLiteral("emsp")] = QChar((unsigned short)8195);
m_entityDecodeMap[QStringLiteral("rarr")] = QChar((unsigned short)8594);
m_entityDecodeMap[QStringLiteral("rArr")] = QChar((unsigned short)8658);
m_entityDecodeMap[QStringLiteral("crarr")] = QChar((unsigned short)8629);
m_entityDecodeMap[QStringLiteral("le")] = QChar((unsigned short)8804);
m_entityDecodeMap[QStringLiteral("ge")] = QChar((unsigned short)8805);
m_entityDecodeMap[QStringLiteral("lte")] = QChar((unsigned short)8804); // wrong, but used somewhere
m_entityDecodeMap[QStringLiteral("gte")] = QChar((unsigned short)8805); // wrong, but used somewhere
m_entityDecodeMap[QStringLiteral("dagger")] = QChar((unsigned short)8224);
m_entityDecodeMap[QStringLiteral("Dagger")] = QChar((unsigned short)8225);
m_entityDecodeMap[QStringLiteral("euro")] = QChar((unsigned short)8364);
m_entityDecodeMap[QStringLiteral("asymp")] = QChar((unsigned short)8776);
m_entityDecodeMap[QStringLiteral("isin")] = QChar((unsigned short)8712);
m_entityDecodeMap[QStringLiteral("notin")] = QChar((unsigned short)8713);
m_entityDecodeMap[QStringLiteral("prod")] = QChar((unsigned short)8719);
m_entityDecodeMap[QStringLiteral("ne")] = QChar((unsigned short)8800);
m_entityDecodeMap[QStringLiteral("amp")] = QStringLiteral("&"); // ampersand
m_entityDecodeMap[QStringLiteral("gt")] = QStringLiteral(">"); // greater than
m_entityDecodeMap[QStringLiteral("lt")] = QStringLiteral("<"); // less than
m_entityDecodeMap[QStringLiteral("quot")] = QStringLiteral("\""); // double quote
m_entityDecodeMap[QStringLiteral("apos")] = QStringLiteral("'"); // single quote
m_entityDecodeMap[QStringLiteral("frasl")] = QStringLiteral("/");
m_entityDecodeMap[QStringLiteral("minus")] = QStringLiteral("-");
m_entityDecodeMap[QStringLiteral("oplus")] = QStringLiteral("+");
m_entityDecodeMap[QStringLiteral("Prime")] = QStringLiteral("\"");
}
QString HelperEntityDecoder::decode(const QString &entity) const
{
// If entity is an ASCII code like &#12349; - just decode it
if (entity.isEmpty()) {
return QLatin1String("");
} else if (entity[0] == QLatin1Char('#')) {
bool valid;
unsigned int ascode = QStringView {entity}.mid(1).toUInt(&valid);
if (!valid) {
qWarning("HelperEntityDecoder::decode: could not decode HTML entity '%s'", qPrintable(entity));
return QString();
}
return (QString)(QChar(ascode));
} else {
QMap<QString, QString>::const_iterator it = m_entityDecodeMap.find(entity);
if (it == m_entityDecodeMap.end()) {
qWarning("HelperEntityDecoder::decode: could not decode HTML entity '%s'", qPrintable(entity));
return QLatin1String("");
}
return *it;
}
}

View file

@ -1,34 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef HELPER_ENTITYDECODER_H
#define HELPER_ENTITYDECODER_H
#include <QMap>
#include <QString>
//
// This helper class decodes the Unicode HTML entities into the Unicode characters
//
class HelperEntityDecoder
{
public:
// Initialization with the specific decoder
explicit HelperEntityDecoder(QTextCodec *encoder = nullptr);
// Used when the encoding changes
void changeEncoding(QTextCodec *encoder = nullptr);
// The decoder function
QString decode(const QString &entity) const;
private:
// Map to decode HTML entitles like &acute; based on current encoding, initialized upon the first use
QMap<QString, QString> m_entityDecodeMap;
};
#endif // HELPER_ENTITYDECODER_H

View file

@ -1,460 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <QApplication>
#include <QTextCodec>
#include "ebook.h"
#include "ebook_search.h"
#include "helper_search_index.h"
static const int DICT_VERSION = 4;
namespace QtAs
{
// Those characters are splitters (i.e. split the word), but added themselves into dictionary too.
// This makes the dictionary MUCH larger, but ensure that for the piece of "window->print" both
// search for "print" and "->print" will find it.
#define SPLIT_CHARACTERS QStringLiteral("!()*&^%#@[]{}':;,.?/|/?<>\\-+=~`")
// Those characters are parts of word - for example, '_' is here, and search for _debug will find only _debug.
#define WORD_CHARACTERS QStringLiteral("$_")
struct Term {
Term()
: frequency(-1)
{
}
Term(const QString &t, int f, const QVector<Document> &l)
: term(t)
, frequency(f)
, documents(l)
{
}
QString term;
int frequency;
QVector<Document> documents;
bool operator<(const Term &i2) const
{
return frequency < i2.frequency;
}
};
QDataStream &operator>>(QDataStream &s, Document &l)
{
s >> l.docNumber;
s >> l.frequency;
return s;
}
QDataStream &operator<<(QDataStream &s, const Document l)
{
s << (short)l.docNumber;
s << (short)l.frequency;
return s;
}
Index::Index()
: QObject(nullptr)
{
lastWindowClosed = false;
connect(qApp, &QGuiApplication::lastWindowClosed, this, &Index::setLastWinClosed);
}
void Index::setLastWinClosed()
{
lastWindowClosed = true;
}
bool Index::makeIndex(const QList<QUrl> &docs, EBook *chmFile)
{
if (docs.isEmpty()) {
return false;
}
docList = docs;
if (chmFile->hasFeature(EBook::FEATURE_ENCODING)) {
entityDecoder.changeEncoding(QTextCodec::codecForName(chmFile->currentEncoding().toUtf8()));
}
QList<QUrl>::ConstIterator it = docList.constBegin();
int steps = docList.count() / 100;
if (!steps) {
steps++;
}
int prog = 0;
for (int i = 0; it != docList.constEnd(); ++it, ++i) {
if (lastWindowClosed) {
return false;
}
const QUrl &filename = *it;
QStringList terms;
if (parseDocumentToStringlist(chmFile, filename, terms)) {
for (QStringList::ConstIterator tit = terms.constBegin(); tit != terms.constEnd(); ++tit) {
insertInDict(*tit, i);
}
}
if (i % steps == 0) {
prog++;
prog = qMin(prog, 99);
Q_EMIT indexingProgress(prog, tr("Processing document %1").arg((*it).path()));
}
}
Q_EMIT indexingProgress(100, tr("Processing completed"));
return true;
}
void Index::insertInDict(const QString &str, int docNum)
{
Entry *e = nullptr;
if (!dict.isEmpty()) {
e = dict[str];
}
if (e) {
if (e->documents.last().docNumber != docNum) {
e->documents.append(Document(docNum, 1));
} else {
e->documents.last().frequency++;
}
} else {
dict.insert(str, new Entry(docNum));
}
}
bool Index::parseDocumentToStringlist(EBook *chmFile, const QUrl &filename, QStringList &tokenlist)
{
QString parsedbuf, parseentity, text;
if (!chmFile->getFileContentAsString(text, filename) || text.isEmpty()) {
qWarning("Search index generator: could not retrieve the document content for %s", qPrintable(filename.toString()));
return false;
}
m_charssplit = SPLIT_CHARACTERS;
m_charsword = WORD_CHARACTERS;
tokenlist.clear();
// State machine states
enum state_t {
STATE_OUTSIDE_TAGS, // outside HTML tags; parse text
STATE_IN_HTML_TAG, // inside HTML tags; wait for end tag
STATE_IN_QUOTES, // inside HTML tags and inside quotes; wait for end quote (in var QuoteChar)
STATE_IN_HTML_ENTITY // inside HTML entity; parse the entity
};
state_t state = STATE_OUTSIDE_TAGS;
QChar QuoteChar; // used in STATE_IN_QUOTES
for (int j = 0; j < text.length(); j++) {
QChar ch = text[j];
if ((j % 20000) == 0) {
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
}
if (state == STATE_IN_HTML_TAG) {
// We are inside HTML tag.
// Ignore everything until we see '>' (end of HTML tag) or quote char (quote start)
if (ch == QLatin1Char('"') || ch == QLatin1Char('\'')) {
state = STATE_IN_QUOTES;
QuoteChar = ch;
} else if (ch == QLatin1Char('>')) {
state = STATE_OUTSIDE_TAGS;
}
continue;
} else if (state == STATE_IN_QUOTES) {
// We are inside quoted text inside HTML tag.
// Ignore everything until we see the quote character again
if (ch == QuoteChar) {
state = STATE_IN_HTML_TAG;
}
continue;
} else if (state == STATE_IN_HTML_ENTITY) {
// We are inside encoded HTML entity (like &nbsp;).
// Collect to parsedbuf everything until we see ;
if (ch.isLetterOrNumber()) {
// get next character of this entity
parseentity.append(ch);
continue;
}
// The entity ended
state = STATE_OUTSIDE_TAGS;
// Some shitty HTML does not terminate entities correctly. Screw it.
if (ch != QLatin1Char(';') && ch != QLatin1Char('<')) {
if (parseentity.isEmpty()) {
// straight '&' symbol. Add and continue.
parsedbuf += QLatin1String("&");
} else {
qWarning("Index::parseDocument: incorrectly terminated HTML entity '&%s%c', ignoring", qPrintable(parseentity), ch.toLatin1());
}
j--; // parse this character again, but in different state
continue;
}
// Don't we have a space?
if (parseentity.toLower() != QLatin1String("nbsp")) {
QString entity = entityDecoder.decode(parseentity);
if (entity.isNull()) {
// decodeEntity() already printed error message
// qWarning( "Index::parseDocument: failed to decode entity &%s;", parsedbuf.ascii() );
continue;
}
parsedbuf += entity;
continue;
} else {
ch = QLatin1Char(' '); // We got a space, so treat it like it, and not add it to parsebuf
}
}
//
// Now process STATE_OUTSIDE_TAGS
//
// Check for start of HTML tag, and switch to STATE_IN_HTML_TAG if it is
if (ch == QLatin1Char('<')) {
state = STATE_IN_HTML_TAG;
goto tokenize_buf;
}
// Check for start of HTML entity
if (ch == QLatin1Char('&')) {
state = STATE_IN_HTML_ENTITY;
parseentity = QString();
continue;
}
// Replace quote by ' - quotes are used in search window to set the phrase
if (ch == QLatin1Char('"')) {
ch = QLatin1Char('\'');
}
// Ok, we have a valid character outside HTML tags, and probably some in buffer already.
// If it is char or letter, add it and continue
if (ch.isLetterOrNumber() || m_charsword.indexOf(ch) != -1) {
parsedbuf.append(ch);
continue;
}
// If it is a split char, add the word to the dictionary, and then add the char itself.
if (m_charssplit.indexOf(ch) != -1) {
if (!parsedbuf.isEmpty()) {
tokenlist.push_back(parsedbuf.toLower());
}
tokenlist.push_back(ch.toLower());
parsedbuf = QString();
continue;
}
tokenize_buf:
// Just add the word; it is most likely a space or terminated by tokenizer.
if (!parsedbuf.isEmpty()) {
tokenlist.push_back(parsedbuf.toLower());
parsedbuf = QString();
}
}
// Add the last word if still here - for broken htmls.
if (!parsedbuf.isEmpty()) {
tokenlist.push_back(parsedbuf.toLower());
}
return true;
}
void Index::writeDict(QDataStream &stream)
{
stream << DICT_VERSION;
stream << m_charssplit;
stream << m_charsword;
// Document list
stream << docList;
// Dictionary
for (QHash<QString, Entry *>::ConstIterator it = dict.constBegin(); it != dict.constEnd(); ++it) {
stream << it.key();
stream << (int)it.value()->documents.count();
stream << it.value()->documents;
}
}
bool Index::readDict(QDataStream &stream)
{
dict.clear();
docList.clear();
QString key;
int version, numOfDocs;
stream >> version;
if (version < 2) {
return false;
}
stream >> m_charssplit;
stream >> m_charsword;
// Read the document list
stream >> docList;
while (!stream.atEnd()) {
stream >> key;
stream >> numOfDocs;
QVector<Document> docs(numOfDocs);
stream >> docs;
dict.insert(key, new Entry(docs));
}
return dict.size() > 0;
}
QList<QUrl> Index::query(const QStringList &terms, const QStringList &termSeq, const QStringList &seqWords, EBook *chmFile)
{
QList<Term> termList;
for (const auto &term : terms) {
Entry *e = nullptr;
if (dict[term]) {
e = dict[term];
termList.append(Term(term, e->documents.count(), e->documents));
} else {
return QList<QUrl>();
}
}
if (termList.isEmpty()) {
return QList<QUrl>();
}
std::sort(termList.begin(), termList.end());
QVector<Document> minDocs = termList.takeFirst().documents;
for (const Term &t : std::as_const(termList)) {
const QVector<Document> docs = t.documents;
for (QVector<Document>::Iterator minDoc_it = minDocs.begin(); minDoc_it != minDocs.end();) {
bool found = false;
for (QVector<Document>::ConstIterator doc_it = docs.constBegin(); doc_it != docs.constEnd(); ++doc_it) {
if ((*minDoc_it).docNumber == (*doc_it).docNumber) {
(*minDoc_it).frequency += (*doc_it).frequency;
found = true;
break;
}
}
if (!found) {
minDoc_it = minDocs.erase(minDoc_it);
} else {
++minDoc_it;
}
}
}
QList<QUrl> results;
std::sort(minDocs.begin(), minDocs.end());
if (termSeq.isEmpty()) {
for (const Document &doc : std::as_const(minDocs)) {
results << docList.at((int)doc.docNumber);
}
return results;
}
QUrl fileName;
for (const Document &doc : std::as_const(minDocs)) {
fileName = docList[(int)doc.docNumber];
if (searchForPhrases(termSeq, seqWords, fileName, chmFile)) {
results << fileName;
}
}
return results;
}
bool Index::searchForPhrases(const QStringList &phrases, const QStringList &words, const QUrl &filename, EBook *chmFile)
{
QStringList parsed_document;
if (!parseDocumentToStringlist(chmFile, filename, parsed_document)) {
return false;
}
miniDict.clear();
// Initialize the dictionary with the words in phrase(s)
for (const QString &word : words) {
miniDict.insert(word, new PosEntry(0));
}
// Fill the dictionary with the words from the document
unsigned int word_offset = 3;
for (QStringList::ConstIterator it = parsed_document.constBegin(); it != parsed_document.constEnd(); it++, word_offset++) {
PosEntry *entry = miniDict[*it];
if (entry) {
entry->positions.append(word_offset);
}
}
// Dump it
/*
QDictIterator<PosEntry> it( miniDict );
for( ; it.current(); ++it )
{
QString text( it.currentKey() );
QValueList<uint> pos = miniDict[text]->positions;
for ( unsigned int i = 1; i < pos.size(); i++ )
text += " " + QString::number( pos[i] );
qDebug( "%s", text.ascii());
}
*/
QList<uint> first_word_positions;
for (QStringList::ConstIterator phrase_it = phrases.constBegin(); phrase_it != phrases.constEnd(); phrase_it++) {
QStringList phrasewords = phrase_it->split(QLatin1Char(' '));
first_word_positions = miniDict[phrasewords[0]]->positions;
for (int j = 1; j < phrasewords.count(); ++j) {
QList<uint> next_word_it = miniDict[phrasewords[j]]->positions;
QList<uint>::iterator dict_it = first_word_positions.begin();
while (dict_it != first_word_positions.end()) {
if (next_word_it.indexOf(*dict_it + 1) != -1) {
(*dict_it)++;
++dict_it;
} else {
dict_it = first_word_positions.erase(dict_it);
}
}
}
}
return !first_word_positions.isEmpty();
}
};

View file

@ -1,135 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef EBOOK_SEARCH_INDEX_H
#define EBOOK_SEARCH_INDEX_H
#include <QDataStream>
#include <QHash>
#include <QStringList>
#include <QUrl>
#include <QVector>
#include "helper_entitydecoder.h"
class EBook;
// This code is based on some pretty old version of Qt Assistant
namespace QtAs
{
struct Document {
Document(int d, int f)
: docNumber(d)
, frequency(f)
{
}
Document()
: docNumber(-1)
, frequency(0)
{
}
bool operator==(const Document doc) const
{
return docNumber == doc.docNumber;
}
bool operator<(const Document doc) const
{
return frequency > doc.frequency;
}
bool operator<=(const Document doc) const
{
return frequency >= doc.frequency;
}
bool operator>(const Document doc) const
{
return frequency < doc.frequency;
}
qint16 docNumber;
qint16 frequency;
};
QDataStream &operator>>(QDataStream &s, Document &l);
QDataStream &operator<<(QDataStream &s, const Document l);
class Index : public QObject
{
Q_OBJECT
public:
Index();
void writeDict(QDataStream &stream);
bool readDict(QDataStream &stream);
bool makeIndex(const QList<QUrl> &docs, EBook *chmFile);
QList<QUrl> query(const QStringList &, const QStringList &, const QStringList &, EBook *chmFile);
QString getCharsSplit() const
{
return m_charssplit;
}
QString getCharsPartOfWord() const
{
return m_charsword;
}
Q_SIGNALS:
void indexingProgress(int, const QString &);
public Q_SLOTS:
void setLastWinClosed();
private:
struct Entry {
explicit Entry(int d)
{
documents.append(Document(d, 1));
}
explicit Entry(const QVector<Document> &l)
: documents(l)
{
}
QVector<Document> documents;
};
struct PosEntry {
explicit PosEntry(int p)
{
positions.append(p);
}
QList<uint> positions;
};
bool parseDocumentToStringlist(EBook *chmFile, const QUrl &filename, QStringList &tokenlist);
void insertInDict(const QString &, int);
QStringList getWildcardTerms(const QString &);
QStringList split(const QString &);
QList<Document> setupDummyTerm(const QStringList &);
bool searchForPhrases(const QStringList &phrases, const QStringList &words, const QUrl &filename, EBook *chmFile);
QList<QUrl> docList;
QHash<QString, Entry *> dict;
QHash<QString, PosEntry *> miniDict;
bool lastWindowClosed;
HelperEntityDecoder entityDecoder;
// Those characters are splitters (i.e. split the word), but added themselves into dictionary too.
// This makes the dictionary MUCH larger, but ensure that for the piece of "window->print" both
// search for "print" and "->print" will find it.
QString m_charssplit;
// Those characters are parts of word - for example, '_' is here, and search for _debug will find only _debug.
QString m_charsword;
};
};
Q_DECLARE_TYPEINFO(QtAs::Document, Q_MOVABLE_TYPE);
#endif // EBOOK_SEARCH_INDEX_H

View file

@ -1,23 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "helperxmlhandler_epubcontainer.h"
bool HelperXmlHandler_EpubContainer::startElement(const QString &, const QString &, const QString &qName, const QXmlAttributes &atts)
{
if (qName == QLatin1String("rootfile")) {
int idx = atts.index(QLatin1String("full-path"));
if (idx == -1) {
return false;
}
contentPath = atts.value(idx);
}
return true;
}

View file

@ -1,23 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef HELPERXMLHANDLER_EPUBCONTAINER_H
#define HELPERXMLHANDLER_EPUBCONTAINER_H
#include <QXmlDefaultHandler>
class HelperXmlHandler_EpubContainer : public QXmlDefaultHandler
{
public:
// Overridden members
bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts) override;
// The content path
QString contentPath;
};
#endif // HELPERXMLHANDLER_EPUBCONTAINER_H

View file

@ -1,81 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "helperxmlhandler_epubcontent.h"
HelperXmlHandler_EpubContent::HelperXmlHandler_EpubContent()
{
m_state = STATE_NONE;
}
bool HelperXmlHandler_EpubContent::startElement(const QString &, const QString &localName, const QString &, const QXmlAttributes &atts)
{
// <metadata> tag contains the medatada which goes into m_metadata
if (localName == QLatin1String("metadata")) {
m_state = STATE_IN_METADATA;
} else if (localName == QLatin1String("manifest")) {
m_state = STATE_IN_MANIFEST;
} else if (localName == QLatin1String("spine")) {
m_state = STATE_IN_SPINE;
} else if (m_state == STATE_IN_METADATA) { // we don't need to store the first 'metadata' here
// Now handle the states
m_tagname = localName;
} else if (m_state == STATE_IN_MANIFEST && localName == QLatin1String("item")) {
int idx_id = atts.index(QLatin1String("id"));
int idx_href = atts.index(QLatin1String("href"));
int idx_mtype = atts.index(QLatin1String("media-type"));
if (idx_id == -1 || idx_href == -1 || idx_mtype == -1) {
return false;
}
manifest[atts.value(idx_id)] = atts.value(idx_href);
if (atts.value(idx_mtype) == QLatin1String("application/x-dtbncx+xml")) {
tocname = atts.value(idx_href);
}
// qDebug() << "MANIFEST: " << atts.value( idx_id ) << "->" << atts.value( idx_href );
} else if (m_state == STATE_IN_SPINE && localName == QLatin1String("itemref")) {
int idx = atts.index(QLatin1String("idref"));
if (idx == -1) {
return false;
}
spine.push_back(atts.value(idx));
// qDebug() << "SPINE: " << atts.value( idx );
}
return true;
}
bool HelperXmlHandler_EpubContent::characters(const QString &ch)
{
if (m_state == STATE_IN_METADATA && !m_tagname.isEmpty() && !ch.trimmed().isEmpty()) {
// Some metadata may be duplicated; we concantenate them with |
if (metadata.contains(m_tagname)) {
metadata[m_tagname].append(QStringLiteral("|"));
metadata[m_tagname].append(ch.trimmed());
} else {
metadata[m_tagname] = ch.trimmed();
}
// qDebug() << "METATAG: " << m_tagname << " " << metadata[ m_tagname ];
}
return true;
}
bool HelperXmlHandler_EpubContent::endElement(const QString &, const QString &, const QString &qName)
{
if (qName == QLatin1String("manifest") || qName == QLatin1String("metadata") || qName == QLatin1String("spine")) {
m_state = STATE_NONE;
}
return true;
}

View file

@ -1,44 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef HELPERXMLHANDLER_EPUBCONTENT_H
#define HELPERXMLHANDLER_EPUBCONTENT_H
#include <QMap>
#include <QString>
#include <QXmlDefaultHandler>
class HelperXmlHandler_EpubContent : public QXmlDefaultHandler
{
public:
HelperXmlHandler_EpubContent();
// Keep the tag-associated metadata
QMap<QString, QString> metadata;
// Manifest storage, id -> href
QMap<QString, QString> manifest;
// Spline storage
QList<QString> spine;
// TOC (NCX) filename
QString tocname;
private:
enum State { STATE_NONE, STATE_IN_METADATA, STATE_IN_MANIFEST, STATE_IN_SPINE };
bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts) override;
bool characters(const QString &ch) override;
bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName) override;
// Tracking
State m_state;
QString m_tagname;
};
#endif // HELPERXMLHANDLER_EPUBCONTENT_H

View file

@ -1,104 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "helperxmlhandler_epubtoc.h"
#include <QtDebug>
HelperXmlHandler_EpubTOC::HelperXmlHandler_EpubTOC(EBook_EPUB *epub)
{
m_epub = epub;
m_inNavMap = false;
m_inText = false;
m_indent = 0;
}
bool HelperXmlHandler_EpubTOC::startElement(const QString &, const QString &localName, const QString &, const QXmlAttributes &atts)
{
// qDebug() << "startElement " << " " << localName;
// for ( int i = 0; i < atts.count(); i++ )
// qDebug() << " " << atts.localName(i) << " " << atts.value(i);
if (localName == QLatin1String("navMap")) {
m_inNavMap = true;
return true;
}
if (!m_inNavMap) {
return true;
}
if (localName == QLatin1String("navPoint")) {
m_indent++;
}
if (localName == QLatin1String("text")) {
m_inText = true;
}
if (localName == QLatin1String("content")) {
int idx = atts.index(QLatin1String("src"));
if (idx == -1) {
return false;
}
m_lastId = atts.value(idx);
checkNewTocEntry();
}
return true;
}
bool HelperXmlHandler_EpubTOC::characters(const QString &ch)
{
// qDebug() << "characters" << " " << ch;
if (m_inText) {
m_lastTitle = ch;
}
checkNewTocEntry();
return true;
}
bool HelperXmlHandler_EpubTOC::endElement(const QString &, const QString &localName, const QString &)
{
// qDebug() << "endElement" << " " << qName;
if (localName == QLatin1String("navMap")) {
m_inNavMap = false;
return true;
}
if (localName == QLatin1String("navPoint")) {
m_indent--;
}
if (localName == QLatin1String("text")) {
m_inText = false;
}
return true;
}
void HelperXmlHandler_EpubTOC::checkNewTocEntry()
{
if (!m_lastId.isEmpty() && !m_lastTitle.isEmpty()) {
EBookTocEntry entry;
entry.name = m_lastTitle;
entry.url = m_epub->pathToUrl(m_lastId);
entry.iconid = EBookTocEntry::IMAGE_AUTO;
entry.indent = m_indent - 1;
entries.push_back(entry);
// qDebug() << "TOC entry: " << m_lastId << " :" << m_lastTitle << " :" << m_indent - 1;
m_lastId.clear();
m_lastTitle.clear();
}
}

View file

@ -1,36 +0,0 @@
/*
Kchmviewer - a CHM and EPUB file viewer with broad language support
SPDX-FileCopyrightText: 2004-2014 George Yunaev gyunaev@ulduzsoft.com
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef HELPERXMLHANDLER_EPUBTOC_H
#define HELPERXMLHANDLER_EPUBTOC_H
#include "ebook_epub.h"
#include <QXmlDefaultHandler>
class HelperXmlHandler_EpubTOC : public QXmlDefaultHandler
{
public:
explicit HelperXmlHandler_EpubTOC(EBook_EPUB *epub);
QList<EBookTocEntry> entries;
private:
// Overridden members
bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts) override;
bool characters(const QString &ch) override;
bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName) override;
void checkNewTocEntry();
bool m_inNavMap;
bool m_inText;
unsigned int m_indent;
QString m_lastId;
QString m_lastTitle;
EBook_EPUB *m_epub;
};
#endif // HELPERXMLHANDLER_EPUBTOC_H

View file

@ -1,97 +0,0 @@
/*
SPDX-FileCopyrightText: 2004-2007 Georgy Yunaev gyunaev@ulduzsoft.com
Please do not use email address above for bug reports; see
the README file
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QDir>
#include <QRegularExpression>
#include <QString>
namespace LCHMUrlFactory
{
static inline bool isRemoteURL(const QString &url, QString &protocol)
{
// Check whether the URL is external
QRegularExpression uriregex(QStringLiteral("^(\\w+):\\/\\/"));
QRegularExpressionMatch match;
// mailto: can also have different format, so handle it
if (url.startsWith(QLatin1String("mailto:"))) {
protocol = QStringLiteral("mailto");
return true;
} else if ((match = uriregex.match(url)).hasMatch()) {
const QString proto = match.captured(1).toLower();
// Filter the URLs which need to be opened by a browser
if (proto == QLatin1String("http") || proto == QLatin1String("ftp") || proto == QLatin1String("mailto") || proto == QLatin1String("news")) {
protocol = proto;
return true;
}
}
return false;
}
// Some JS urls start with javascript://
static inline bool isJavascriptURL(const QString &url)
{
return url.startsWith(QLatin1String("javascript://"));
}
// Parse urls like "ms-its:file name.chm::/topic.htm"
static inline bool isNewChmURL(const QString &url, QString &chmfile, QString &page)
{
QRegularExpression uriregex(QStringLiteral("^ms-its:(.*)::(.*)$"));
uriregex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = uriregex.match(url);
if (match.hasMatch()) {
chmfile = match.captured(1);
page = match.captured(2);
return true;
}
return false;
}
static inline QString makeURLabsoluteIfNeeded(const QString &url)
{
QString p1, p2, newurl = url;
if (!isRemoteURL(url, p1) && !isJavascriptURL(url) && !isNewChmURL(url, p1, p2)) {
newurl = QDir::cleanPath(url);
// Normalize url, so it becomes absolute
if (newurl[0] != QLatin1Char('/')) {
newurl = QLatin1Char('/') + newurl;
}
}
// qDebug ("makeURLabsolute (%s) -> (%s)", url.ascii(), newurl.ascii());
return newurl;
}
// Returns a special string, which allows the kio-slave, or viewwindow-browser interaction
// to recognize our own internal urls, which is necessary to show image-only pages.
static inline QString getInternalUriExtension()
{
return QStringLiteral(".KCHMVIEWER_SPECIAL_HANDLER");
}
static inline bool handleFileType(const QString &link, QString &generated)
{
QString intext = getInternalUriExtension();
if (!link.endsWith(intext)) {
return false;
}
QString filelink = link.left(link.length() - intext.length());
generated = QStringLiteral("<html><body><img src=\"") + filelink + QStringLiteral("\"></body></html>");
return true;
}
}

View file

@ -1,270 +0,0 @@
{
"KPlugin": {
"Authors": [
{
"Email": "niedakh@gmail.com",
"Name": "Piotr Szymański",
"Name[ar]": "Piotr Szymański",
"Name[az]": "Piotr Szymański",
"Name[be]": "Piotr Szymański",
"Name[bg]": "Piotr Szymański",
"Name[ca@valencia]": "Piotr Szymański",
"Name[ca]": "Piotr Szymański",
"Name[cs]": "Piotr Szymański",
"Name[da]": "Piotr Szymański",
"Name[de]": "Piotr Szymański",
"Name[el]": "Piotr Szymański",
"Name[en_GB]": "Piotr Szymański",
"Name[eo]": "Piotr Szymański",
"Name[es]": "Piotr Szymański",
"Name[et]": "Piotr Szymański",
"Name[eu]": "Piotr Szymański",
"Name[fi]": "Piotr Szymański",
"Name[fr]": "Piotr Szymański",
"Name[gl]": "Piotr Szymański",
"Name[he]": "פיוטר ז׳ימנסקי",
"Name[hu]": "Piotr Szymański",
"Name[ia]": "Piotr Szymański",
"Name[ie]": "Piotr Szymański",
"Name[is]": "Piotr Szymański",
"Name[it]": "Piotr Szymański",
"Name[ka]": "Piotr Szymański",
"Name[ko]": "Piotr Szymański",
"Name[lt]": "Piotr Szymański",
"Name[lv]": "Piotr Szymański",
"Name[nl]": "Piotr Szymański",
"Name[nn]": "Piotr Szymański",
"Name[pl]": "Piotr Szymański",
"Name[pt]": "Piotr Szymański",
"Name[pt_BR]": "Piotr Szymański",
"Name[ro]": "Piotr Szymański",
"Name[ru]": "Piotr Szymański",
"Name[sk]": "Piotr Szymański",
"Name[sl]": "Piotr Szymański",
"Name[sr@ijekavian]": "Пјотр Шимањски",
"Name[sr@ijekavianlatin]": "Pjotr Šimanjski",
"Name[sr@latin]": "Pjotr Šimanjski",
"Name[sr]": "Пјотр Шимањски",
"Name[sv]": "Piotr Szymański",
"Name[ta]": "பியோதுர் சிமான்ஸுகீ",
"Name[tr]": "Piotr Szymański",
"Name[uk]": "Piotr Szymański",
"Name[vi]": "Piotr Szymański",
"Name[x-test]": "xxPiotr Szymańskixx",
"Name[zh_CN]": "Piotr Szymański",
"Name[zh_TW]": "Piotr Szymański"
},
{
"Email": "aacid@kde.org",
"Name": "Albert Astals Cid",
"Name[ar]": "Albert Astals Cid",
"Name[az]": "Albert Astals Cid",
"Name[be]": "Albert Astals Cid",
"Name[bg]": "Albert Astals Cid",
"Name[ca@valencia]": "Albert Astals Cid",
"Name[ca]": "Albert Astals Cid",
"Name[cs]": "Albert Astals Cid",
"Name[da]": "Albert Astals Cid",
"Name[de]": "Albert Astals Cid",
"Name[el]": "Albert Astals Cid",
"Name[en_GB]": "Albert Astals Cid",
"Name[eo]": "Albert Astals Cid",
"Name[es]": "Albert Astals Cid",
"Name[et]": "Albert Astals Cid",
"Name[eu]": "Albert Astals Cid",
"Name[fi]": "Albert Astals Cid",
"Name[fr]": "Albert Astals Cid",
"Name[gl]": "Albert Astals Cid",
"Name[he]": "אלברט אסטלס סיד",
"Name[hu]": "Albert Astals Cid",
"Name[ia]": "Albert Astals Cid",
"Name[ie]": "Albert Astals Cid",
"Name[is]": "Albert Astals Cid",
"Name[it]": "Albert Astals Cid",
"Name[ka]": "Albert Astals Cid",
"Name[ko]": "Albert Astals Cid",
"Name[lt]": "Albert Astals Cid",
"Name[lv]": "Albert Astals Cid",
"Name[nl]": "Albert Astals Cid",
"Name[nn]": "Albert Astals Cid",
"Name[pa]": "ਅਲਬਰਟ ਅਸਟਾਲਸ ਸਿਡ",
"Name[pl]": "Albert Astals Cid",
"Name[pt]": "Albert Astals Cid",
"Name[pt_BR]": "Albert Astals Cid",
"Name[ro]": "Albert Astals Cid",
"Name[ru]": "Albert Astals Cid",
"Name[sk]": "Albert Astals Cid",
"Name[sl]": "Albert Astals Cid",
"Name[sr@ijekavian]": "Алберт Асталс Сид",
"Name[sr@ijekavianlatin]": "Albert Astals Sid",
"Name[sr@latin]": "Albert Astals Sid",
"Name[sr]": "Алберт Асталс Сид",
"Name[sv]": "Albert Astals Cid",
"Name[ta]": "ஆல்பர்டு அஸ்டால்சு சிடு",
"Name[tr]": "Albert Astals Cid",
"Name[uk]": "Albert Astals Cid",
"Name[vi]": "Albert Astals Cid",
"Name[x-test]": "xxAlbert Astals Cidxx",
"Name[zh_CN]": "Albert Astals Cid",
"Name[zh_TW]": "Albert Astals Cid"
}
],
"Copyright": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ar]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[az]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[be]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[bg]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ca@valencia]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ca]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[cs]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[da]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[de]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[el]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[en_GB]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[eo]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[es]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[et]": "© 2005-2007: Piotr Szymański\n© 2008: Albert Astals Cid",
"Copyright[eu]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[fi]": "© 20052007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[fr]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[gl]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[he]": "© 2005-2007 פיוטר ז׳ימנסקי\n© 2008 אלברט אסטלס סיד",
"Copyright[hu]": "© Piotr Szymański, 2005-2007.\n© Albert Astals Cid, 2008.",
"Copyright[ia]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ie]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[is]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[it]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ka]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ko]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[lt]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[lv]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[nl]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[nn]": "© 20052007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[pa]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[pl]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[pt]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[pt_BR]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ro]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[ru]": "© Piotr Szymański, 2005-2007\n© Albert Astals Cid, 2008",
"Copyright[sk]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[sl]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[sr@ijekavian]": "© 2005—2007, Пјотр Шимањски\n© 2008, Алберт Асталс Сид",
"Copyright[sr@ijekavianlatin]": "© 2005—2007, Pjotr Šimanjski\n© 2008, Albert Astals Sid",
"Copyright[sr@latin]": "© 2005—2007, Pjotr Šimanjski\n© 2008, Albert Astals Sid",
"Copyright[sr]": "© 2005—2007, Пјотр Шимањски\n© 2008, Алберт Асталс Сид",
"Copyright[sv]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[tr]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[uk]": "© Piotr Szymański, 20052007\n© Albert Astals Cid, 2008",
"Copyright[vi]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[x-test]": "xx© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cidxx",
"Copyright[zh_CN]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Copyright[zh_TW]": "© 2005-2007 Piotr Szymański\n© 2008 Albert Astals Cid",
"Description": "A Microsoft Windows help file renderer",
"Description[ar]": "عارض مستندات مساعدة ويندوز",
"Description[az]": "Microsoft Windows köməkçi fayl tərtibatçısı",
"Description[be]": "Сродак візуалізацыі файла даведкі Microsoft Windows",
"Description[bg]": "Визуализатор на Microsoft Windows help",
"Description[ca@valencia]": "Un renderitzador de fitxers d'ajuda de Microsoft Windows",
"Description[ca]": "Un renderitzador de fitxers d'ajuda de Microsoft Windows",
"Description[cs]": "Nástroj pro zobrazování nápovědy Microsoft Windows",
"Description[da]": "En gengiver til Microsoft Windows-hjælpfiler",
"Description[de]": "Darstellungsprogramm für Hilfedateien aus Microsoft Windows",
"Description[el]": "Πρόγραμμα αποτύπωσης για αρχεία βοήθειας Microsoft Windows",
"Description[en_GB]": "A Microsoft Windows help file renderer",
"Description[eo]": "Mikrosofto Vindozo helpdosierigilo",
"Description[es]": "Un visor de archivos de ayuda de Microsoft Windows",
"Description[et]": "Microsoft Windowsi abifailide renderdaja",
"Description[eu]": "Microsoft Windows laguntza fitxategien errendatzaile bat",
"Description[fi]": "Microsoft Windows -ohjetiedostohahmonnin",
"Description[fr]": "Un système de rendu pour les fichiers d'aide Windows",
"Description[gl]": "Un visor de ficheiros de axuda de Microsoft Windows.",
"Description[he]": "מעבד קובצי עזרה של Microsoft Windows",
"Description[hu]": "Microsoft Windows súgófájl-leképező",
"Description[ia]": "Un rendition de file de adjuta de Microsoft Windows",
"Description[is]": "Myndunarviðbót fyrir Microsoft Windows hjálparskrár",
"Description[it]": "Un visualizzatore per file di aiuto di Microsoft Windows",
"Description[ka]": "Microsoft Windows -ის დახმარების ფაილის რენდერერი",
"Description[ko]": "Microsoft Windows 도움말 파일 표시기",
"Description[lt]": "Microsoft Windows pagalbos failų atvaizdavimas",
"Description[lv]": "„Microsoft Windows“ palīdzības datņu atveidotājs",
"Description[nl]": "Een viewer voor Microsoft Windows-helpbestanden",
"Description[nn]": "Ein framvisar for Microsoft Windows-hjelpefiler",
"Description[pl]": "Wyświetlanie plików pomocy Microsoft Windows",
"Description[pt]": "Um visualizador de ficheiros de ajuda do Microsoft Windows",
"Description[pt_BR]": "Um interpretador de arquivos de ajuda do Microsoft Windows",
"Description[ro]": "Randor pentru fișiere cu ajutor Microsoft Windows",
"Description[ru]": "Модуль поддержки формата CHM (справка Microsoft Windows)",
"Description[sk]": "Vykresľovanie súborov pomocníka Microsoft Windows",
"Description[sl]": "Izrisovalnik datotek za pomoč za Microsoft Windows",
"Description[sr@ijekavian]": "Рендерер за фајлове Виндоузове помоћи",
"Description[sr@ijekavianlatin]": "Renderer za fajlove Windowsove pomoći",
"Description[sr@latin]": "Renderer za fajlove Windowsove pomoći",
"Description[sr]": "Рендерер за фајлове Виндоузове помоћи",
"Description[sv]": "Ett återgivningsprogram av Microsoft Windows hjälpfiler",
"Description[tr]": "Microsoft Windows yardım dosyası sunucu",
"Description[uk]": "Відображення файлів довідки Microsoft Windows",
"Description[vi]": "Một trình kết xuất tệp trợ giúp Windows Microsoft",
"Description[x-test]": "xxA Microsoft Windows help file rendererxx",
"Description[zh_CN]": "Microsoft Windows 帮助文件的渲染程序",
"Description[zh_TW]": "Microsoft Windows 說明檔成像器",
"License": "GPL",
"MimeTypes": [
"application/x-chm"
],
"Name": "CHM Backend",
"Name[ar]": "خلفية CHM",
"Name[az]": "CHM formatı dəstəkləmə modulu",
"Name[be]": "Рухавік CHM",
"Name[bg]": "Бекенд на CHM",
"Name[ca@valencia]": "Dorsal CHM",
"Name[ca]": "Dorsal CHM",
"Name[cs]": "Podpora CHM",
"Name[da]": "CHM-backend",
"Name[de]": "Anzeigemodul für CHM",
"Name[el]": "Σύστημα υποστήριξης CHM",
"Name[en_GB]": "CHM Backend",
"Name[eo]": "CHM-Backend",
"Name[es]": "Motor para CHM",
"Name[et]": "CHM-i taustaprogramm",
"Name[eu]": "CHM-ren bizkarraldekoa",
"Name[fi]": "CHM-taustaosa",
"Name[fr]": "Moteur CHM",
"Name[gl]": "Motor de CHM",
"Name[he]": "מנגנון CHM",
"Name[hu]": "CHM modul",
"Name[ia]": "Retro-Administration de CHM",
"Name[ie]": "Infrastructura CHM",
"Name[is]": "CHM-bakendi",
"Name[it]": "Backend CHM",
"Name[ka]": "CHM უკანაბოლო",
"Name[ko]": "CHM 백엔드",
"Name[lt]": "CHM programinė sąsaja",
"Name[lv]": "CHM aizmugursistēma",
"Name[nl]": "CHM-backend",
"Name[nn]": "CHM-motor",
"Name[pa]": "CHM ਬੈਕਐਂਡ",
"Name[pl]": "Obsługa CHM",
"Name[pt]": "Infra-Estrutura de CHM",
"Name[pt_BR]": "Infraestrutura CHM",
"Name[ro]": "Platformă CHM",
"Name[ru]": "Модуль поддержки формата CHM",
"Name[sk]": "CHM Backend",
"Name[sl]": "Zaledje za CHM",
"Name[sr@ijekavian]": "Позадина за ЦХМ",
"Name[sr@ijekavianlatin]": "Pozadina za CHM",
"Name[sr@latin]": "Pozadina za CHM",
"Name[sr]": "Позадина за ЦХМ",
"Name[sv]": "CHM-gränssnitt",
"Name[ta]": "CHM பின்நிலை",
"Name[tr]": "CHM Arka Ucu",
"Name[uk]": "Модуль CHM",
"Name[vi]": "Hậu phương CHM",
"Name[x-test]": "xxCHM Backendxx",
"Name[zh_CN]": "CHM 后端程序",
"Name[zh_TW]": "CHM 後端介面",
"Version": "0.1.4"
},
"X-KDE-Priority": 2,
"X-KDE-okularAPIVersion": 1,
"X-KDE-okularHasInternalSettings": false
}

View file

@ -1,257 +0,0 @@
[Desktop Entry]
MimeType=application/x-chm;
Terminal=false
Name=Okular
Name[ar]=اوكلار
Name[az]=Okular
Name[be]=Okular
Name[bg]=Okular
Name[bs]=Okular
Name[ca]=Okular
Name[ca@valencia]=Okular
Name[cs]=Okular
Name[da]=Okular
Name[de]=Okular
Name[el]=Okular
Name[en_GB]=Okular
Name[eo]=Okular
Name[es]=Okular
Name[et]=Okular
Name[eu]=Okular
Name[fi]=Okular
Name[fr]=Okular
Name[ga]=Okular
Name[gl]=Okular
Name[he]=Okular
Name[hne]=
Name[hr]=Okular
Name[hu]=Okular
Name[ia]=Okular
Name[ie]=Okular
Name[is]=Okular
Name[it]=Okular
Name[ja]=Okular
Name[ka]=Okular
Name[kk]=Okular
Name[km]=Okular
Name[ko]=Okular
Name[ku]=Okular
Name[lt]=Okular
Name[lv]=Okular
Name[mr]=
Name[nb]=Okular
Name[nds]=Okular
Name[nl]=Okular
Name[nn]=Okular
Name[pa]=
Name[pl]=Okular
Name[pt]=Okular
Name[pt_BR]=Okular
Name[ro]=Okular
Name[ru]=Okular
Name[si]=Okular
Name[sk]=Okular
Name[sl]=Okular
Name[sq]=Okular
Name[sr]=Окулар
Name[sr@ijekavian]=Окулар
Name[sr@ijekavianlatin]=Okular
Name[sr@latin]=Okular
Name[sv]=Okular
Name[ta]=
Name[th]=
Name[tr]=Okular
Name[ug]=Okular
Name[uk]=Okular
Name[vi]=Okular
Name[x-test]=xxOkularxx
Name[zh_CN]=Okular
Name[zh_TW]=Okular
GenericName=Document Viewer
GenericName[ar]=عارض المستندات
GenericName[az]=Sənədə baxış vasitısi
GenericName[be]=Сродак для прагляду дакументаў
GenericName[bg]=Преглед на документи
GenericName[bs]=Prikazivač dokumenata
GenericName[ca]=Visualitzador de documents
GenericName[ca@valencia]=Visor de documents
GenericName[cs]=Prohlížeč dokumentů
GenericName[da]=Dokumentfremviser
GenericName[de]=Dokumentenbetrachter
GenericName[el]=Προβολέας εγγράφων
GenericName[en_GB]=Document Viewer
GenericName[eo]=Dokumenta rigardilo
GenericName[es]=Visor de documentos
GenericName[et]=Dokumendinäitaja
GenericName[eu]=Dokumentu erakuslea
GenericName[fa]=مشاهدهگر سند
GenericName[fi]=Asiakirjakatselin
GenericName[fr]=Afficheur de documents
GenericName[ga]=Amharcán Cáipéisí
GenericName[gl]=Visor de documentos
GenericName[he]=מציג מסמכים
GenericName[hi]=
GenericName[hne]=
GenericName[hr]=Preglednik dokumenata
GenericName[hu]=Dokumentummegjelenítő
GenericName[ia]=Visor de documento
GenericName[ie]=Visor de documentes
GenericName[is]=Skjalaskoðari
GenericName[it]=Visore di documenti
GenericName[ja]=
GenericName[ka]=
GenericName[kk]=Құжатты қарау құралы
GenericName[km]=
GenericName[ko]=
GenericName[ku]=Nîşanderê Belgeyan
GenericName[lt]=Dokumentų žiūryklė
GenericName[lv]=Dokumentu skatītājs
GenericName[mr]=
GenericName[nb]=Dokumentviser
GenericName[nds]=Dokmentkieker
GenericName[ne]=
GenericName[nl]=Documentenviewer
GenericName[nn]=Dokumentvisar
GenericName[oc]=Visualizaire de documents
GenericName[pa]=
GenericName[pl]=Przeglądarka dokumentów
GenericName[pt]=Visualizador de Documentos
GenericName[pt_BR]=Visualizador de documentos
GenericName[ro]=Vizualizor de documente
GenericName[ru]=Просмотр документов
GenericName[sk]=Prehliadač dokumentov
GenericName[sl]=Pregledovalnik dokumentov
GenericName[sq]=Shikues dokumentesh
GenericName[sr]=Приказивач докумената
GenericName[sr@ijekavian]=Приказивач докумената
GenericName[sr@ijekavianlatin]=Prikazivač dokumenata
GenericName[sr@latin]=Prikazivač dokumenata
GenericName[sv]=Dokumentvisare
GenericName[ta]= ி
GenericName[th]=
GenericName[tr]=Belge Görüntüleyicisi
GenericName[ug]=پۈتۈك كۆرگۈ
GenericName[uk]=Переглядач документів
GenericName[vi]=Trình xem tài liu
GenericName[x-test]=xxDocument Viewerxx
GenericName[zh_CN]=
GenericName[zh_TW]=
Comment=Universal document viewer
Comment[ar]=عارض المستندات عالمي
Comment[az]=Sənədə universal baxış vasitəsi
Comment[be]=Універсальны сродак для прагляду дакументаў
Comment[bg]=Универсална програма за преглед на документи
Comment[ca]=Visualitzador universal de documents
Comment[ca@valencia]=Visor universal de documents
Comment[cs]=Univerzální prohlížeč dokumentů
Comment[da]=Universel dokumentfremviser
Comment[de]=Universeller Dokumentenbetrachter
Comment[el]=Καθολικός προβολέας εγγράφων
Comment[en_GB]=Universal document viewer
Comment[eo]=Universala dokumenta spektilo
Comment[es]=Visor de documentos universal
Comment[et]=Universaalne dokumendinäitaja
Comment[eu]=Dokumentu erakusle unibertsala
Comment[fi]=Yleinen asiakirjakatselin
Comment[fr]=Afficheur de document universel
Comment[gl]=Visor de documentos universal.
Comment[he]=מציג מסמכים אוניברסלי
Comment[hu]=Univerzális dokumentummegjelenítő
Comment[ia]=Visor de documento universal
Comment[ie]=Universal visor de documentes
Comment[is]=Fjölhæfur skjalaskoðari
Comment[it]=Visore di documenti universale
Comment[ka]=
Comment[ko]=
Comment[lt]=Universali dokumentų žiūryklė
Comment[lv]=Universāls dokumentu skatītājs
Comment[nl]=Universele documentviewer
Comment[nn]=Dokumentvisar for mange format
Comment[pa]=
Comment[pl]=Wszechstronna przeglądarka dokumentów
Comment[pt]=Visualizador de documentos universal
Comment[pt_BR]=Visualizador de documentos universal
Comment[ro]=Vizualizor de documente universal
Comment[ru]=Универсальная программа просмотра документов
Comment[sk]=Univerzálny prehliadač dokumentov
Comment[sl]=Vsestranski pregledovalnik dokumentov
Comment[sq]=Shikues univerzal dokumentesh
Comment[sr]=Универзални приказивач докумената
Comment[sr@ijekavian]=Универзални приказивач докумената
Comment[sr@ijekavianlatin]=Univerzalni prikazivač dokumenata
Comment[sr@latin]=Univerzalni prikazivač dokumenata
Comment[sv]=Generell dokumentvisare
Comment[ta]= ி
Comment[tr]=Çok amaçlı belge görüntüleyicisi
Comment[uk]=Універсальний переглядач документів
Comment[vi]=Trình xem tài liu vn năng
Comment[x-test]=xxUniversal document viewerxx
Comment[zh_CN]=
Comment[zh_TW]=
Exec=okular %U
Icon=okular
Type=Application
InitialPreference=3
Categories=Qt;KDE;Graphics;Viewer;
NoDisplay=true
X-KDE-Keywords=chm
X-KDE-Keywords[ar]=chm
X-KDE-Keywords[az]=chm
X-KDE-Keywords[be]=chm
X-KDE-Keywords[bg]=chm
X-KDE-Keywords[bs]=chm
X-KDE-Keywords[ca]=chm
X-KDE-Keywords[ca@valencia]=chm
X-KDE-Keywords[cs]=chm
X-KDE-Keywords[da]=chm
X-KDE-Keywords[de]=chm
X-KDE-Keywords[el]=chm
X-KDE-Keywords[en_GB]=chm
X-KDE-Keywords[eo]=chm
X-KDE-Keywords[es]=chm
X-KDE-Keywords[et]=chm
X-KDE-Keywords[eu]=chm
X-KDE-Keywords[fi]=chm
X-KDE-Keywords[fr]=chm
X-KDE-Keywords[ga]=chm
X-KDE-Keywords[gl]=chm
X-KDE-Keywords[he]=chm
X-KDE-Keywords[hu]=chm
X-KDE-Keywords[ia]=chm
X-KDE-Keywords[ie]=chm
X-KDE-Keywords[is]=chm
X-KDE-Keywords[it]=chm
X-KDE-Keywords[ja]=chm
X-KDE-Keywords[ka]=chm
X-KDE-Keywords[kk]=chm
X-KDE-Keywords[km]=chm
X-KDE-Keywords[ko]=chm
X-KDE-Keywords[lt]=chm
X-KDE-Keywords[lv]=chm
X-KDE-Keywords[mr]=chm
X-KDE-Keywords[nb]=chm
X-KDE-Keywords[nds]=CHM
X-KDE-Keywords[nl]=chm
X-KDE-Keywords[nn]=chm
X-KDE-Keywords[pa]=chm
X-KDE-Keywords[pl]=chm
X-KDE-Keywords[pt]=chm
X-KDE-Keywords[pt_BR]=chm
X-KDE-Keywords[ro]=chm
X-KDE-Keywords[ru]=chm
X-KDE-Keywords[sk]=chm
X-KDE-Keywords[sl]=chm
X-KDE-Keywords[sq]=chm
X-KDE-Keywords[sr]=chm,ЦХМ
X-KDE-Keywords[sr@ijekavian]=chm,ЦХМ
X-KDE-Keywords[sr@ijekavianlatin]=chm,CHM
X-KDE-Keywords[sr@latin]=chm,CHM
X-KDE-Keywords[sv]=chm
X-KDE-Keywords[ta]=chm
X-KDE-Keywords[tr]=chm
X-KDE-Keywords[uk]=chm
X-KDE-Keywords[vi]=chm
X-KDE-Keywords[x-test]=xxchmxx
X-KDE-Keywords[zh_CN]=chm
X-KDE-Keywords[zh_TW]=chm
X-KDE-AliasFor=org.kde.okular.desktop

View file

@ -1,250 +0,0 @@
[Desktop Entry]
Name=Reader
Name[ar]=التصيير
Name[az]=Oxuyucu
Name[be]=Сродак чытання
Name[bg]=Четец
Name[bs]=Čitač
Name[ca]=Lector
Name[ca@valencia]=Lector
Name[cs]=Čtečka
Name[da]=Læser
Name[de]=Lesegerät
Name[el]=Πρόγραμμα ανάγνωσης
Name[en_GB]=Reader
Name[eo]=Legilo
Name[es]=Lector
Name[et]=Lugeja
Name[eu]=Irakurlea
Name[fi]=Lukija
Name[fr]=Lecteur
Name[ga]=Léitheoir
Name[gl]=Lector
Name[he]=קורא
Name[hu]=Olvasó
Name[ia]=Lector
Name[ie]=Letor
Name[is]=Lesari
Name[it]=Lettore
Name[ka]=
Name[kk]=Оқу құралы
Name[ko]=
Name[lt]=Skaitytuvas
Name[lv]=Lasītājs
Name[mr]=
Name[nb]=Leser
Name[nds]=Leser
Name[nl]=Lezer
Name[nn]=Lesar
Name[pa]=
Name[pl]=Czytnik
Name[pt]=Leitor
Name[pt_BR]=Leitor
Name[ro]=Cititor
Name[ru]=Просмотрщик
Name[sk]=Čítačka
Name[sl]=Bralnik
Name[sq]=Reader
Name[sr]=Читач
Name[sr@ijekavian]=Читач
Name[sr@ijekavianlatin]=Čitač
Name[sr@latin]=Čitač
Name[sv]=Läsprogram
Name[ta]=ி
Name[tr]=Okuyucu
Name[ug]=ئوقۇغۇ
Name[uk]=Переглядач
Name[vi]=Trình đc
Name[x-test]=xxReaderxx
Name[zh_CN]=
Name[zh_TW]=
GenericName=Document viewer
GenericName[ar]=عارض المستندات
GenericName[az]=Sənədə baxış vasitısi
GenericName[be]=Сродак для прагляду дакументаў
GenericName[bg]=Преглед на документи
GenericName[bs]=Prikazivač dokumenata
GenericName[ca]=Visualitzador de documents
GenericName[ca@valencia]=Visor de documents
GenericName[cs]=Prohlížeč dokumentů
GenericName[da]=Dokumentfremviser
GenericName[de]=Dokumentenbetrachter
GenericName[el]=Προβολέας εγγράφων
GenericName[en_GB]=Document Viewer
GenericName[eo]=Dokumentrigardilo
GenericName[es]=Visor de documentos
GenericName[et]=Dokumendinäitaja
GenericName[eu]=Dokumentu erakuslea
GenericName[fi]=Asiakirjakatselin
GenericName[fr]=Afficheur de document
GenericName[ga]=Amharcán cáipéisí
GenericName[gl]=Visor de documentos
GenericName[he]=מציג מסמכים
GenericName[hi]=
GenericName[hu]=Dokumentummegjelenítő
GenericName[ia]=Visor de documento
GenericName[ie]=Visor de documentes
GenericName[is]=Skjalaskoðari
GenericName[it]=Visore di documenti
GenericName[ja]=
GenericName[ka]=
GenericName[kk]=Құжатты қарау құралы
GenericName[ko]=
GenericName[lt]=Dokumentų žiūryklė
GenericName[lv]=Dokumentu skatītājs
GenericName[mr]=
GenericName[nb]=Dokumentviser
GenericName[nds]=Dokmentkieker
GenericName[nl]=Documentenviewer
GenericName[nn]=Dokumentvisar
GenericName[pa]=
GenericName[pl]=Przeglądarka dokumentów
GenericName[pt]=Visualizador de documentos
GenericName[pt_BR]=Visualizador de documentos
GenericName[ro]=Vizualizor de documente
GenericName[ru]=Просмотр документов
GenericName[sk]=Prehliadač dokumentov
GenericName[sl]=Pregledovalnik dokumentov
GenericName[sq]=Shikues dokumentesh
GenericName[sr]=Приказивач докумената
GenericName[sr@ijekavian]=Приказивач докумената
GenericName[sr@ijekavianlatin]=Prikazivač dokumenata
GenericName[sr@latin]=Prikazivač dokumenata
GenericName[sv]=Dokumentvisare
GenericName[ta]= ி
GenericName[tr]=Belge görüntüleyicisi
GenericName[uk]=Переглядач документів
GenericName[vi]=Trình xem tài liu
GenericName[x-test]=xxDocument viewerxx
GenericName[zh_CN]=
GenericName[zh_TW]=
Comment=Viewer for various types of documents
Comment[ar]=عارض للعديد من أنواع المستندات
Comment[az]=Müxtəlif növlü sənədlər üçün görüntüləyici
Comment[be]=Сродак для прагляду разнастайных тыпаў дакументаў
Comment[bg]=Преглед на различни видове документи
Comment[bs]=Pregledač raznih vrsta dokumenata
Comment[ca]=Visualitzador de diversos tipus de documents
Comment[ca@valencia]=Visor de diversos tipus de documents
Comment[cs]=Prohlížeč různých typů dokumentů
Comment[da]=Fremviser af diverse dokumenttyper
Comment[de]=Betrachter für verschiedene Arten von Dokumenten
Comment[el]=Πρόγραμμα προβολής για διάφορους τύπους εγγράφων
Comment[en_GB]=Viewer for various types of documents
Comment[eo]=Vidilo por diversaj specoj de dokumentoj
Comment[es]=Visor de diversos tipos de documentos
Comment[et]=Eri tüüpi dokumentide näitaja
Comment[eu]=Hainbat dokumentu moten erakuslea
Comment[fi]=Monenlaisten asiakirjojen katseluohjelma
Comment[fr]=Afficheur pour différents types de documents
Comment[ga]=Amharcán le haghaidh cáipéisí éagsúla
Comment[gl]=Visor de varios tipos de documentos.
Comment[he]=מציג למגוון סוגי מסמכים
Comment[hu]=Megjelenítő különféle típusú dokumentumokhoz
Comment[ia]=Visor pro varie typos de documento
Comment[ie]=Visor por varie tipes de documentes
Comment[is]=Skoðari fyrir ýmsar gerðir skjala
Comment[it]=Visore per vari tipi di documenti
Comment[ka]=
Comment[kk]=Түрлі құжаттар қарау құралы
Comment[ko]=
Comment[lt]=Žiūryklė įvairiems dokumentų tipams
Comment[lv]=Dažādu dokumentu tipu skatītājs
Comment[mr]=िि
Comment[nb]=Framviser for forskjellige dokumenttyper
Comment[nds]=Kieker för en Reeg Dokmenttypen
Comment[nl]=Viewer voor verschillende typen documenten
Comment[nn]=Framvisar for forskjellige dokumenttypar
Comment[pa]= ਿ
Comment[pl]=Przeglądarka dla różnych typów dokumentów
Comment[pt]=Visualizador de vários tipos de documentos
Comment[pt_BR]=Visualizador para vários tipos de documentos
Comment[ro]=Vizualizor pentru diferite tipuri de documente
Comment[ru]=Программа для просмотра различных типов документов
Comment[sk]=Prehliadač pre rôzne typy dokumentov
Comment[sl]=Pregledovalnik raznih vrst dokumentov
Comment[sq]=Shikues për lloje të ndryshme të dokumentave
Comment[sr]=Приказивач различитих врста докумената
Comment[sr@ijekavian]=Приказивач различитих врста докумената
Comment[sr@ijekavianlatin]=Prikazivač različitih vrsta dokumenata
Comment[sr@latin]=Prikazivač različitih vrsta dokumenata
Comment[sv]=Visningsprogram för diverse typer av dokument
Comment[ta]= ி
Comment[tr]=Çeşitli belge türleri için görüntüleyici
Comment[ug]=ھەر خىل تىپتىكى پۈتۈكلەرنى كۆرىدىغان پروگرامما
Comment[uk]=Програма для перегляду документів різних типів
Comment[vi]=Trình xem các kiu tài liu khác nhau
Comment[x-test]=xxViewer for various types of documentsxx
Comment[zh_CN]=
Comment[zh_TW]=
TryExec=okularkirigami
Exec=okularkirigami %u
Terminal=false
Icon=okular
Type=Application
Categories=Qt;KDE;Graphics;Office;Viewer;
InitialPreference=2
NoDisplay=true
MimeType=application/x-chm;
X-KDE-Keywords=chm
X-KDE-Keywords[ar]=chm
X-KDE-Keywords[az]=chm
X-KDE-Keywords[be]=chm
X-KDE-Keywords[bg]=chm
X-KDE-Keywords[bs]=chm
X-KDE-Keywords[ca]=chm
X-KDE-Keywords[ca@valencia]=chm
X-KDE-Keywords[cs]=chm
X-KDE-Keywords[da]=chm
X-KDE-Keywords[de]=chm
X-KDE-Keywords[el]=chm
X-KDE-Keywords[en_GB]=chm
X-KDE-Keywords[eo]=chm
X-KDE-Keywords[es]=chm
X-KDE-Keywords[et]=chm
X-KDE-Keywords[eu]=chm
X-KDE-Keywords[fi]=chm
X-KDE-Keywords[fr]=chm
X-KDE-Keywords[ga]=chm
X-KDE-Keywords[gl]=chm
X-KDE-Keywords[he]=chm
X-KDE-Keywords[hu]=chm
X-KDE-Keywords[ia]=chm
X-KDE-Keywords[ie]=chm
X-KDE-Keywords[is]=chm
X-KDE-Keywords[it]=chm
X-KDE-Keywords[ja]=chm
X-KDE-Keywords[ka]=chm
X-KDE-Keywords[kk]=chm
X-KDE-Keywords[km]=chm
X-KDE-Keywords[ko]=chm
X-KDE-Keywords[lt]=chm
X-KDE-Keywords[lv]=chm
X-KDE-Keywords[mr]=chm
X-KDE-Keywords[nb]=chm
X-KDE-Keywords[nds]=CHM
X-KDE-Keywords[nl]=chm
X-KDE-Keywords[nn]=chm
X-KDE-Keywords[pa]=chm
X-KDE-Keywords[pl]=chm
X-KDE-Keywords[pt]=chm
X-KDE-Keywords[pt_BR]=chm
X-KDE-Keywords[ro]=chm
X-KDE-Keywords[ru]=chm
X-KDE-Keywords[sk]=chm
X-KDE-Keywords[sl]=chm
X-KDE-Keywords[sq]=chm
X-KDE-Keywords[sr]=chm,ЦХМ
X-KDE-Keywords[sr@ijekavian]=chm,ЦХМ
X-KDE-Keywords[sr@ijekavianlatin]=chm,CHM
X-KDE-Keywords[sr@latin]=chm,CHM
X-KDE-Keywords[sv]=chm
X-KDE-Keywords[ta]=chm
X-KDE-Keywords[tr]=chm
X-KDE-Keywords[uk]=chm
X-KDE-Keywords[vi]=chm
X-KDE-Keywords[x-test]=xxchmxx
X-KDE-Keywords[zh_CN]=chm
X-KDE-Keywords[zh_TW]=chm
X-KDE-AliasFor=org.kde.okular.kirigami.desktop

View file

@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<component type="addon">
<id>org.kde.okular-chm</id>
<extends>org.kde.okular.desktop</extends>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0+ and GFDL-1.3</project_license>
<name>CHM Documents</name>
<name xml:lang="ar">مستندات CHM</name>
<name xml:lang="az">CHM sənədləri</name>
<name xml:lang="be">Дакументы CHM</name>
<name xml:lang="bg">CHM документи</name>
<name xml:lang="ca">Documents CHM</name>
<name xml:lang="ca-valencia">Documents CHM</name>
<name xml:lang="cs">Dokumenty CHM</name>
<name xml:lang="de">CHM-Dokumente</name>
<name xml:lang="el">Έγγραφα CHM</name>
<name xml:lang="en-GB">CHM Documents</name>
<name xml:lang="eo">CHM-Dokumentoj</name>
<name xml:lang="es">Documentos CHM</name>
<name xml:lang="et">CHM-dokumendid</name>
<name xml:lang="eu">CHM dokumentuak</name>
<name xml:lang="fi">CHM-tiedostot</name>
<name xml:lang="fr">Documents « CHM »</name>
<name xml:lang="gl">Documentos CHM</name>
<name xml:lang="he">מסמכי CHM</name>
<name xml:lang="hi">सीएचएम दस्तावेज़</name>
<name xml:lang="hu">CHM-dokumentumok</name>
<name xml:lang="ia">Documentos CHM</name>
<name xml:lang="is">CHM-skjöl</name>
<name xml:lang="it">Documenti CHM</name>
<name xml:lang="ka">CHM დოკუმენტები</name>
<name xml:lang="ko">CHM 문서</name>
<name xml:lang="lt">CHM dokumentai</name>
<name xml:lang="lv">CHM dokumenti</name>
<name xml:lang="ml">സി എച് എം പ്രമാണങ്ങൾ</name>
<name xml:lang="nl">CHM-documenten</name>
<name xml:lang="nn">CHM-dokument</name>
<name xml:lang="pl">Dokumenty CHM</name>
<name xml:lang="pt">Documentos CHM</name>
<name xml:lang="pt-BR">Documentos CHM</name>
<name xml:lang="ro">Documente CHM</name>
<name xml:lang="ru">Документы CHM</name>
<name xml:lang="sk">CHM dokumenty</name>
<name xml:lang="sl">Dokumenti CHM</name>
<name xml:lang="sr">ЦХМ документи</name>
<name xml:lang="sr-Latn">CHM dokumenti</name>
<name xml:lang="sr-ijekavian">ЦХМ документи</name>
<name xml:lang="sr-ijekavianlatin">CHM dokumenti</name>
<name xml:lang="sv">CHM-dokument</name>
<name xml:lang="ta">CHM ஆவணங்கள்</name>
<name xml:lang="tr">CHM Belgeleri</name>
<name xml:lang="uk">документи CHM</name>
<name xml:lang="vi">Tài liệu CHM</name>
<name xml:lang="x-test">xxCHM Documentsxx</name>
<name xml:lang="zh-CN">CHM 文档</name>
<name xml:lang="zh-TW">CHM 文件</name>
<summary>Adds support for reading CHM documents</summary>
<summary xml:lang="ar">يضيف دعم لقراءة مستندات CHM</summary>
<summary xml:lang="az">CHM sənədlərini oxumaq üçün dəstək əlavə edir</summary>
<summary xml:lang="be">Падтрымка чытання дакументаў CHM</summary>
<summary xml:lang="bg">Добавя поддръжка за CHM документи</summary>
<summary xml:lang="ca">Afegeix la implementació per a llegir documents CHM</summary>
<summary xml:lang="ca-valencia">Afig la implementació per a llegir documents CHM</summary>
<summary xml:lang="cs">Přidává podporu pro čtení dokumentů CHM</summary>
<summary xml:lang="de">Bietet Unterstützung zum Lesen von CHM-Dokumenten</summary>
<summary xml:lang="el">Προσθέτει υποστήριξη για την ανάγνωση εγγράφων CHM</summary>
<summary xml:lang="en-GB">Adds support for reading CHM documents</summary>
<summary xml:lang="eo">Aldonas subtenon por lego de CHM-Dokumentoj</summary>
<summary xml:lang="es">Permite la lectura de documentos CHM</summary>
<summary xml:lang="et">CHM-dokumentide lugemise toetus</summary>
<summary xml:lang="eu">CHM dokumentuak irakurtzeko euskarria gehitzen du</summary>
<summary xml:lang="fi">Lisää CHM-tiedostojen lukutuen</summary>
<summary xml:lang="fr">Permet la lecture des documents « CHM »</summary>
<summary xml:lang="gl">Engade a posibilidade de ler documentos CHM.</summary>
<summary xml:lang="he">מוסיף תמיכה בקריאת מסמכי CHM (מסמכי עזרה דחוסים)</summary>
<summary xml:lang="hi">सीएचएम दस्तावेज़ पढ़ने के लिए समर्थन जोड़ता है</summary>
<summary xml:lang="hu">Támogatás CHM-dokumentumok olvasásához</summary>
<summary xml:lang="ia">Adde supporto per leger documentos CHM</summary>
<summary xml:lang="is">Bætir við stuðningi til að lesa CHM-skjöl</summary>
<summary xml:lang="it">Aggiunge il supporto per la lettura di documenti CHM</summary>
<summary xml:lang="ka">CHM დოკუმენტების კითხვის მხარდაჭერის დამატება</summary>
<summary xml:lang="ko">CHM 문서 읽기 지원 추가</summary>
<summary xml:lang="lt">Prideda palaikymą CHM dokumentų skaitymui</summary>
<summary xml:lang="lv">Pievieno atbalstu CHM dokumentu lasīšanai</summary>
<summary xml:lang="ml">സി എം എച് പ്രമാണങ്ങൾ വായിക്കുവാൻ പിന്തുണ ചേർക്കുന്നു</summary>
<summary xml:lang="nl">Voegt ondersteuning voor lezen van CHM-documenten toe</summary>
<summary xml:lang="nn">Legg til støtte for å lesa CHM-dokument</summary>
<summary xml:lang="pl">Dodaje obsługę dokumentów CHM</summary>
<summary xml:lang="pt">Adiciona o suporte para a leitura de documentos CHM</summary>
<summary xml:lang="pt-BR">Adiciona o suporte para leitura de documentos CHM</summary>
<summary xml:lang="ro">Adaugă suport pentru citirea documentelor CHM</summary>
<summary xml:lang="ru">Поддержка чтения документов CHM</summary>
<summary xml:lang="sk">Pridá podporu pre čítanie CHM dokumentov</summary>
<summary xml:lang="sl">Doda podporo za branje dokumentov CHM</summary>
<summary xml:lang="sr">Подршка за читање ЦХМ докумената</summary>
<summary xml:lang="sr-Latn">Podrška za čitanje CHM dokumenata</summary>
<summary xml:lang="sr-ijekavian">Подршка за читање ЦХМ докумената</summary>
<summary xml:lang="sr-ijekavianlatin">Podrška za čitanje CHM dokumenata</summary>
<summary xml:lang="sv">Lägger till stöd för att läsa CHM-dokument</summary>
<summary xml:lang="ta">CHM ஆவணங்களைப் படிப்பதற்கான ஆதரவை சேர்க்கும்</summary>
<summary xml:lang="tr">CHM belgelerini okuma desteği ekler</summary>
<summary xml:lang="uk">Додає підтримку читання документів CHM</summary>
<summary xml:lang="vi">Thêm hỗ trợ đọc tài liệu CHM</summary>
<summary xml:lang="x-test">xxAdds support for reading CHM documentsxx</summary>
<summary xml:lang="zh-CN">增加对 CHM 文档的阅读支持</summary>
<summary xml:lang="zh-TW">加入對讀取 CHM 文件的支援</summary>
<provides>
<mediatype>application/x-chm</mediatype>
</provides>
<url type="homepage">https://okular.kde.org</url>
<releases>
<release version="24.02.2" date="2024-04-11"/>
<release version="24.02.1" date="2024-03-21"/>
<release version="24.02.0" date="2024-02-28"/>
<release version="23.08.5" date="2024-02-15"/>
</releases>
</component>