mirror of
https://invent.kde.org/graphics/okular
synced 2024-09-29 20:54:45 +00:00
Remove CHM generator; disabled for 4 months
This commit is contained in:
parent
4435325e5a
commit
460cf6123e
|
@ -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)
|
||||
|
|
|
@ -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} )
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/sh
|
||||
$XGETTEXT $(find . -name "*.cpp" -o -name "*.h") -o $podir/okular_chm.pot
|
|
@ -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; */
|
Binary file not shown.
|
@ -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"
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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 í
|
||||
*/
|
||||
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
|
|
@ -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");
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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 〽 - 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;
|
||||
}
|
||||
}
|
|
@ -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 ´ based on current encoding, initialized upon the first use
|
||||
QMap<QString, QString> m_entityDecodeMap;
|
||||
};
|
||||
|
||||
#endif // HELPER_ENTITYDECODER_H
|
|
@ -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 ).
|
||||
// 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();
|
||||
}
|
||||
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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]": "© 2005–2007 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]": "© 2005–2007 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, 2005–2007\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
|
||||
}
|
|
@ -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 liệu
|
||||
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 liệu vạn 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
|
|
@ -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 liệu
|
||||
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 kiểu tài liệu 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
|
|
@ -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>
|
Loading…
Reference in a new issue