diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 47d455dd55be855e4cc691c32f687f723d9247ee (patch) | |
tree | 52e236aaa2576bdb3840ebede26619692fed6d7d /kviewshell/plugins/djvu | |
download | tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kviewshell/plugins/djvu')
124 files changed, 76604 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/Makefile.am b/kviewshell/plugins/djvu/Makefile.am new file mode 100644 index 00000000..c0124821 --- /dev/null +++ b/kviewshell/plugins/djvu/Makefile.am @@ -0,0 +1,33 @@ +INCLUDES = -I$(top_srcdir) $(all_includes) \ + -I$(top_srcdir)/kviewshell \ + -I$(top_builddir)/kviewshell \ + -I$(kde_includes)/kviewshell \ + -I$(srcdir)/libdjvu + +SUBDIRS = libdjvu . + +KDE_CXXFLAGS = -Wno-deprecated + +METASOURCES = AUTO + +# this is where the desktop file will go +kde_services_DATA = djvumultipage.desktop + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/kviewshell/plugins/djvu + +kde_module_LTLIBRARIES = djvuviewpart.la +djvuviewpart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +djvuviewpart_la_LIBADD = -lkdeprint -lkparts $(top_builddir)/kviewshell/libkmultipage.la libdjvu/libdjvu.la +djvuviewpart_la_SOURCES = djvumultipage.cpp djvurenderer.cpp kprintDialogPage_DJVUpageoptions.cpp \ + kprintDialogPage_DJVUconversionoptions.cpp kprintDialogPage_DJVUconversionoptions_basewidget.ui \ + pageRangeWidget_base.ui pageRangeWidget.cpp \ + prefs.kcfgc + +kde_kcfg_DATA = djvumultipage.kcfg + +pluginsdir = $(kde_datadir) +plugins_DATA = djvumultipage.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kdjview.pot diff --git a/kviewshell/plugins/djvu/djvumultipage.cpp b/kviewshell/plugins/djvu/djvumultipage.cpp new file mode 100644 index 00000000..2599cc29 --- /dev/null +++ b/kviewshell/plugins/djvu/djvumultipage.cpp @@ -0,0 +1,357 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * Copyright (C) 2005 by Wilfried Huss * + * Wilfried.Huss@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <kaction.h> +#include <kdialogbase.h> +#include <kfiledialog.h> +#include <kparts/genericfactory.h> +#include <kprinter.h> +#include <ktempfile.h> +#include "kvsprefs.h" +#include <qapplication.h> +#include <qpaintdevicemetrics.h> +#include <qprinter.h> +#include <qtooltip.h> + +#include "ByteStream.h" +#include "DjVuToPS.h" +#include "kprintDialogPage_DJVUpageoptions.h" +#include "kprintDialogPage_DJVUconversionoptions.h" +#include "djvumultipage.h" +#include "pageRangeWidget.h" +#include "prefs.h" + +#include "kmessagebox.h" + +typedef KParts::GenericFactory<DjVuMultiPage> DjVuMultiPageFactory; +K_EXPORT_COMPONENT_FACTORY(djvuviewpart, DjVuMultiPageFactory) + + +DjVuMultiPage::DjVuMultiPage(QWidget *parentWidget, const char *widgetName, QObject *parent, + const char *name, const QStringList&) + : KMultiPage(parentWidget, widgetName, parent, name), djvuRenderer(parentWidget) +{ + /* This is kparts wizardry that cannot be understood by man. Simply + change the names to match your implementation. */ + setInstance(DjVuMultiPageFactory::instance()); + djvuRenderer.setName("DjVu renderer"); + + // Render modes + QStringList renderModes; + renderModes.append(i18n("Color")); + renderModes.append(i18n("Black and White")); + renderModes.append(i18n("Show foreground only")); + renderModes.append(i18n("Show background only")); + renderModeAction = new KSelectAction (i18n("Render Mode"), 0, 0, 0, actionCollection(), "render_mode"); + renderModeAction->setItems(renderModes); + + renderModeAction->setCurrentItem(Prefs::renderMode()); + + deletePagesAction = new KAction(i18n("Delete Pages..."), 0, this, SLOT(slotDeletePages()), actionCollection(), "delete_pages"); + + // change the rendermode + connect(renderModeAction, SIGNAL(activated(int)), this, SLOT(setRenderMode(int))); + + /* It is very important that this method is called in the + constructor. Otherwise kmultipage does not know how to render + files, and crashes may result. */ + setRenderer(&djvuRenderer); + + setXMLFile("djvumultipage.rc"); + + enableActions(false); +} + + +DjVuMultiPage::~DjVuMultiPage() +{ + ; +} + + +KAboutData* DjVuMultiPage::createAboutData() +{ + /* You obviously want to change this to match your setup */ + KAboutData* about = new KAboutData("djvumultipage", I18N_NOOP("KDjView"), "0.1", + I18N_NOOP("KViewshell DjVu Plugin."), + KAboutData::License_GPL, + "Wilfried Huss", + I18N_NOOP("This program displays DjVu files.")); + + about->addAuthor ("Stefan Kebekus", + I18N_NOOP("KViewShell plugin"), + "kebekus@kde.org", + "http://www.mi.uni-koeln.de/~kebekus"); + + about->addAuthor ("Wilfried Huss", + I18N_NOOP("DjVu file loading"), + "Wilfried.Huss@gmx.at"); + + return about; +} + +void DjVuMultiPage::enableActions(bool b) +{ + KMultiPage::enableActions(b); + + deletePagesAction->setEnabled(b); +} + +void DjVuMultiPage::setFile(bool r) +{ + enableActions(r); +} + +QStringList DjVuMultiPage::fileFormats() const +{ + /* This list is used in the file selection dialog when the file is + saved */ + QStringList r; + r << i18n("*.djvu|DjVu file (*.djvu)"); + return r; +} + + +void DjVuMultiPage::setRenderMode(int mode) +{ + // Save renderMode for future uses + switch (mode) + { + case Prefs::EnumRenderMode::BlackAndWhite: + Prefs::setRenderMode(Prefs::EnumRenderMode::BlackAndWhite); + + break; + case Prefs::EnumRenderMode::Foreground: + Prefs::setRenderMode(Prefs::EnumRenderMode::Foreground); + + break; + case Prefs::EnumRenderMode::Background: + Prefs::setRenderMode(Prefs::EnumRenderMode::Background); + + break; + default: //Prefs::EnumRenderMode::Color + Prefs::setRenderMode(Prefs::EnumRenderMode::Color); + } + Prefs::writeConfig(); + renderModeChanged(); +} + + +void DjVuMultiPage::slotDeletePages() +{ + if (numberOfPages() == 0) + return; + + KDialogBase dialog( parentWdg, "urldialog", true, i18n("Delete Pages"), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ); + PageRangeWidget range( 1, numberOfPages(), currentPageNumber(), &dialog, "range widget" ); + QToolTip::add( &range, i18n( "Select the pages you wish to delete." ) ); + dialog.setButtonOK(i18n("Delete Pages")); + dialog.setMainWidget(&range); + if (dialog.exec() != QDialog::Accepted) + return; + + djvuRenderer.deletePages(range.getFrom(), range.getTo()); + + + // ========= + pageCache->deselectText(); + document_history.clear(); + pageCache->clear(); + + generateDocumentWidgets(); + + // Set number of widgets in the thumbnail sidebar + markList()->clear(); + markList()->setNumberOfPages(numberOfPages(), KVSPrefs::showThumbnails()); + + // Set Table of Contents + //@@@@@@@@@@ tableOfContents->setContents(renderer->getBookmarks()); + + // Clear Statusbar + emit setStatusBarText(QString::null); +} + + +void DjVuMultiPage::print() +{ + // Paranoid safety checks + if (djvuRenderer.isEmpty()) + return; + + // Allocate the printer structure + KPrinter *printer = getPrinter(false); + if (printer == 0) + return; + + KPrintDialogPage_DJVUPageOptions *pageOptions = new KPrintDialogPage_DJVUPageOptions(); + if (pageOptions == 0) { + kdError(1223) << "DjVuMultiPage::print(): Cannot allocate new KPrintDialogPage_PageOptions structure" << endl; + delete printer; + return; + } + printer->addDialogPage( pageOptions ); + + KPrintDialogPage_DJVUConversionOptions *conversionOptions = new KPrintDialogPage_DJVUConversionOptions(); + if (pageOptions == 0) { + kdError(1223) << "DjVuMultiPage::print(): Cannot allocate new KPrintDialogPage_ConversionOptions structure" << endl; + delete printer; + return; + } + printer->addDialogPage( conversionOptions ); + + // initialize the printer using the print dialog + if ( printer->setup(parentWdg, i18n("Print %1").arg(m_file.section('/', -1))) ) { + // Now do the printing. + QValueList<int> pageList = printer->pageList(); + if (pageList.isEmpty()) + printer->abort(); + else { + // Printing usually takes a while. This is to keep the GUI + // updated. + qApp->processEvents(); + + // Printing... + DjVuToPS converter; + DjVuToPS::Options &options = converter.options; + + // Set PostScript Language Level, taking 3 as the default + options.set_format(DjVuToPS::Options::PS); + QString op = printer->option( "kde-kdjvu-pslevel" ); + if (op == "1") + options.set_level(1); + else + if (op == "3") + options.set_level(3); + else + options.set_level(2); + + // Set page size orientation + if (printer->option( "kde-kviewshell-rotatepage" ) == "true") + options.set_orientation (DjVuToPS::Options::AUTO); + else + if ( printer->orientation() == KPrinter::Landscape ) + options.set_orientation (DjVuToPS::Options::LANDSCAPE); + else + options.set_orientation (DjVuToPS::Options::PORTRAIT); + + // Set render mode, taking "color" as default + op = printer->option("kde-kdjvu-rendermode"); + if (op == "black-and-white") + options.set_mode(DjVuToPS::Options::BW); + else + if (op == "foreground") + options.set_mode(DjVuToPS::Options::FORE); + else + if (op == "background") + options.set_mode(DjVuToPS::Options::BACK); + else + options.set_mode(DjVuToPS::Options::COLOR); + + // Set Color or Grayscale mode + if (printer->colorMode() == KPrinter::Color) + options.set_color(true); + else + options.set_color(false); + + // Set Zoom + if (printer->option( "kde-kdjvu-fitpage" ) == "true") + options.set_zoom(0); + else + options.set_zoom(100); + + KTempFile tmpPSFile(QString::null, "ps"); + tmpPSFile.close(); + tmpPSFile.setAutoDelete(true); + + if (djvuRenderer.convertToPSFile(converter, tmpPSFile.name(), pageList ) == true) + printer->printFiles( QStringList(tmpPSFile.name()), true ); + else + printer->abort(); + } + delete printer; + } +} + + +bool DjVuMultiPage::isReadWrite() const +{ + return true; +} + + +bool DjVuMultiPage::isModified() const +{ + return djvuRenderer.isModified(); +} + + +void DjVuMultiPage::slotSave() +{ + // Paranoid safety checks + if (djvuRenderer.isEmpty()) + return; + + // Try to guess the proper ending... + QString formats; + QString ending; + int rindex = m_file.findRev("."); + if (rindex == -1) { + ending = QString::null; + formats = QString::null; + } else { + ending = m_file.mid(rindex); // e.g. ".dvi" + formats = fileFormats().grep(ending).join("\n"); + } + + QString fileName = KFileDialog::getSaveFileName(QString::null, formats, 0, i18n("Save File As")); + + if (fileName.isEmpty()) + return; + + // Add the ending to the filename. I hope the user likes it that + // way. + if (!ending.isEmpty() && fileName.find(ending) == -1) + fileName = fileName+ending; + + if (QFile(fileName).exists()) { + int r = KMessageBox::warningContinueCancel(parentWdg, i18n("The file %1\nalready exists. Do you want to overwrite it?").arg(fileName), + i18n("Overwrite File"), i18n("Overwrite")); + if (r == KMessageBox::Cancel) + return; + } + + djvuRenderer.save(fileName); + + /* + if (!djvuRenderer.save(fileName) == false) + KMessageBox::error( parentWdg, + i18n("<qt><strong>File error.</strong> Unable to write to the specified file '%1'. The document is <strong>not</strong> saved.</qt>").arg(fileName), + i18n("File Error")); + */ + + return; +} + + + +#include "djvumultipage.moc" diff --git a/kviewshell/plugins/djvu/djvumultipage.desktop b/kviewshell/plugins/djvu/djvumultipage.desktop new file mode 100644 index 00000000..1de46b47 --- /dev/null +++ b/kviewshell/plugins/djvu/djvumultipage.desktop @@ -0,0 +1,57 @@ +[Desktop Entry] +Name=kdjview +Name[hu]=KDjView +Name[ja]=Kdjview +Name[ne]=केडीजे दृशà¥à¤¯ +Name[sk]=kdjView +Name[sv]=Kdjview +Name[zh_CN]=KDjView +Icon=kdjview +Type=Service +Comment=KViewShell plugin for DjVu files +Comment[bg]=ПриÑтавка за файлове DjVu +Comment[br]=Lugent KViewShell evit ar restroù DjVu +Comment[bs]=KViewShell dodatak za DjVu datoteke +Comment[ca]=Connector pel KViewShell per fitxers DjVu +Comment[cs]=KViewShell modul pro DjVu soubory +Comment[da]=Kviewshell-plugin for DjVu-filer +Comment[de]=Ein KViewShell-Modul für DjVu-Dateien +Comment[el]=Î Ïόσθετο του KViewShell για αÏχεία DjVu +Comment[es]=Extensión KViewShell para archivos DjVu +Comment[et]=KView DjVu-failide plugin +Comment[eu]=DjVu fitxategien KViewShell-en plugina +Comment[fa]=وصلۀ KViewShell برای پرونده‌های DjVu +Comment[fi]=KViewShell sovelma DjVu-tiedostoille +Comment[fr]=Module KViewShell pour les fichiers DjVu +Comment[gl]=Extensión de KViewShell para ficheiros DjVu +Comment[hu]=KViewShell-modul DjVu-fájlokhoz +Comment[is]=KViewShell Ãforrit fyrir DjVu skrár +Comment[it]=Plugin KViewShell per file DjVu +Comment[ja]=DjVu ファイル用㮠KViewShell プラグイン +Comment[kk]=DjVu файлдарына арналған KViewShell плагин модулі +Comment[km]=កម្មវិធី​ជំនួយ KViewShell សម្រាប់​ឯកសារ DjVu +Comment[lt]=KViewShell priedas, skirtas DjVu byloms +Comment[ms]=Plugin KViewShell untuk fail DjVu +Comment[nb]=KViewShell programtillegg for DjVu-filer +Comment[nds]=En "KViewShell"-Moduul för DjVu-Dateien +Comment[ne]=डिजे à¤à¥€à¤¯à¥‚ फाइलका लागि केडीई दृशà¥à¤¯ शेल पà¥à¤²à¤—इन +Comment[nl]=KViewShell-plugin voor DjVu-bestanden +Comment[nn]=KViewShell-programtillegg for DjVu-filer +Comment[pl]=Wtyczka KViewShell do plików DjVu +Comment[pt]='Plugin' do KViewShell para ficheiros do DjVu +Comment[pt_BR]=Plugin KViewShell para arquivos DjVu +Comment[ru]=Компонент проÑмотра файлов DjVu +Comment[sk]=KViewShell modul pre DjVu súbory +Comment[sl]=Vstavek za KViewShell za datoteke DjVu +Comment[sr]=KViewShell-ов прикључак за DjVu фајлове +Comment[sr@Latn]=KViewShell-ov prikljuÄak za DjVu fajlove +Comment[sv]=Kviewshell-insticksprogram för DjVu-filer +Comment[tr]=DjVu dosyaları için KViewShell eklentisi +Comment[uk]=Втулок переглÑду файлів DjVu Ð´Ð»Ñ KViewShell +Comment[zh_CN]=DjVu 文件的 KViewShell æ’件 +Comment[zh_HK]=用於 DjVu 檔案的 KViewShell æ’件 +Comment[zh_TW]=DjVu 檔的 KViewShell å¤–æŽ›ç¨‹å¼ +ServiceTypes=KViewShell/MultiPage +X-KDE-MimeTypes=image/x-djvu +X-KDE-Library=djvuviewpart +X-KDE-MultiPageVersion=2 diff --git a/kviewshell/plugins/djvu/djvumultipage.h b/kviewshell/plugins/djvu/djvumultipage.h new file mode 100644 index 00000000..b417144b --- /dev/null +++ b/kviewshell/plugins/djvu/djvumultipage.h @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * Copyright (C) 2005 by Wilfried Huss * + * Wilfried.Huss@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef __DJVUMULTIPAGE_H +#define __DJVUMULTIPAGE_H + +#include <qstringlist.h> + +#include "kmultipage.h" +#include "djvurenderer.h" + +#include "DjVuToPS.h" + +class KSelectAction; + +/*! \mainpage DjVuMultiPage + +\section intro_sec Introduction + +kvsdemo is a minimal, but well-documented reference implementation of +a kviewshell plugin that can serve as a starting point for a +real-world implementation. + +\section install_sec Usage + +When kvsdemo is installed, the kviewshell program can open C++ source +files, i.e. files of mime type text/x-c++src. When such a file is +loaded, kviewshell shows 10 blank pages of A4 size. + +\section content Content + +Only the two classes that are absolutely necessary for a working +plugin are implemented. The only other file that is installed is a +desktop file, which tells kviewhshell to use the plugin. + +- kvsdemo_multipage, an implementation of a kmultipage. In a real +application, this class would create and manage the GUI elements that +the plugin adds to the GUI of the kviewshell. This implementation adds +nothing, and does only the minimal initialization required.. + +- kvsdemo_renderer, an implementation of a documentRenderer. This +class is responsible for document loading and rendering. + +- kvsdemo.desktop, the desktop entry file that tells KDE that kvsdemo +is a plugin for kviewshell that handles files of type +text/x-c++src. Without this file installed, the file dialog in +kviewshell would not show C++ source files, and the command line +"kvieshell test.cpp" would fail with an error dialog "No plugin for +text/x-c++src files installed". + +*/ + + + + +/*! \brief Well-documented minimal implementation of a KMultiPage + +This class provides a well-documented reference implementation of a +KMultiPage, suitable as a starting point for a real-world +implementation. In a real application, this class would contain the +GUI elements that the plugin adds to the GUI of the kviewshell. Our +implementation adds nothing, and does only the minimal initialization +required. + +*/ + +class DjVuMultiPage : public KMultiPage +{ + Q_OBJECT + +public: + /** Constructor + + The constructor needs to initialize several members of the + kmultipage. Please have a look at the constructor's source code to + see how to adjust this for your implementation. + */ + DjVuMultiPage(QWidget *parentWidget, const char *widgetName, QObject *parent, + const char *name, const QStringList& args = QStringList()); + + /** Destructor + + This destructor does nothing. + */ + virtual ~DjVuMultiPage(); + + virtual void setFile(bool r); + + /** List of file formats for file saving + + This method returns the list of supported file formats for saving + the file. + */ + virtual QStringList fileFormats() const; + + /** Author information + + This member returns a structure that contains information about the + authors of the implementation + */ + static KAboutData* createAboutData(); + + /** Re-implementation of the print method */ + virtual void print(); + + virtual bool isReadWrite() const; + virtual bool isModified() const; + + virtual void slotSave(); + + protected: + virtual void enableActions(bool); + + private slots: + void setRenderMode(int mode); + + /** Opens a dialog to delete pages */ + void slotDeletePages(); + + private: + /** This member holds the renderer which is used by the demo + implementation */ + DjVuRenderer djvuRenderer; + + KSelectAction* renderModeAction; + KAction* deletePagesAction; +}; + +#endif diff --git a/kviewshell/plugins/djvu/djvumultipage.kcfg b/kviewshell/plugins/djvu/djvumultipage.kcfg new file mode 100644 index 00000000..478667b7 --- /dev/null +++ b/kviewshell/plugins/djvu/djvumultipage.kcfg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="djvumultipagerc" /> + <group name="djvu"> + <entry key="RenderMode" type="Enum"> + <default>Color</default> + <choices> + <choice name="Color" /> + <choice name="BlackAndWhite" /> + <choice name="Foreground" /> + <choice name="Background" /> + </choices> + </entry> + </group> +</kcfg> diff --git a/kviewshell/plugins/djvu/djvumultipage.rc b/kviewshell/plugins/djvu/djvumultipage.rc new file mode 100644 index 00000000..c340fe42 --- /dev/null +++ b/kviewshell/plugins/djvu/djvumultipage.rc @@ -0,0 +1,15 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="djvumultipage" version="3"> + <MenuBar> + + <Menu name="edit"><text>&Edit</text> + <Separator/> + <Action name="delete_pages"/> + </Menu> + + <Menu name="view"><text>&View</text> + <Action name="render_mode"/> + </Menu> + + </MenuBar> +</kpartgui> diff --git a/kviewshell/plugins/djvu/djvurenderer.cpp b/kviewshell/plugins/djvu/djvurenderer.cpp new file mode 100644 index 00000000..54a96b38 --- /dev/null +++ b/kviewshell/plugins/djvu/djvurenderer.cpp @@ -0,0 +1,719 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * Copyright (C) 2005 by Wilfried Huss * + * Wilfried.Huss@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <kmessagebox.h> +#include <kdebug.h> +#include <klocale.h> +#include <qfileinfo.h> +#include <qimage.h> +#include <qpainter.h> +#include <kapp.h> + +#include "GBitmap.h" +#include "BSByteStream.h" +#include "IFFByteStream.h" + +#include "prefs.h" + +#include "documentWidget.h" +#include "djvurenderer.h" +#include "djvumultipage.h" +#include "hyperlink.h" +#include "renderedDocumentPagePixmap.h" +#include "textBox.h" + +//#define KF_DEBUG + +inline GUTF8String GStringFromQString(const QString& x) +{ + GUTF8String retval=(const char*)x.utf8(); + return retval; +} + + +inline QString QStringFromGString(const GUTF8String& x) +{ + QString retval=QString::fromUtf8((const char*)x); + return retval; +} + + +DjVuRenderer::DjVuRenderer(QWidget* par) + : DocumentRenderer(par) +{ +#ifdef KF_DEBUG + kdError() << "DjVuRenderer( parent=" << par << " )" << endl; +#endif + + PPMstream = ByteStream::create(); +} + + + +DjVuRenderer::~DjVuRenderer() +{ +#ifdef KF_DEBUG + kdDebug() << "~DjVuRenderer" << endl; +#endif + + // Wait for all access to this documentRenderer to finish + QMutexLocker locker( &mutex ); +} + + +void DjVuRenderer::drawPage(double resolution, RenderedDocumentPage* page) +{ +#ifdef KF_DEBUG + kdDebug() << "DjVuRenderer::drawPage(documentPage*) called, page number " << page->getPageNumber() << endl; +#endif + + // Paranoid safety checks + if (page == 0) { + kdError() << "DjVuRenderer::drawPage(documentPage*) called with argument == 0" << endl; + return; + } + if (page->getPageNumber() == 0) { + kdError() << "DjVuRenderer::drawPage(documentPage*) called for a documentPage with page number 0" << endl; + return; + } + + // Wait for all access to this documentRenderer to finish + QMutexLocker locker( &mutex ); + + // more paranoid safety checks + if (page->getPageNumber() > numPages) { + kdError() << "DjVuRenderer::drawPage(documentPage*) called for a documentPage with page number " << page->getPageNumber() + << " but the current fax file has only " << numPages << " pages." << endl; + return; + } + + int pageNumber = page->getPageNumber() - 1; + + GP<DjVuImage> djvuPage = document->get_page(pageNumber, true); + if (!djvuPage->wait_for_complete_decode()) + { + kdDebug() << "decoding failed." << endl; + return; + } + + if (!pageSizes[pageNumber].isValid()) + { + int djvuResolution = djvuPage->get_dpi(); + int djvuPageWidth = djvuPage->get_width(); + int djvuPageHeight = djvuPage->get_height(); + + Length w, h; + w.setLength_in_inch(djvuPageWidth / (double)djvuResolution); + h.setLength_in_inch(djvuPageHeight / (double)djvuResolution); + pageSizes[pageNumber].setPageSize(w, h); + + SimplePageSize ps = sizeOfPage(page->getPageNumber()); + + // If we are not printing we need to resize the pixmap. + RenderedDocumentPagePixmap* pagePixmap = dynamic_cast<RenderedDocumentPagePixmap*>(page); + if (pagePixmap) + pagePixmap->resize(ps.sizeInPixel(resolution)); + } + + //kdDebug() << "render page " << pageNumber + 1 << " at size (" << pageWidth << ", " << pageHeight << ")" << endl; + + int pageHeight = page->height(); + int pageWidth = page->width(); + + GRect pageRect(0, 0, pageWidth, pageHeight); + + + GP<GPixmap> djvuPixmap; + if (Prefs::renderMode() == Prefs::EnumRenderMode::Color) + djvuPixmap = djvuPage->get_pixmap(pageRect, pageRect); + else if (Prefs::renderMode() == Prefs::EnumRenderMode::Foreground) + djvuPixmap = djvuPage->get_fg_pixmap(pageRect, pageRect); + else if (Prefs::renderMode() == Prefs::EnumRenderMode::Background) + djvuPixmap = djvuPage->get_bg_pixmap(pageRect, pageRect); + + QPainter* foreGroundPaint = page->getPainter(); + if (foreGroundPaint != 0) + { + if(djvuPixmap && Prefs::renderMode() != Prefs::EnumRenderMode::BlackAndWhite) + { + PPMstream->seek(0); + djvuPixmap->save_ppm(*PPMstream); + long pixmapsize = PPMstream->tell(); + PPMstream->seek(0); + uchar* buf = new uchar[pixmapsize]; + long bytesRead = PPMstream->readall(buf, pixmapsize); + + bool ok = pixmap.loadFromData(buf, bytesRead, "PPM"); + if (!ok) + { + kdError() << "loading failed" << endl; + //draw an empty page + foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, Qt::white); + } + foreGroundPaint->drawPixmap(0, 0, pixmap); + delete[] buf; + +/* for (int i = 0; i < pageHeight; i++) + { + GPixel* pixmapRow = (*djvuPixmap)[i]; + + for (int j = 0; j < pageWidth; j++) + { + GPixel pixel = pixmapRow[j]; + foreGroundPaint->setPen(QColor(pixel.r, pixel.g, pixel.b)); + foreGroundPaint->drawPoint(j, pageHeight - i - 1); + } + }*/ + } + else + { + GP<GBitmap> djvuBitmap = djvuPage->get_bitmap(pageRect, pageRect); + if(djvuBitmap) + { + PPMstream->seek(0); + if(djvuBitmap->get_grays() == 2) + djvuBitmap->save_pbm(*PPMstream); + else + djvuBitmap->save_pgm(*PPMstream); + + long pixmapsize = PPMstream->tell(); + PPMstream->seek(0); + uchar* buf = new uchar[pixmapsize]; + long bytesRead = PPMstream->readall(buf, pixmapsize); + + bool ok = pixmap.loadFromData(buf, bytesRead, "PPM"); + if (!ok) + { + kdError() << "loading failed" << endl; + //draw an empty page + foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, Qt::white); + } + foreGroundPaint->drawPixmap(0, 0, pixmap); + delete[] buf; +/* + for (int i = 0; i < pageHeight; i++) + { + unsigned char* bitmapRow = (*djvuBitmap)[i]; + for (int j = 0; j < pageWidth; j++) + { + unsigned char pixel = 255-bitmapRow[j]; + foreGroundPaint->setPen(QColor(pixel, pixel, pixel)); + foreGroundPaint->drawPoint(j, pageHeight - i - 1); + } + }*/ + } + else + { + //draw an empty page + foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, Qt::white); + } + } + + //kdDebug() << "rendering page " << pageNumber + 1 << " at size (" << pageWidth << ", " << pageHeight << ") finished." << endl; + page->returnPainter(foreGroundPaint); + } + + GP<DjVuTXT> pageText = getText(pageNumber); + + if (pageText) + { + QSize djvuPageSize(djvuPage->get_width(), djvuPage->get_real_height()); + fillInText(page, pageText, pageText->page_zone, djvuPageSize); + //kdDebug() << "Text of page " << pageNumber << endl; + //kdDebug() << (const char*)pageText->textUTF8 << endl; + } + + getAnnotations(page, djvuPage); + + page->isEmpty = false; +} + + +bool DjVuRenderer::setFile(const QString &fname, const KURL &) +{ +#ifdef KF_DEBUG + kdDebug() << "DjVuRenderer::setFile(" << fname << ") called" << endl; +#endif + + // Wait for all access to this documentRenderer to finish + QMutexLocker locker( &mutex ); + + // If fname is the empty string, then this means: "close". + if (fname.isEmpty()) { + kdDebug() << "DjVuRenderer::setFile( ... ) called with empty filename. Closing the file." << endl; + return true; + } + + // Paranoid saftey checks: make sure the file actually exists, and + // that it is a file, not a directory. Otherwise, show an error + // message and exit.. + QFileInfo fi(fname); + QString filename = fi.absFilePath(); + if (!fi.exists() || fi.isDir()) { + KMessageBox::error( parentWidget, + i18n("<qt><strong>File error.</strong> The specified file '%1' does not exist.</qt>").arg(filename), + i18n("File Error")); + // the return value 'false' indicates that this operation was not successful. + return false; + } + + // Clear previously loaded document + clear(); + + // Now we assume that the file is fine and load the file. + G_TRY { + document = DjVuDocEditor::create_wait(GURL::Filename::UTF8(GStringFromQString(filename))); + } + G_CATCH(ex) { + ; + } + G_ENDCATCH; + + // If the above assumption was false. + if (!document) + { + KMessageBox::error( parentWidget, + i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>").arg(filename), + i18n("File Error")); + + clear(); + kdDebug(1223) << "Loading of document failed." << endl; + return false; + } + + bool r = initializeDocument(); + + // the return value 'true' indicates that this operation was successful. + return r; +} + +void DjVuRenderer::getAnnotations(RenderedDocumentPage* page, GP<DjVuImage> djvuPage) +{ + GP<ByteStream> annotations = djvuPage->get_anno(); + if (!(annotations && annotations->size())) + return; + + GP<DjVuANT> ant = DjVuANT::create(); + + GP<IFFByteStream> iff = IFFByteStream::create(annotations); + + GUTF8String chkid; + + while (iff->get_chunk(chkid)) + { + if (chkid == "ANTa") + { + ant->merge(*iff->get_bytestream()); + } + else if (chkid == "ANTz") + { + GP<ByteStream> bsiff = BSByteStream::create(iff->get_bytestream()); + ant->merge(*bsiff); + } + iff->close_chunk(); + } + + if (!ant->is_empty()) + { + // Scaling factors for the coordinate conversion. + // TODO: Refractor this into a function shared with fillInText. + int pageWidth = page->width(); + int pageHeight = page->height(); + + double scaleX = pageWidth / (double)djvuPage->get_width(); + double scaleY = pageHeight / (double)djvuPage->get_height(); + + GPList<GMapArea> map = ant->map_areas; + + for (GPosition pos = map; pos; ++pos) + { + // Currently we only support rectangular links + if (!map[pos]->get_shape_type() == GMapArea::RECT) + continue; + + GRect rect = map[pos]->get_bound_rect(); + + QRect hyperlinkRect((int)(rect.xmin*scaleX+0.5), (int)((djvuPage->get_height()-rect.ymax)*scaleY+0.5), + (int)(rect.width()*scaleX +0.5), (int)(rect.height()*scaleY+0.5)); + + QString url((const char*)map[pos]->url); + QString target((const char*)map[pos]->target); + QString comment((const char*)map[pos]->comment); + + // Create an anchor for this link. + if (!anchorList.contains(url)) + { + // For now we only accept links to pages in the same document. + if(url[0] == '#' && target == "_self") + { + bool conversionOk; + PageNumber targetPage = url.remove('#').toInt(&conversionOk); + if (conversionOk) + anchorList[url] = Anchor(targetPage, Length()); + } + } + + Hyperlink hyperlink(hyperlinkRect.bottom(), hyperlinkRect, url); + page->hyperLinkList.push_back(hyperlink); + } + } +} + + +bool DjVuRenderer::initializeDocument() +{ + if (document == 0) + return false; + + if (!document->wait_for_complete_init()) { + kdDebug() << "Document Initialization failed." << endl; + return false; + } + + // Set the number of pages page sizes + numPages = document->get_pages_num(); + + pageSizes.resize(numPages); + Length w,h; + + // Set the page sizes in the pageSizes array. Give feedback for + // very long documents + if (numPages > 100) + emit setStatusBarText(i18n("Loading file. Computing page sizes...")); + for(Q_UINT16 i=0; i<numPages; i++) { + // Keep the GUI updated + if (i%100 == 0) + kapp->processEvents(); + + GP<DjVuFile> djvuFile = document->get_djvu_file(i); + int resolution; + int pageWidth; + int pageHeight; + bool ok = getPageInfo(djvuFile, pageWidth, pageHeight, resolution); + if (!ok) + kdError() << "Decoding info of page " << i << " failed." << endl; + else { + w.setLength_in_inch(pageWidth / (double)resolution); + h.setLength_in_inch(pageHeight / (double)resolution); + pageSizes[i].setPageSize(w, h); + } + } + emit setStatusBarText(QString::null); + + // We will also generate a list of hyperlink-anchors in the document. + // So declare the existing lists empty. + anchorList.clear(); + return true; +} + + +GP<DjVuTXT> DjVuRenderer::getText(PageNumber pageNumber) +{ + GUTF8String chkid; + + const GP<DjVuFile> file = document->get_djvu_file(pageNumber); + const GP<ByteStream> bs(file->get_text()); + if (bs) + { + long int i=0; + const GP<IFFByteStream> iff(IFFByteStream::create(bs)); + while (iff->get_chunk(chkid)) + { + i++; + if (chkid == GUTF8String("TXTa")) + { + GP<DjVuTXT> txt = DjVuTXT::create(); + txt->decode(iff->get_bytestream()); + return txt; + } + else if (chkid == GUTF8String("TXTz")) + { + GP<DjVuTXT> txt = DjVuTXT::create(); + GP<ByteStream> bsiff=BSByteStream::create(iff->get_bytestream()); + txt->decode(bsiff); + return txt; + } + iff->close_chunk(); + } + } + return 0; +} + +void DjVuRenderer::fillInText(RenderedDocumentPage* page, const GP<DjVuTXT>& text, DjVuTXT::Zone& zone, QSize& djvuPageSize) +{ + if (zone.children.isempty()) + { + int pageWidth = page->width(); + int pageHeight = page->height(); + + double scaleX = pageWidth / (double)djvuPageSize.width(); + double scaleY = pageHeight / (double)djvuPageSize.height(); + + QString zoneString = QStringFromGString(text->textUTF8.substr(zone.text_start, zone.text_length)); + + //kdDebug() << "zone text: " << zoneString << endl; + + QRect textRect((int)(zone.rect.xmin*scaleX+0.5), (int)((djvuPageSize.height()-zone.rect.ymax)*scaleY+0.5), + (int)(zone.rect.width()*scaleX+0.5), (int)(zone.rect.height()*scaleY+0.5)); + //kdDebug() << "zone rect: " << textRect.x() << ", " << textRect.y() << ", " << textRect.width() << ", " << textRect.height() << endl; + TextBox textBox(textRect, zoneString); + page->textBoxList.push_back(textBox); + } + else + { + for (GPosition pos=zone.children; pos; ++pos) + { + fillInText(page, text, zone.children[pos], djvuPageSize); + } + } +} + +bool DjVuRenderer::getPageInfo(GP<DjVuFile> file, int& width, int& height, int& dpi) +{ + if (!file || !file->is_all_data_present()) + return false; + + const GP<ByteStream> pbs(file->get_djvu_bytestream(false, false)); + const GP<IFFByteStream> iff(IFFByteStream::create(pbs)); + + GUTF8String chkid; + if (iff->get_chunk(chkid)) + { + if (chkid == "FORM:DJVU") + { + while (iff->get_chunk(chkid) && chkid!="INFO") + iff->close_chunk(); + if (chkid == "INFO") + { + GP<ByteStream> gbs = iff->get_bytestream(); + GP<DjVuInfo> info=DjVuInfo::create(); + info->decode(*gbs); + int rot = ((360-GRect::findangle(info->orientation))/90)%4; + + width = (rot&1) ? info->height : info->width; + height = (rot&1) ? info->width : info->height; + dpi = info->dpi; + return true; + } + } + else if (chkid == "FORM:BM44" || chkid == "FORM:PM44") + { + while (iff->get_chunk(chkid) && chkid!="BM44" && chkid!="PM44") + iff->close_chunk(); + if (chkid=="BM44" || chkid=="PM44") + { + GP<ByteStream> gbs = iff->get_bytestream(); + if (gbs->read8() == 0) + { + gbs->read8(); + gbs->read8(); + unsigned char xhi = gbs->read8(); + unsigned char xlo = gbs->read8(); + unsigned char yhi = gbs->read8(); + unsigned char ylo = gbs->read8(); + + width = (xhi<<8)+xlo; + height = (yhi<<8)+ylo; + dpi = 100; + return true; + } + } + } + } + return false; +} + +void DjVuRenderer::getText(RenderedDocumentPage* page) +{ + QMutexLocker locker( &mutex ); + + int pageNumber = page->getPageNumber() - 1; + GP<DjVuTXT> pageText = getText(pageNumber); + + if (pageText) + { + GP<DjVuFile> djvuFile = document->get_djvu_file(pageNumber); + int resolution; + int pageWidth; + int pageHeight; + bool ok = getPageInfo(djvuFile, pageWidth, pageHeight, resolution); + + if (ok) + { + QSize djvuPageSize(pageWidth, pageHeight); + fillInText(page, pageText, pageText->page_zone, djvuPageSize); + } + } +} + + +bool DjVuRenderer::convertToPSFile( DjVuToPS &converter, QString filename, QValueList<int> &pageList ) +{ + if (document == 0) { + kdError(1223) << "DjVuRenderer::convertToPSFile(..) called when document was 0" << endl; + return false; + } + + QMutexLocker locker( &mutex ); + + // Set up progress dialog + KProgressDialog *pdialog = new KProgressDialog(parentWidget, "Printing-ProgressDialog", i18n("Printing..."), i18n("Preparing pages for printing..."), true); + pdialog->setButtonText(i18n("Abort")); + pdialog->showCancelButton(true); + pdialog->progressBar()->setTotalSteps(pageList.size()); + pdialog->progressBar()->setFormat(QString::null); + + // Open output file + GURL outname = GURL::Filename::UTF8(GStringFromQString(filename)); + GP<ByteStream> obs = ByteStream::create(outname, "w"); + + QString pagename; + QValueList<int>::ConstIterator it = pageList.begin(); + while (true) { + pagename += QString::number(*it); + ++it; + if (it == pageList.end()) + break; + pagename += ","; + } + GUTF8String pages = GStringFromQString(pagename); + + converter.set_info_cb(printerInfoCallBack, (void*)pdialog); + bool iscancelled = false; + G_TRY { + converter.print(*obs, (DjVuDocument *)document, pages ); + } + G_CATCH(ex) { + iscancelled = true; + } + G_ENDCATCH; + + delete pdialog; + + // This is to keep the GUI updated. + kapp->processEvents(); + + obs->flush(); + return !iscancelled; +} + + +void DjVuRenderer::deletePages(Q_UINT16 from, Q_UINT16 to) +{ + // Paranoia security checks + if (document == 0) { + kdError(1223) << "DjVuRenderer::deletePages(...) called when no document was loaded" << endl; + return; + } + if ((from > to) || (from == 0) || (from > totalPages()) || (to > totalPages())) { + kdError(1223) << "DjVuRenderer::deletePages(...) called with invalid arguments" << endl; + return; + } + + QMutexLocker locker( &mutex ); + + KProgressDialog *pdialog = 0; + if (to-from > 9) { + pdialog = new KProgressDialog(parentWidget, "Printing-ProgressDialog", i18n("Deleting pages..."), i18n("Please wait while pages are removed..."), true); + pdialog->showCancelButton(false); + pdialog->progressBar()->setTotalSteps(to-from+1); + pdialog->progressBar()->setFormat(QString::null); + pdialog->show(); + kapp->processEvents(); + } + + // set the document pointer temporarily to 0, so that no-one tries + // to render a page while we are deleting pages + GP<DjVuDocEditor> document_new = document; + document = 0; + + // Delete pages + if (pdialog == 0) { + GList<int> pageList; + for(Q_UINT16 i=from; i<= to; i++) + pageList.append(i-1); + document_new->remove_pages(pageList); + } else { + for(Q_UINT16 i=from; i<=to; i++) { + document_new->remove_page(from-1); + pdialog->progressBar()->setProgress(i-from); + pdialog->progressBar()->setFormat(i18n("deleting page %1").arg(i)); + kapp->processEvents(); + } + delete pdialog; + } + _isModified = true; + document = document_new; + + initializeDocument(); +} + + +bool DjVuRenderer::save(const QString &filename) +{ + if (document == 0) { + kdError() << "DjVuRenderer::save(..) called when document==0" << endl; + return false; + } + + QMutexLocker locker( &mutex ); + + G_TRY { + document->save_as(GURL::Filename::UTF8(GStringFromQString(filename)), true); + } + G_CATCH(ex) { + return false; + } + G_ENDCATCH; + + document->save_as(GURL::Filename::UTF8(filename.ascii()), true); + + if (QFile::exists(filename) == false) + return false; + + _isModified = false; + return true; +} + + +void DjVuRenderer::printerInfoCallBack(int page_num, int page_count, int, DjVuToPS::Stage, void *pd) +{ + if (pd == 0) + return; + + // Update the progress dialog. + KProgressDialog *pdialog = (KProgressDialog *)pd; + + pdialog->progressBar()->setProgress(page_count); + pdialog->progressBar()->setFormat(i18n("processing page %1").arg(page_num+1)); + pdialog->show(); + + if (pdialog->wasCancelled()) + G_THROW("STOP"); + + // This is to keep the GUI updated. + kapp->processEvents(); +} + + +#include "djvurenderer.moc" diff --git a/kviewshell/plugins/djvu/djvurenderer.h b/kviewshell/plugins/djvu/djvurenderer.h new file mode 100644 index 00000000..40418c23 --- /dev/null +++ b/kviewshell/plugins/djvu/djvurenderer.h @@ -0,0 +1,146 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * Copyright (C) 2005 by Wilfried Huss * + * Wilfried.Huss@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef _DJVURENDERER_H_ +#define _DJVURENDERER_H_ + +#include <kprogress.h> +#include <qpixmap.h> + +#include "DjVuImage.h" +#include "DjVuDocEditor.h" +#include "DjVuText.h" +#include "DjVuToPS.h" +#include "ByteStream.h" + +#include "documentRenderer.h" + +class RenderedDocumentPage; + +/*! \brief Well-documented minimal implementation of a documentRenderer + +This class provides a well-documented reference implementation of a +documentRenderer, suitable as a starting point for a real-world +implementation. This class is responsible for document loading and +rendering. Apart from the constructor and the descructor, it +implements only the necessary methods setFile() and drawPage(). The +method setFile() ignores the file content and simulates a document +with 10 empty pages of A4 size and a few anchors and bookmarks. +*/ + +class DjVuRenderer : public DocumentRenderer +{ + Q_OBJECT + +public: + /** Default constructor + + This constructor simply prints a message and calls the default constructor. + */ + DjVuRenderer(QWidget* parent); + + /** Destructor + + The destructor simply prints a message. It uses the mutex to + ensure that this class is not destructed while another thread + is currently using it. + */ + ~DjVuRenderer(); + + /** Opening a file + + This implementation does the necessary consistency checks and + complains, e.g. if the file does not exist, but otherwise completely + disregards the file content. It simulates a document of 10 empty pages of + A4 size, with a few sample bookmarks and anchors "p1", "p2", "p3" + for page 1, 2 and 3, respectively. + + @param fname the name of the file that should be opened. + */ + virtual bool setFile(const QString& fname, const KURL &); + + /** Rendering a page + @param res resolution at which drawing should take place + @param page pointer to a page structur on which we should draw + */ + virtual void drawPage(double res, RenderedDocumentPage* page); + + /** Extract the hidden text layer + + This function decodes the hidden text layer without actually decoding the full page. + It is therefore much faster then drawPage if you only need the text information. + */ + virtual void getText(RenderedDocumentPage* page); + + virtual bool supportsTextSearch() const { return true; }; + + /** DJVU to PostScript conversion + + This method uses the converter to convert the document to a PostScript. + + @param converter a DjVuToPS converter, whose options should already + be set to + + @param filename name of the PostScript file to generate + + @param pageList list of pages that are to be converted, with the + usual convention that "1" means "first page" + + @returns 'true' if the conversion was successful, 'false' if it + wasn't. The conversion can fail, for example, when the user aborts + the operation. + */ + bool convertToPSFile( DjVuToPS &converter, QString filename, QValueList<int> &pageList ); + + /** Deletes pages from the document */ + void deletePages(Q_UINT16 from, Q_UINT16 to); + + /** Saves the file */ + bool save(const QString &filename); + +private: + /* This method is called after a document is loaded with + create_wait() or has been modified (e.g. inserting/deleting + pages). It sets "numPages", fills the "pageSizes" array, and + clear the anchorList. */ + bool initializeDocument(); + + void getAnnotations(RenderedDocumentPage* page, GP<DjVuImage> djvuPage); + + bool getPageInfo(GP<DjVuFile> file, int& width, int& height, int& dpi); + + GP<DjVuTXT> getText(PageNumber pageNumber); + + void fillInText(RenderedDocumentPage* page, const GP<DjVuTXT>& text, DjVuTXT::Zone& zone, QSize& djvuPageSize); + + GP<DjVuDocEditor> document; + + /** Method used internally to report the progress of the DjVu to + PostScript conversion */ + static void printerInfoCallBack(int page_num, int page_count, int tot_pages, DjVuToPS::Stage, void *); + + QPixmap pixmap; + GP<ByteStream> PPMstream; +}; + +#endif diff --git a/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions.cpp b/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions.cpp new file mode 100644 index 00000000..aea7d6b7 --- /dev/null +++ b/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <klocale.h> +#include <qlayout.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <kdebug.h> + +#include "kprintDialogPage_DJVUconversionoptions.h" +#include "kprintDialogPage_DJVUconversionoptions_basewidget.h" + +KPrintDialogPage_DJVUConversionOptions::KPrintDialogPage_DJVUConversionOptions( QWidget *parent, const char *name ) + : KPrintDialogPage( parent, name ) +{ + setTitle( i18n("DJVU to PS Conversion") ); + + kprintDialogPage_pageoptions_baseLayout = new QVBoxLayout( this, 11, 6, "kprintDialogPage_pageoptions_baseLayout"); + if (kprintDialogPage_pageoptions_baseLayout == 0) { + kdError(1223) << "KPrintDialogPage_DJVUPageOptions::KPrintDialogPage_DJVUPageOptions() cannot create layout" << endl; + return; + } + + wdg = new kprintDialogPage_DJVUconversionoptions_basewidget(this, "basewdg" ); + if (wdg != 0) { + kprintDialogPage_pageoptions_baseLayout->addWidget( wdg ); + } +} + + + +void KPrintDialogPage_DJVUConversionOptions::getOptions( QMap<QString,QString>& opts, bool ) +{ + if (wdg == 0) + return; + + opts["kde-kdjvu-pslevel"] = QString::number(wdg->psLevel->currentItem() + 1); + + kdDebug() << "getOptions: renderMode = " << wdg->renderMode->currentItem() << endl; + switch (wdg->renderMode->currentItem()) + { + case 1: + opts["kde-kdjvu-rendermode"] = "black-and-white"; + break; + case 2: + opts["kde-kdjvu-rendermode"] = "foreground"; + break; + case 3: + opts["kde-kdjvu-rendermode"] = "background"; + break; + default: // 0 + opts["kde-kdjvu-rendermode"] = "color"; + } +} + + +void KPrintDialogPage_DJVUConversionOptions::setOptions( const QMap<QString,QString>& opts ) +{ + if (wdg == 0) + return; + + bool ok; + // Set PostScript Language Level, taking 2 as the default + int psLevel = opts["kde-kdjvu-pslevel"].toInt(&ok); + + if (ok && psLevel >= 1 && psLevel <= 3) + { + wdg->psLevel->setCurrentItem(psLevel - 1); + } + else + { + wdg->psLevel->setCurrentItem(1); // PostScript Level 2 + } + + // Set render mode, taking "color" as default + QString op = opts["kde-kdjvu-rendermode"]; + if (op == "black-and-white") + { + wdg->renderMode->setCurrentItem(1); + } + else + { + if (op == "foreground") + wdg->renderMode->setCurrentItem(2); + else + { + if (op == "background") + wdg->renderMode->setCurrentItem(3); + else + wdg->renderMode->setCurrentItem(0); + } + } +} + + +bool KPrintDialogPage_DJVUConversionOptions::isValid( QString& ) +{ + return true; +} diff --git a/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions.h b/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions.h new file mode 100644 index 00000000..9e3faa90 --- /dev/null +++ b/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef KPRINTDIALOGPAGE_DJVUCONVERSIONOPTIONS_H +#define KPRINTDIALOGPAGE_DJVUCONVERSIONOPTIONS_H + +#include <kdeprint/kprintdialogpage.h> + + +class kprintDialogPage_DJVUconversionoptions_basewidget; + + +// This is a fairly standard KPrintDialogPage that allows the user to +// chose page size & placement options: shrink oversized pages, and +// expand small pages + +class KPrintDialogPage_DJVUConversionOptions : public KPrintDialogPage +{ + public: + KPrintDialogPage_DJVUConversionOptions( QWidget *parent = 0, const char *name = 0 ); + + void getOptions( QMap<QString,QString>& opts, bool incldef = false ); + void setOptions( const QMap<QString,QString>& opts ); + bool isValid( QString& msg ); + + kprintDialogPage_DJVUconversionoptions_basewidget* wdg; + + private: + QVBoxLayout* kprintDialogPage_pageoptions_baseLayout; +}; + + +#endif // KPRINTDIALOGPAGE_PAGEOPTIONS_H diff --git a/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions_basewidget.ui b/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions_basewidget.ui new file mode 100644 index 00000000..fbc48750 --- /dev/null +++ b/kviewshell/plugins/djvu/kprintDialogPage_DJVUconversionoptions_basewidget.ui @@ -0,0 +1,145 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>kprintDialogPage_DJVUconversionoptions_basewidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kprintDialogPage_DJVUconversionoptions_basewidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>548</width> + <height>126</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>PostScript language level:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Render mode:</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Level 1 (almost obsolete)</string> + </property> + </item> + <item> + <property name="text"> + <string>Level 2 (default)</string> + </property> + </item> + <item> + <property name="text"> + <string>Level 3 (might print faster)</string> + </property> + </item> + <property name="name"> + <cstring>psLevel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><p>With this dialog you can choose the PostScript language level used by KViewShell. The choice of a language level can dramatically affect printing speed, but has no impact on the quality of the printout.</p> +<p><b>Level 1:</b> This is the most conservative option, because PostScript Level 1 files can be printed on all printers. The files produced are, however, extremely long, and printing can be very slow.</p> +<p><b>Level 2:</b> Level 2 PostScript files are much smaller and print much faster than Level 1 files. Level 2 files are supported by almost all printers.</p> +<p><b>Level 3:</b> Level 3 PostScript files are much smaller and print even faster than Level 2 files. However, Level 3 files are supported only by some modern printers. If Level 3 works for you, this is the best option.</p></string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Print Full Page (default)</string> + </property> + </item> + <item> + <property name="text"> + <string>Black & White</string> + </property> + </item> + <item> + <property name="text"> + <string>Foreground Only</string> + </property> + </item> + <item> + <property name="text"> + <string>Background Only</string> + </property> + </item> + <property name="name"> + <cstring>renderMode</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Good DJVU files are separated into foreground and background images. The foreground mostly contains the text. With the render mode you can decide what part of your page will be printed.</p> +<p><b>Print Full Page:</b> The full page, including foreground and background will be printed, either in color or in grayscale.</p> +<p><b>Black & White:</b> Foreground and background are printed, but only in black-and-white. If this option is chosen, the files generated will print much faster, but quality will not be as good.</p> +<p><b>Foreground Only:</b> This option is useful if the background of the page is disturbing and affects the readability of the text.</p> +<p><b>Background Only:</b> Print only the background of the page.</p></string> + </property> + </widget> + <spacer row="2" column="1"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </grid> +</widget> +<layoutdefaults spacing="6" margin="0"/> +</UI> diff --git a/kviewshell/plugins/djvu/kprintDialogPage_DJVUpageoptions.cpp b/kviewshell/plugins/djvu/kprintDialogPage_DJVUpageoptions.cpp new file mode 100644 index 00000000..cd77fa0e --- /dev/null +++ b/kviewshell/plugins/djvu/kprintDialogPage_DJVUpageoptions.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <klocale.h> +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <kdebug.h> + +#include "kprintDialogPage_DJVUpageoptions.h" + +KPrintDialogPage_DJVUPageOptions::KPrintDialogPage_DJVUPageOptions( QWidget *parent, const char *name ) + : KPrintDialogPage( parent, name ) +{ + setTitle( i18n("Page Size & Placement") ); + + kprintDialogPage_pageoptions_baseLayout = 0; + checkBox_rotate = 0; + checkBox_fitpage = 0; + + + kprintDialogPage_pageoptions_baseLayout = new QVBoxLayout( this, 11, 6, "kprintDialogPage_pageoptions_baseLayout"); + if (kprintDialogPage_pageoptions_baseLayout == 0) { + kdError(1223) << "KPrintDialogPage_DJVUPageOptions::KPrintDialogPage_DJVUPageOptions() cannot create layout" << endl; + return; + } + + checkBox_rotate = new QCheckBox( this, "checkBox_rotate" ); + if (checkBox_rotate != 0) { + checkBox_rotate->setText( i18n( "Automatically choose landscape or portrait orientation" ) ); + QToolTip::add( checkBox_rotate, i18n( "If this option is enabled, some pages might be rotated to better fit the paper size." ) ); + QWhatsThis::add( checkBox_rotate, i18n( "<qt><p>If this option is enabled, landscape or portrait orientation are automatically chosen on a " + "page-by-page basis. This makes better use of the paper and gives more visually-" + "appealing printouts.</p>" + "<p><b>Note:</b> This option overrides the Portrait/Landscape option chosen in the printer " + "properties. If this option is enabled, and if the pages in your document have different sizes, " + "then some pages might be rotated while others are not.</p></qt>" ) ); + kprintDialogPage_pageoptions_baseLayout->addWidget( checkBox_rotate ); + } + + checkBox_fitpage = new QCheckBox( this, "checkBox_shrink" ); + if (checkBox_fitpage != 0) { + checkBox_fitpage->setText( i18n( "Scale pages to fit paper size" ) ); + QToolTip::add( checkBox_fitpage, i18n( "If this option is enabled, all pages will be scaled to optimally fit the printer's paper size." ) ); + QWhatsThis::add( checkBox_fitpage, i18n( "<qt><p>If this option is enabled, all pages will be scaled to optimally fit the printer's " + "paper size.</p>" + "<p><b>Note:</b> If this option is enabled, and if the pages in your document have different sizes, " + "then different pages might be scaled by different scaling factors.</p></qt>" ) ); + kprintDialogPage_pageoptions_baseLayout->addWidget( checkBox_fitpage ); + } + + kprintDialogPage_pageoptions_baseLayout->addStretch(); + + resize( QSize(319, 166).expandedTo(minimumSizeHint()) ); + clearWState( WState_Polished ); +} + + + +void KPrintDialogPage_DJVUPageOptions::getOptions( QMap<QString,QString>& opts, bool ) +{ + // Save options, taking default values into consideration. Warning: + // The default values are also coded into setOptions() and + // kmultipage::print(..). + + if (checkBox_rotate != 0) + if (checkBox_rotate->isChecked()) + opts[ "kde-kviewshell-rotatepage" ] = "true"; + else + opts[ "kde-kviewshell-rotatepage" ] = "false"; + + if (checkBox_fitpage != 0) + if (checkBox_fitpage->isChecked()) + opts[ "kde-kdjvu-fitpage" ] = "true"; + else + opts[ "kde-kdjvu-fitpage" ] = "false"; +} + + +void KPrintDialogPage_DJVUPageOptions::setOptions( const QMap<QString,QString>& opts ) +{ + // Warning: All default values are also coded into getOptions() and + // kmultipage::print(..). + + // same for rotation + QString op = opts[ "kde-kviewshell-rotatepage" ]; + if (checkBox_rotate != 0) + checkBox_rotate->setChecked( op != "false" ); + + // Sets the fitpage option. By default, this option is not checked + op = opts[ "kde-kdjvu-fitpage" ]; + if (checkBox_fitpage != 0) + checkBox_fitpage->setChecked( op == "true" ); +} + + +bool KPrintDialogPage_DJVUPageOptions::isValid( QString& ) +{ + return true; +} diff --git a/kviewshell/plugins/djvu/kprintDialogPage_DJVUpageoptions.h b/kviewshell/plugins/djvu/kprintDialogPage_DJVUpageoptions.h new file mode 100644 index 00000000..0121c1a0 --- /dev/null +++ b/kviewshell/plugins/djvu/kprintDialogPage_DJVUpageoptions.h @@ -0,0 +1,42 @@ +// KPrintDialogPage_PageOptions.h +// +// Part of KVIEWSHELL - A framework for multipage text/gfx viewers +// +// (C) 2005 Stefan Kebekus +// Distributed under the GPL + +// Add header files alphabetically + +#ifndef KPRINTDIALOGPAGE_DJVUPAGEOPTIONS_H +#define KPRINTDIALOGPAGE_DJVUPAGEOPTIONS_H + + +#include <kdeprint/kprintdialogpage.h> + + +class QVBoxLayout; +class QCheckBox; + + +// This is a fairly standard KPrintDialogPage that allows the user to +// chose page size & placement options: shrink oversized pages, and +// expand small pages + +class KPrintDialogPage_DJVUPageOptions : public KPrintDialogPage +{ + public: + KPrintDialogPage_DJVUPageOptions( QWidget *parent = 0, const char *name = 0 ); + + void getOptions( QMap<QString,QString>& opts, bool incldef = false ); + void setOptions( const QMap<QString,QString>& opts ); + bool isValid( QString& msg ); + + QCheckBox* checkBox_rotate; + QCheckBox* checkBox_fitpage; + + private: + QVBoxLayout* kprintDialogPage_pageoptions_baseLayout; +}; + + +#endif // KPRINTDIALOGPAGE_PAGEOPTIONS_H diff --git a/kviewshell/plugins/djvu/libdjvu/Arrays.cpp b/kviewshell/plugins/djvu/libdjvu/Arrays.cpp new file mode 100644 index 00000000..5cb7b04c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Arrays.cpp @@ -0,0 +1,305 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: Arrays.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "Arrays.h" +#include "GException.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +ArrayRep::ArrayRep(int xelsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int)) : + data(0), minlo(0), maxhi(-1), lobound(0), hibound(-1), + elsize(xelsize), destroy(xdestroy), init1(xinit1), + init2(xinit2), copy(xcopy), insert(xinsert) +{ +} + +ArrayRep::ArrayRep(int xelsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int hi) : data(0), minlo(0), maxhi(-1), + lobound(0), hibound(-1), elsize(xelsize), destroy(xdestroy), init1(xinit1), + init2(xinit2), copy(xcopy), insert(xinsert) +{ + resize(0, hi); +} + +ArrayRep::ArrayRep(int xelsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int lo, int hi) : data(0), minlo(0), maxhi(-1), + lobound(0), hibound(-1), elsize(xelsize), destroy(xdestroy), init1(xinit1), + init2(xinit2), copy(xcopy), insert(xinsert) +{ + resize(lo,hi); +} + +ArrayRep::ArrayRep(const ArrayRep & arr) : data(0), minlo(0), maxhi(-1), + lobound(0), hibound(-1), elsize(arr.elsize), destroy(arr.destroy), + init1(arr.init1), init2(arr.init2), copy(arr.copy), insert(arr.insert) +{ + resize(arr.lobound, arr.hibound); + arr.copy(data, lobound-minlo, hibound-minlo, + arr.data, arr.lobound-arr.minlo, arr.hibound-arr.minlo); +} + +ArrayRep::~ArrayRep() +{ + destroy(data, lobound-minlo, hibound-minlo); + operator delete(data); + data=0; +} + +ArrayRep & +ArrayRep::operator= (const ArrayRep & rep) +{ + if (&rep == this) return *this; + empty(); + resize(rep.lobound, rep.hibound); + copy(data, lobound-minlo, hibound-minlo, + rep.data, rep.lobound-rep.minlo, rep.hibound-rep.minlo); + return *this; +} + +void +ArrayRep::resize(int lo, int hi) +{ + int nsize = hi - lo + 1; + // Validation + if (nsize < 0) + G_THROW( ERR_MSG("arrays.resize") ); + // Destruction + if (nsize == 0) + { + destroy(data, lobound-minlo, hibound-minlo); + operator delete(data); + data = 0; + lobound = minlo = lo; + hibound = maxhi = hi; + return; + } + // Simple extension + if (lo >= minlo && hi <= maxhi) + { + init1(data, lo-minlo, lobound-1-minlo); + destroy(data, lobound-minlo, lo-1-minlo); + init1(data, hibound+1-minlo, hi-minlo); + destroy(data, hi+1-minlo, hibound-minlo); + lobound = lo; + hibound = hi; + return; + } + // General case + int nminlo = minlo; + int nmaxhi = maxhi; + if (nminlo > nmaxhi) + nminlo = nmaxhi = lo; + while (nminlo > lo) { + int incr = nmaxhi - nminlo; + nminlo -= (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + while (nmaxhi < hi) { + int incr = nmaxhi - nminlo; + nmaxhi += (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + // allocate + int bytesize=elsize*(nmaxhi-nminlo+1); + void * ndata; + GPBufferBase gndata(ndata,bytesize,1); + memset(ndata, 0, bytesize); + // initialize + init1(ndata, lo-nminlo, lobound-1-nminlo); + init2(ndata, lobound-nminlo, hibound-nminlo, + data, lobound-minlo, hibound-minlo); + init1(ndata, hibound+1-nminlo, hi-nminlo); + destroy(data, lobound-minlo, hibound-minlo); + + // free and replace + void *tmp=data; + data = ndata; + ndata=tmp; + + minlo = nminlo; + maxhi = nmaxhi; + lobound = lo; + hibound = hi; +} + +void +ArrayRep::shift(int disp) +{ + lobound += disp; + hibound += disp; + minlo += disp; + maxhi += disp; +} + +void +ArrayRep::del(int n, unsigned int howmany) +{ + if (howmany == 0) + return; + if ((int)(n + howmany) > hibound +1) + G_THROW( ERR_MSG("arrays.ill_arg") ); + copy(data, n-minlo, hibound-howmany-minlo, + data, n+howmany-minlo, hibound-minlo); + destroy(data, hibound+1-howmany-minlo, hibound-minlo); + hibound = hibound - howmany; +} + +void +ArrayRep::ins(int n, const void * what, unsigned int howmany) +{ + int nhi = hibound + howmany; + if (howmany == 0) return; + if (maxhi < nhi) + { + int nmaxhi = maxhi; + while (nmaxhi < nhi) + nmaxhi += (nmaxhi < 8 ? 8 : (nmaxhi > 32768 ? 32768 : nmaxhi)); + int bytesize = elsize*(nmaxhi-minlo+1); + void *ndata; + GPBufferBase gndata(ndata,bytesize,1); + memset(ndata, 0, bytesize); + copy(ndata, lobound-minlo, hibound-minlo, + data, lobound-minlo, hibound-minlo); + destroy(data, lobound-minlo, hibound-minlo); + void *tmp=data; + data=ndata; + tmp=data; + maxhi = nmaxhi; + } + + insert(data, hibound+1-minlo, n-minlo, what, howmany); + hibound=nhi; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + + +// --------------------------------------- +// BEGIN HACK +// --------------------------------------- +// Included here to avoid dependency +// from ByteStream.o to Arrays.o + +#ifndef DO_NOT_MOVE_GET_DATA_TO_ARRAYS_CPP +#include "ByteStream.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif +TArray<char> +ByteStream::get_data(void) +{ + const int s=size(); + if(s > 0) + { + TArray<char> data(0, s-1); + readat((char*)data, s, 0); + return data; + }else + { + TArray<char> data(0, -1); + return data; + } +} + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + +// --------------------------------------- +// END HACK +// --------------------------------------- + diff --git a/kviewshell/plugins/djvu/libdjvu/Arrays.h b/kviewshell/plugins/djvu/libdjvu/Arrays.h new file mode 100644 index 00000000..b2676d5a --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Arrays.h @@ -0,0 +1,997 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: Arrays.h,v 1.10 2004/05/13 15:16:34 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _ARRAYS_H_ +#define _ARRAYS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include "GException.h" +#include "GSmartPointer.h" +#include <string.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** @name Arrays.h + + Files #"Arrays.h"# and #"Arrays.cpp"# implement three array template classes. + Class \Ref{TArray} implements an array of objects of trivial types + such as #char#, #int#, #float#, etc. It is faster than general implementation + for any type done in \Ref{DArray} because it does not cope with + element's constructors, destructors and copy operators. Although + implemented as a template, which makes it possible to incorrectly use + \Ref{TArray} with non-trivial classes, it should not be done. + + A lot of things is shared by these three arrays. That is why there are + more base classes: + \begin{itemize} + \item \Ref{ArrayBase} defines functions independent of the elements type + \item \Ref{ArrayBaseT} template class defining functions shared by + \Ref{DArray} and \Ref{TArray} + \end{itemize} + + The main difference between \Ref{GArray} (now obsolete) and these ones + is the copy-on-demand strategy, which allows you to copy array objects + without copying the real data. It's the same thing, which has been + implemented in \Ref{GString} long ago: as long as you don't try to modify + the underlying data, it may be shared between several copies of array + objects. As soon as you attempt to make any changes, a private copy + is created automatically and transparently for you - the procedure, that + we call "copy-on-demand". + + Also, please note that now there is no separate class, which does fast + sorting. Both \Ref{TArray} (dynamic array for trivial types) and + \Ref{DArray} (dynamic array for arbitrary types) can sort their elements. + + {\bf Historical comments} --- Leon chose to implement his own arrays because + the STL classes were not universally available and the compilers were + rarely able to deal with such a template galore. Later it became clear + that there is no really good reason why arrays should be derived from + containers. It was also suggested to create separate arrays implementation + for simple classes and do the copy-on-demand strategy, which would allow + to assign array objects without immediate copying of their elements. + + At this point \Ref{DArray} and \Ref{TArray} should only be used when + it is critical to have the copy-on-demand feature. The \Ref{GArray} + implementation is a lot more efficient. + + @memo Template array classes. + @author + Andrei Erofeev <eaf@geocities.com> -- Copy-on-demand implementation. + @version + #$Id: Arrays.h,v 1.10 2004/05/13 15:16:34 leonb Exp $# */ +//@{ + +// Auxiliary classes: Will be used in place of GPBase and GPEnabled objects +class _ArrayRep +{ + friend class _ArrayBase; +public: + _ArrayRep(void) : count(0) {} + _ArrayRep(const _ArrayRep &) {} + virtual ~_ArrayRep(void) {} + + _ArrayRep & operator=(const _ArrayRep &) { return *this; } + + int get_count(void) const { return count; } +private: + int count; + + void ref(void) { count++; } + void unref(void) { if (--count==0) delete this; } +}; + +class _ArrayBase +{ +public: + _ArrayBase(void) : rep(0) {} + _ArrayBase(const _ArrayBase & ab) : rep(0) + { + if (ab.rep) ab.rep->ref(); + rep=ab.rep; + } + _ArrayBase(_ArrayRep * ar) : rep(0) + { + if (ar) ar->ref(); + rep=ar; + } + virtual ~_ArrayBase(void) + { + if (rep) { rep->unref(); rep=0; } + } + + _ArrayRep * get(void) const { return rep; } + _ArrayBase & assign(_ArrayRep * ar) + { + if (ar) ar->ref(); + if (rep) rep->unref(); + rep=ar; + return *this; + } + _ArrayBase & operator=(const _ArrayBase & ab) { return assign(ab.rep); } + bool operator==(const _ArrayBase & ab) { return rep==ab.rep; } +private: + _ArrayRep * rep; +}; + +// Internal "Array repository" holding the pointer to the actual data, +// data bounds, etc. It copes with data elements with the help of five +// static functions which pointers are supposed to be passed to the +// constructor. +class ArrayRep : public _ArrayRep +{ +public: + ArrayRep(int elsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int)); + ArrayRep(int elsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int hibound); + ArrayRep(int elsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int lobound, int hibound); + ArrayRep(const ArrayRep & rep); + + virtual ~ArrayRep(); + + // Following is the standard interface to DArray. DArray will call these + // functions to access data. + int size() const; + int lbound() const; + int hbound() const; + + void empty(); + void touch(int n); + void resize(int lobound, int hibound); + void shift(int disp); + void del(int n, unsigned int howmany=1); + + // ins() is an exception. It does it job only partially. + // The derived class is supposed to finish insertion. + void ins(int n, const void * what, unsigned int howmany); + + ArrayRep & operator=(const ArrayRep & rep); + + // All data is public because DArray... classes will need access to it + void *data; + int minlo; + int maxhi; + int lobound; + int hibound; + int elsize; +private: + // These functions can't be virtual as they're called from + // constructors and destructors :(( + // destroy(): should destroy elements in data[] array from 'lo' to 'hi' + void (* destroy)(void * data, int lo, int hi); + // init1(): should initialize elements in data[] from 'lo' to 'hi' + // using default constructors + void (* init1)(void * data, int lo, int hi); + // init2(): should initialize elements in data[] from 'lo' to 'hi' + // using corresponding elements from src[] (copy constructor) + void (* init2)(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + // copy(): should copy elements from src[] to dst[] (copy operator) + void (* copy)(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi); + // insert(): should insert '*what' at position 'where' 'howmany' times + // into array data[] having 'els' initialized elements + void (* insert)(void * data, int els, int where, const void * what, + int howmany); +}; + +inline int +ArrayRep::size() const +{ + return hibound - lobound + 1; +} + +inline int +ArrayRep::lbound() const +{ + return lobound; +} + +inline int +ArrayRep::hbound() const +{ + return hibound; +} + +inline void +ArrayRep::empty() +{ + resize(0, -1); +} + +inline void +ArrayRep::touch(int n) +{ + if (hibound < lobound) + { + resize(n,n); + } else + { + int nlo = lobound; + int nhi = hibound; + if (n < nlo) nlo = n; + if (n > nhi) nhi = n; + resize(nlo, nhi); + } +} + +/** Dynamic array base class. + This is an auxiliary base class for \Ref{DArray} and \Ref{TArray} + implementing some shared functions independent of the type of array + elements. It's not supposed to be constructed by hands. Use \Ref{DArray} + and \Ref{TArray} instead. + */ + +class ArrayBase : protected _ArrayBase +{ +protected: + void check(void); + void detach(void); + + ArrayBase(void) {}; +public: + /// Returns the number of elements in the array + int size() const; + /** Returns the lower bound of the valid subscript range. */ + int lbound() const; + /** Returns the upper bound of the valid subscript range. */ + int hbound() const; + /** Erases the array contents. All elements in the array are destroyed. + The valid subscript range is set to the empty range. */ + void empty(); + /** Extends the subscript range so that is contains #n#. + This function does nothing if #n# is already int the valid subscript range. + If the valid range was empty, both the lower bound and the upper bound + are set to #n#. Otherwise the valid subscript range is extended + to encompass #n#. This function is very handy when called before setting + an array element: + \begin{verbatim} + int lineno=1; + DArray<GString> a; + while (! end_of_file()) { + a.touch[lineno]; + a[lineno++] = read_a_line(); + } + \end{verbatim} + */ + void touch(int n); + /** Resets the valid subscript range to #0#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #hibound# to + #-1# resets the valid subscript range to the empty range. + @param hibound upper bound of the new subscript range. */ + void resize(int hibound); + /** Resets the valid subscript range to #lobound#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #lobound# to #0# and + #hibound# to #-1# resets the valid subscript range to the empty range. + @param lobound lower bound of the new subscript range. + @param hibound upper bound of the new subscript range. */ + void resize(int lobound, int hibound); + /** Shifts the valid subscript range. Argument #disp# is added to both + bounds of the valid subscript range. Array elements previously + located at subscript #x# will now be located at subscript #x+disp#. */ + void shift(int disp); + /** Deletes array elements. The array elements corresponding to + subscripts #n#...#n+howmany-1# are destroyed. All array elements + previously located at subscripts greater or equal to #n+howmany# + are moved to subscripts starting with #n#. The new subscript upper + bound is reduced in order to account for this shift. + @param n subscript of the first element to delete. + @param howmany number of elements to delete. */ + void del(int n, unsigned int howmany=1); + + virtual ~ArrayBase(void) {}; +}; + +inline void +ArrayBase::detach(void) +{ + ArrayRep * new_rep=new ArrayRep(*(ArrayRep *) get()); + assign(new_rep); +} + +inline void +ArrayBase::check(void) +{ + if (get()->get_count()>1) detach(); +} + +inline int +ArrayBase::size() const +{ + return ((const ArrayRep *) get())->size(); +} + +inline int +ArrayBase::lbound() const +{ + return ((const ArrayRep *) get())->lobound; +} + +inline int +ArrayBase::hbound() const +{ + return ((const ArrayRep *) get())->hibound; +} + +inline void +ArrayBase::empty() +{ + check(); + ((ArrayRep *) get())->empty(); +} + +inline void +ArrayBase::resize(int lo, int hi) +{ + check(); + ((ArrayRep *) get())->resize(lo, hi); +} + +inline void +ArrayBase::resize(int hi) +{ + resize(0, hi); +} + +inline void +ArrayBase::touch(int n) +{ + check(); + ((ArrayRep *) get())->touch(n); +} + +inline void +ArrayBase::shift(int disp) +{ + check(); + ((ArrayRep *) get())->shift(disp); +} + +inline void +ArrayBase::del(int n, unsigned int howmany) +{ + check(); + + ((ArrayRep *) get())->del(n, howmany); +} + +/** Dynamic array template base class. + This is an auxiliary template base class for \Ref{DArray} and \Ref{TArray} + implementing some shared functions which {\em depend} on the type of + the array elements (this is contrary to \Ref{ArrayBase}). + It's not supposed to be constructed by hands. Use \Ref{DArray} and + \Ref{TArray} instead. + */ + +template <class TYPE> +class ArrayBaseT : public ArrayBase +{ +public: + virtual ~ArrayBaseT(void) {}; + + /** Returns a reference to the array element for subscript #n#. This + reference can be used for both reading (as "#a[n]#") and writing (as + "#a[n]=v#") an array element. This operation will not extend the valid + subscript range: an exception \Ref{GException} is thrown if argument #n# + is not in the valid subscript range. */ + TYPE& operator[](int n); + /** Returns a constant reference to the array element for subscript #n#. + This reference can only be used for reading (as "#a[n]#") an array + element. This operation will not extend the valid subscript range: an + exception \Ref{GException} is thrown if argument #n# is not in the valid + subscript range. This variant of #operator[]# is necessary when dealing + with a #const DArray<TYPE>#. */ + const TYPE& operator[](int n) const; + + /** Returns a pointer for reading or writing the array elements. This + pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator TYPE* (); + /** Returns a pointer for reading (but not modifying) the array elements. + This pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator const TYPE* () const; + +#ifndef __MWERKS__ //MCW can't compile + operator const TYPE* (); +#endif + /** Insert new elements into an array. This function inserts + #howmany# elements at position #n# into the array. The initial value #val# + is copied into the new elements. All array elements previously located at subscripts + #n# and higher are moved to subscripts #n+howmany# and higher. The upper bound of the + valid subscript range is increased in order to account for this shift. + @param n subscript of the first inserted element. + @param val initial value of the new elements. + @param howmany number of elements to insert. */ + void ins(int n, const TYPE &val, unsigned int howmany=1); + + /** Sort array elements. Sort all array elements in ascending order. Array + elements are compared using the less-or-equal comparison operator for + type #TYPE#. */ + void sort(); + /** Sort array elements in subscript range #lo# to #hi#. Sort all array + elements whose subscripts are in range #lo#..#hi# in ascending order. + The other elements of the array are left untouched. An exception is + thrown if arguments #lo# and #hi# are not in the valid subscript range. + Array elements are compared using the less-or-equal comparison operator + for type #TYPE#. + @param lo low bound for the subscripts of the elements to sort. + @param hi high bound for the subscripts of the elements to sort. */ + void sort(int lo, int hi); +protected: + ArrayBaseT(void) {}; +private: + // Callbacks called from ArrayRep + static void destroy(void * data, int lo, int hi); + static void init1(void * data, int lo, int hi); + static void init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + static void copy(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi); + static void insert(void * data, int els, int where, + const void * what, int howmany); +}; + +template <class TYPE> inline +ArrayBaseT<TYPE>::operator TYPE* () +{ + check(); + + ArrayRep * rep=(ArrayRep *) get(); + return &((TYPE *) rep->data)[-rep->minlo]; +} + +#ifndef __MWERKS__ //MCW can't compile +template <class TYPE> inline +ArrayBaseT<TYPE>::operator const TYPE* () +{ + const ArrayRep * rep=(const ArrayRep *) get(); + return &((const TYPE *) rep->data)[-rep->minlo]; +} +#endif + +template <class TYPE> inline +ArrayBaseT<TYPE>::operator const TYPE* () const +{ + const ArrayRep * rep=(const ArrayRep *) get(); + return &((const TYPE *) rep->data)[-rep->minlo]; +} + +template <class TYPE> inline TYPE& +ArrayBaseT<TYPE>::operator[](int n) +{ + check(); + + ArrayRep * rep=(ArrayRep *) get(); + if (n<rep->lobound || n>rep->hibound) + G_THROW( ERR_MSG("arrays.ill_sub") ); + return ((TYPE *) rep->data)[n - rep->minlo]; +} + +template <class TYPE> inline const TYPE& +ArrayBaseT<TYPE>::operator[](int n) const +{ + const ArrayRep * rep=(const ArrayRep *) get(); + if (n<rep->lobound || n>rep->hibound) + G_THROW( ERR_MSG("arrays.ill_sub") ); + return ((const TYPE *) rep->data)[n - rep->minlo]; +} + +template <class TYPE> inline void +ArrayBaseT<TYPE>::ins(int n, const TYPE &val, unsigned int howmany) +{ + check(); + + ((ArrayRep *) get())->ins(n, &val, howmany); +} + +template <class TYPE> void +ArrayBaseT<TYPE>::sort() +{ + sort(lbound(), hbound()); +} + +template <class TYPE> void +ArrayBaseT<TYPE>::sort(int lo, int hi) +{ + if (hi <= lo) + return; + // Test for insertion sort (optimize!) + if (hi <= lo + 20) + { + for (int i=lo+1; i<=hi; i++) + { + int j = i; + TYPE tmp = (*this)[i]; + while ((--j>=lo) && !((*this)[j]<=tmp)) + (*this)[j+1] = (*this)[j]; + (*this)[j+1] = tmp; + } + return; + } + // -- determine suitable quick-sort pivot + TYPE tmp = (*this)[lo]; + TYPE pivot = (*this)[(lo+hi)/2]; + if (pivot <= tmp) + { tmp = pivot; pivot=(*this)[lo]; } + if ((*this)[hi] <= tmp) + { pivot = tmp; } + else if ((*this)[hi] <= pivot) + { pivot = (*this)[hi]; } + // -- partition set + int h = hi; + int l = lo; + while (l < h) + { + while (! (pivot <= (*this)[l])) l++; + while (! ((*this)[h] <= pivot)) h--; + if (l < h) + { + tmp = (*this)[l]; + (*this)[l] = (*this)[h]; + (*this)[h] = tmp; + l = l+1; + h = h-1; + } + } + // -- recursively restart + sort(lo, h); + sort(l, hi); +} + +/** Dynamic array for simple types. + Template class #TArray<TYPE># implements an array of + elements of {\em simple} type #TYPE#. {\em Simple} means that the type + may be #char#, #int#, #float# etc. The limitation is imposed by the + way in which the #TArray# is working with its elements: it's not trying + to execute elements' constructors, destructors or copy operators. It's + just doing bitwise copy. Except for this it's pretty much the same as + \Ref{DArray}. + + Please note that most of the methods are implemented in the base classes + \Ref{ArrayBase} and \Ref{ArrayBaseT}. +*/ + +template <class TYPE> +class TArray : public ArrayBaseT<TYPE> { +public: + /** Constructs an empty array. The valid subscript range is initially + empty. Member function #touch# and #resize# provide convenient ways + to enlarge the subscript range. */ + TArray(); + /** Constructs an array with subscripts in range 0 to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param hibound upper bound of the initial subscript range. */ + TArray(int hibound); + /** Constructs an array with subscripts in range #lobound# to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param lobound lower bound of the initial subscript range. + @param hibound upper bound of the initial subscript range. */ + TArray(int lobound, int hibound); + + virtual ~TArray() {}; +private: + // Callbacks called from ArrayRep + static void destroy(void * data, int lo, int hi); + static void init1(void * data, int lo, int hi); + static void init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + static void insert(void * data, int els, int where, + const void * what, int howmany); +}; + +template <class TYPE> void +TArray<TYPE>::destroy(void * data, int lo, int hi) +{ +} + +template <class TYPE> void +TArray<TYPE>::init1(void * data, int lo, int hi) +{ +} + +template <class TYPE> void +TArray<TYPE>::init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi) +{ + if (data && src) + { + int els=hi-lo+1; + if (els>src_hi-src_lo+1) els=src_hi-src_lo+1; + if (els>0) + memmove((void *) &((TYPE *) data)[lo], + (void *) &((TYPE *) src)[src_lo], els*sizeof(TYPE)); + }; +} + +// inline removed +template <class TYPE> void +TArray<TYPE>::insert(void * data, int els, int where, + const void * what, int howmany) +{ + memmove(((TYPE *) data)+where+howmany, + ((TYPE *) data)+where, sizeof(TYPE)*(els-where)); + for(int i=0;i<howmany;i++) + ((TYPE *) data)[where+i]=*(TYPE *) what; +} + +template <class TYPE> +TArray<TYPE>::TArray () +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, init2, insert)); +} + +template <class TYPE> +TArray<TYPE>::TArray(int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, init2, insert, hi)); +} + +template <class TYPE> +TArray<TYPE>::TArray(int lo, int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, init2, insert, lo, hi)); +} + +//inline removal ends + +/** Dynamic array for general types. + Template class #DArray<TYPE># implements an array of + elements of type #TYPE#. Each element is identified by an integer + subscript. The valid subscripts range is defined by dynamically + adjustable lower- and upper-bounds. Besides accessing and setting + elements, member functions are provided to insert or delete elements at + specified positions. + + This template class must be able to access + \begin{itemize} + \item a null constructor #TYPE::TYPE()#, + \item a copy constructor #TYPE::TYPE(const TYPE &)#, + \item and a copy operator #TYPE & operator=(const TYPE &)#. + \end{itemize} + + The class offers "copy-on-demand" policy, which means that when you + copy the array object, array elements will stay intact as long as you + don't try to modify them. As soon as you make an attempt to change + array contents, the copying is done automatically and transparently + for you - the procedure that we call "copy-on-demand". This is the main + difference between this class and \Ref{GArray} (now obsolete) + + Please note that most of the methods are implemented in the base classes + \Ref{ArrayBase} and \Ref{ArrayBaseT}. +*/ + +template <class TYPE> +class DArray : public ArrayBaseT<TYPE> { +public: + /** Constructs an empty array. The valid subscript range is initially + empty. Member function #touch# and #resize# provide convenient ways + to enlarge the subscript range. */ + DArray(void); + /** Constructs an array with subscripts in range 0 to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param hibound upper bound of the initial subscript range. */ + DArray(const int hibound); + /** Constructs an array with subscripts in range #lobound# to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param lobound lower bound of the initial subscript range. + @param hibound upper bound of the initial subscript range. */ + DArray(const int lobound, const int hibound); + + virtual ~DArray() {}; +private: + // Callbacks called from ArrayRep + static void destroy(void * data, int lo, int hi); + static void init1(void * data, int lo, int hi); + static void init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + static void copy(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi); + static void insert(void * data, int els, int where, + const void * what, int howmany); +}; + +template <class TYPE> void +DArray<TYPE>::destroy(void * data, int lo, int hi) +{ + if (data) + for(int i=lo;i<=hi;i++) + ((TYPE *) data)[i].TYPE::~TYPE(); +} + +template <class TYPE> void +DArray<TYPE>::init1(void * data, int lo, int hi) +{ + if (data) + for(int i=lo;i<=hi;i++) + new ((void *) &((TYPE *) data)[i]) TYPE; +} + +template <class TYPE> void +DArray<TYPE>::init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi) +{ + if (data && src) + { + int i, j; + for(i=lo, j=src_lo;i<=hi && j<=src_hi;i++, j++) + new ((void *) &((TYPE *) data)[i]) TYPE(((TYPE *) src)[j]); + }; +} + +template <class TYPE> void +DArray<TYPE>::copy(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi) +{ + if (dst && src) + { + int i, j; + for(i=dst_lo, j=src_lo;i<=dst_hi && j<=src_hi;i++, j++) + ((TYPE *) dst)[i]=((TYPE *) src)[j]; + }; +} + +template <class TYPE> inline void +DArray<TYPE>::insert(void * data, int els, int where, + const void * what, int howmany) +{ + // Now do the insertion + TYPE * d=(TYPE *) data; + + int i; + for (i=els+howmany-1; i>=els; i--) + { + if (i-where >= (int)howmany) + new ((void*) &d[i]) TYPE (d[i-howmany]); + else + new ((void*) &d[i]) TYPE (*(TYPE *) what); + } + + for (i=els-1; i>=where; i--) + { + if (i-where >= (int)howmany) + d[i] = d[i-howmany]; + else + d[i] = *(TYPE *) what; + } +} + +template <class TYPE> inline +DArray<TYPE>::DArray () +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, copy, insert)); +} + +template <class TYPE> inline +DArray<TYPE>::DArray(const int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, copy, insert, hi)); +} + +template <class TYPE> inline +DArray<TYPE>::DArray(const int lo, const int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, copy, insert, lo, hi)); +} + +/** Dynamic array for \Ref{GPBase}d classes. + + There are many situations when it's necessary to create arrays of + \Ref{GP} pointers. For example, #DArray<GP<Dialog> ># or #DArray<GP<Button> >#. + This would result in compilation of two instances of \Ref{DArray} because + from the viewpoint of the compiler there are two different classes used + as array elements: #GP<Dialog># and #GP<Button>#. In reality though, + all \Ref{GP} pointers have absolutely the same binary structure because + they are derived from \Ref{GPBase} class and do not add any variables + or virtual functions. That's why it's possible to instantiate \Ref{DArray} + only once for \Ref{GPBase} elements and then just cast types. + + To implement this idea we have created this #DPArray<TYPE># class, + which can be used instead of #DArray<GP<TYPE> >#. It behaves absolutely + the same way as \Ref{DArray} but has one big advantage: overhead of + using #DPArray# with one more type is negligible. + */ +template <class TYPE> +class DPArray : public DArray<GPBase> { +public: + // -- CONSTRUCTORS + DPArray(); + DPArray(int hibound); + DPArray(int lobound, int hibound); + DPArray(const DPArray<TYPE> &gc); + // -- DESTRUCTOR + virtual ~DPArray(); + // -- ACCESS + GP<TYPE>& operator[](int n); + const GP<TYPE>& operator[](int n) const; + // -- CONVERSION + operator GP<TYPE>* (); + +#ifndef __MWERKS__ //MCW can't compile + operator const GP<TYPE>* (); +#endif + + operator const GP<TYPE>* () const; + // -- ALTERATION + void ins(int n, const GP<TYPE> &val, unsigned int howmany=1); + DPArray<TYPE>& operator= (const DPArray &ga); +}; + +template<class TYPE> +DPArray<TYPE>::DPArray() {} + +template<class TYPE> +DPArray<TYPE>::DPArray(int hibound) : + DArray<GPBase>(hibound) {} + +template<class TYPE> +DPArray<TYPE>::DPArray(int lobound, int hibound) : + DArray<GPBase>(lobound, hibound) {} + +template<class TYPE> +DPArray<TYPE>::DPArray(const DPArray<TYPE> &gc) : + DArray<GPBase>(gc) {} + +template<class TYPE> +DPArray<TYPE>::~DPArray() {} + +template<class TYPE> +inline GP<TYPE> & +DPArray<TYPE>::operator[](int n) +{ + return (GP<TYPE> &) DArray<GPBase>::operator[](n); +} + +template<class TYPE> +inline const GP<TYPE> & +DPArray<TYPE>::operator[](int n) const +{ + return (const GP<TYPE> &) DArray<GPBase>::operator[](n); +} + +template<class TYPE> +inline DPArray<TYPE>::operator GP<TYPE>* () +{ + return (GP<TYPE> *) DArray<GPBase>::operator GPBase*(); +} + +#ifndef __MWERKS__ //MCW can't compile +template<class TYPE> +inline DPArray<TYPE>::operator const GP<TYPE>* () +{ + return (const GP<TYPE> *) DArray<GPBase>::operator const GPBase*(); +} +#endif + +template<class TYPE> +inline DPArray<TYPE>::operator const GP<TYPE>* () const +{ + return (const GP<TYPE> *) DArray<GPBase>::operator const GPBase*(); +} + +template<class TYPE> +inline void +DPArray<TYPE>::ins(int n, const GP<TYPE> & val, unsigned int howmany) +{ + DArray<GPBase>::ins(n, val, howmany); +} + +template<class TYPE> +inline DPArray<TYPE> & +DPArray<TYPE>::operator= (const DPArray &ga) +{ + DArray<GPBase>::operator=(ga); + return *this; +} + +// ------------ THE END + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/BSByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/BSByteStream.cpp new file mode 100644 index 00000000..77334a45 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/BSByteStream.cpp @@ -0,0 +1,465 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: BSByteStream.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 07/1998 + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "BSByteStream.h" +#undef BSORT_TIMER +#ifdef BSORT_TIMER +#include "GOS.h" +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class BSByteStream::Decode : public BSByteStream +{ +public: + /** Creates a Static object for allocating the memory area of + length #sz# starting at address #buffer#. */ + Decode(GP<ByteStream> bs); + ~Decode(); + void init(void); + // Virtual functions + virtual size_t read(void *buffer, size_t sz); + virtual void flush(void); +protected: + unsigned int decode(void); +private: + bool eof; +}; + +// ======================================== +// --- Assertion + +#define ASSERT(expr) do{if(!(expr))G_THROW("assertion ("#expr") failed");}while(0) + +// ======================================== +// --- Construction + +BSByteStream::BSByteStream(GP<ByteStream> xbs) +: offset(0), bptr(0), blocksize(0), size(0), bs(xbs), + gbs(xbs), gdata(data,0) +{ + // Initialize context array + memset(ctx, 0, sizeof(ctx)); +} + +BSByteStream::~BSByteStream() {} + +BSByteStream::Decode::Decode(GP<ByteStream> xbs) +: BSByteStream(xbs), eof(false) {} + +void +BSByteStream::Decode::init(void) +{ + gzp=ZPCodec::create(gbs,false,true); +} + +BSByteStream::Decode::~Decode() {} + +GP<ByteStream> +BSByteStream::create(GP<ByteStream> xbs) +{ + BSByteStream::Decode *rbs=new BSByteStream::Decode(xbs); + GP<ByteStream> retval=rbs; + rbs->init(); + return retval; +} + +void +BSByteStream::Decode::flush() +{ + size = bptr = 0; +} + +// ======================================== +// -- Decoding + + +static int +decode_raw(ZPCodec &zp, int bits) +{ + int n = 1; + const int m = (1<<bits); + while (n < m) + { + const int b = zp.decoder(); + n = (n<<1) | b; + } + return n - m; +} + +static inline int +decode_binary(ZPCodec &zp, BitContext *ctx, int bits) +{ + int n = 1; + int m = (1<<bits); + ctx = ctx - 1; + while (n < m) + { + int b = zp.decoder(ctx[n]); + n = (n<<1) | b; + } + return n - m; +} + + +static inline void +assignmtf(unsigned char xmtf[256]) +{ + static const unsigned char mtf[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, + 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, + 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, + 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, + 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, + 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, + 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, + 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, + 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, + 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, + 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; + memcpy(xmtf,mtf,sizeof(mtf)); +} + +unsigned int +BSByteStream::Decode::decode(void) +{ + ///////////////////////////////// + //////////// Decode input stream + + int i; + // Decode block size + ZPCodec &zp=*gzp; + size = decode_raw(zp, 24); + if (!size) + return 0; + if (size>MAXBLOCK*1024) + G_THROW( ERR_MSG("ByteStream.corrupt") ); + // Allocate + if ((int)blocksize < size) + { + blocksize = size; + if (data) + { + gdata.resize(0); + } + } + if (! data) + gdata.resize(blocksize); + // Decode Estimation Speed + int fshift = 0; + if (zp.decoder()) + { + fshift += 1; + if (zp.decoder()) + fshift += 1; + } + // Prepare Quasi MTF + static const unsigned char xmtf[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, + 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, + 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, + 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, + 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, + 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, + 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, + 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, + 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, + 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, + 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; + unsigned char mtf[256]; + memcpy(mtf,xmtf,sizeof(xmtf)); + unsigned int freq[FREQMAX]; + memset(freq,0,sizeof(freq)); + int fadd = 4; + // Decode + int mtfno = 3; + int markerpos = -1; + for (i=0; i<size; i++) + { + int ctxid = CTXIDS-1; + if (ctxid>mtfno) ctxid=mtfno; + BitContext *cx = ctx; + if (zp.decoder(cx[ctxid])) + { mtfno=0; data[i]=mtf[mtfno]; goto rotate; } + cx+=CTXIDS; + if (zp.decoder(cx[ctxid])) + { mtfno=1; data[i]=mtf[mtfno]; goto rotate; } + cx+=CTXIDS; + if (zp.decoder(cx[0])) + { mtfno=2+decode_binary(zp,cx+1,1); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+1; + if (zp.decoder(cx[0])) + { mtfno=4+decode_binary(zp,cx+1,2); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+3; + if (zp.decoder(cx[0])) + { mtfno=8+decode_binary(zp,cx+1,3); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+7; + if (zp.decoder(cx[0])) + { mtfno=16+decode_binary(zp,cx+1,4); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+15; + if (zp.decoder(cx[0])) + { mtfno=32+decode_binary(zp,cx+1,5); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+31; + if (zp.decoder(cx[0])) + { mtfno=64+decode_binary(zp,cx+1,6); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+63; + if (zp.decoder(cx[0])) + { mtfno=128+decode_binary(zp,cx+1,7); data[i]=mtf[mtfno]; goto rotate; } + mtfno=256; + data[i]=0; + markerpos=i; + continue; + // Rotate mtf according to empirical frequencies (new!) + rotate: + // Adjust frequencies for overflow + int k; + fadd = fadd + (fadd>>fshift); + if (fadd > 0x10000000) + { + fadd >>= 24; + freq[0] >>= 24; + freq[1] >>= 24; + freq[2] >>= 24; + freq[3] >>= 24; + for (k=4; k<FREQMAX; k++) + freq[k] = freq[k]>>24; + } + // Relocate new char according to new freq + unsigned int fc = fadd; + if (mtfno < FREQMAX) + fc += freq[mtfno]; + for (k=mtfno; k>=FREQMAX; k--) + mtf[k] = mtf[k-1]; + for (; k>0 && fc>=freq[k-1]; k--) + { + mtf[k] = mtf[k-1]; + freq[k] = freq[k-1]; + } + mtf[k] = data[i]; + freq[k] = fc; + } + + + ///////////////////////////////// + ////////// Reconstruct the string + + if (markerpos<1 || markerpos>=size) + G_THROW( ERR_MSG("ByteStream.corrupt") ); + // Allocate pointers + unsigned int *posn; + GPBuffer<unsigned int> gposn(posn,blocksize); + memset(posn, 0, sizeof(unsigned int)*size); + // Prepare count buffer + int count[256]; + for (i=0; i<256; i++) + count[i] = 0; + // Fill count buffer + for (i=0; i<markerpos; i++) + { + unsigned char c = data[i]; + posn[i] = (c<<24) | (count[c] & 0xffffff); + count[c] += 1; + } + for (i=markerpos+1; i<size; i++) + { + unsigned char c = data[i]; + posn[i] = (c<<24) | (count[c] & 0xffffff); + count[c] += 1; + } + // Compute sorted char positions + int last = 1; + for (i=0; i<256; i++) + { + int tmp = count[i]; + count[i] = last; + last += tmp; + } + // Undo the sort transform + i = 0; + last = size-1; + while (last>0) + { + unsigned int n = posn[i]; + unsigned char c = (posn[i]>>24); + data[--last] = c; + i = count[c] + (n & 0xffffff); + } + // Free and check + if (i != markerpos) + G_THROW( ERR_MSG("ByteStream.corrupt") ); + return size; +} + + + +// ======================================== +// -- ByteStream interface + + + +long +BSByteStream::tell() const +{ + return offset; +} + +size_t +BSByteStream::Decode::read(void *buffer, size_t sz) +{ + if (eof) + return 0; + // Loop + int copied = 0; + while (sz>0 && !eof) + { + // Decode if needed + if (!size) + { + bptr = 0; + if (! decode()) + { + size = 1 ; + eof = true; + } + size -= 1; + } + // Compute remaining + int bytes = size; + if (bytes > (int)sz) + bytes = sz; + // Transfer + if (buffer && bytes) + { + memcpy(buffer, data+bptr, bytes); + buffer = (void*)((char*)buffer + bytes); + } + size -= bytes; + bptr += bytes; + sz -= bytes; + copied += bytes; + offset += bytes; + } + // Return copied bytes + return copied; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/BSByteStream.h b/kviewshell/plugins/djvu/libdjvu/BSByteStream.h new file mode 100644 index 00000000..6a985cdf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/BSByteStream.h @@ -0,0 +1,275 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: BSByteStream.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _BSBYTESTREAM_H +#define _BSBYTESTREAM_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name BSByteStream.h + + Files #"BSByteStream.h"# and #"BSByteStream.cpp"# implement a very compact + general purpose compressor based on the Burrows-Wheeler transform. The + utility program \Ref{bzz} provides a front-end for this class. Although + this compression model is not currently used in DjVu files, it may be used + in the future for encoding textual data chunks. + + {\bf Algorithms} --- The Burrows-Wheeler transform (also named Block-Sorting) + is performed using a combination of the Karp-Miller-Rosenberg and the + Bentley-Sedgewick algorithms. This is comparable to (Sadakane, DCC 98) + with a slightly more flexible ranking scheme. Symbols are then ordered + according to a running estimate of their occurrence frequencies. The + symbol ranks are then coded using a simple fixed tree and the + \Ref{ZPCodec} binary adaptive coder. + + {\bf Performances} --- The basic algorithm is mostly similar to those + implemented in well known compressors like #bzip# or #bzip2# + (\URL{http://www.muraroa.demon.co.uk}). The adaptive binary coder however + generates small differences. The adaptation noise may cost up to 5\% in + file size, but this penalty is usually offset by the benefits of + adaptation. This is good when processing large and highly structured + files like spreadsheet files. Compression and decompression speed is + about twice slower than #bzip2# but the sorting algorithms is more + robust. Unlike #bzip2# (as of August 1998), this code can compress half a + megabyte of "abababab...." in bounded time. + + Here are some comparative results (in bits per character) obtained on the + Canterbury Corpus (\URL{http://corpus.canterbury.ac.nz}) as of August + 1998. The BSByteStream performance on the single spreadsheet file #Excl# + moves #bzz#'s weighted average ahead of much more sophisticated methods, + like Suzanne Bunton's #fsmxBest# system + \URL{http://corpus.canterbury.ac.nz/methodinfo/fsmx.html}. This result + will not last very long. + + {\footnotesize + \begin{tabular}{lccccccccccccc} + & text & fax & Csrc & Excl & SPRC & tech + & poem & html & lisp & man & play & Weighted & Average \\ + compress + & 3.27 & 0.97 & 3.56 & 2.41 & 4.21 & 3.06 + & 3.38 & 3.68 & 3.90 & 4.43 & 3.51 + & 2.55 & 3.31 \\ + gzip -9 + & 2.85 & 0.82 & 2.24 & 1.63 & 2.67 & 2.71 + & 3.23 & 2.59 & 2.65 & 3.31 & 3.12 + & 2.08 & 2.53 \\ + bzip2 -9 + & 2.27 & 0.78 & 2.18 & 1.01 & 2.70 & 2.02 + & 2.42 & 2.48 & 2.79 & 3.33 & 2.53 + & 1.54 & 2.23 \\ + ppmd + & 2.31 & 0.99 & 2.11 & 1.08 & 2.68 & 2.19 + & 2.48 & 2.38 & 2.43 & 3.00 & 2.53 + & 1.65 & 2.20 \\ + fsmx + & {\bf 2.10} & 0.79 & {\bf 1.89} & 1.48 & {\bf 2.52} & {\bf 1.84} + & {\bf 2.21} & {\bf 2.24} & {\bf 2.29} & {\bf 2.91} & {\bf 2.35} + & 1.63 & {\bf 2.06} \\ + {\bf bzz} + & 2.25 & {\bf 0.76} & 2.13 & {\bf 0.78} & 2.67 & 2.00 + & 2.40 & 2.52 & 2.60 & 3.19 & 2.52 + & {\bf 1.44} & 2.16 + \end{tabular} + } + + Note that the DjVu people have several entries in this table. Program + #compress# was written some time ago by Joe Orost + (\URL{http://www.research.att.com/info/orost}). The #ppmc# method, (a + precursor of #ppmd#) was created by Paul Howard + (\URL{http://www.research.att.com/info/pgh}). The #bzz# program is just + below your eyes. + + @author + L\'eon Bottou <leonb@research.att.com> -- Initial implementation\\ + Andrei Erofeev <eaf@geocities.com> -- Improved Block Sorting algorithm. + @memo + Simple Burrows-Wheeler general purpose compressor. + @version + #$Id: BSByteStream.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + +#include "ByteStream.h" +#include "GException.h" +#include "ZPCodec.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** Performs bzz compression/decompression. + + Class #BSByteStream# defines a \Ref{ByteStream} which transparently + performs the BZZ compression/decompression. The constructor of class + \Ref{BSByteStream} takes another \Ref{ByteStream} as argument. Any data + written to the BSByteStream is compressed and written to this second + ByteStream. Any data read from the BSByteStream is internally generated by + decompressing data read from the second ByteStream. + + Program \Ref{bzz} demonstrates how to use this class. All the hard work + is achieved by a simple ByteStream to ByteStream copy, as shown below. + \begin{verbatim} + GP<ByteStream> in=ByteStream::create(infile,"rb"); + GP<ByteStream> out=ByteStream::create(outfile,"wb"); + if (encoding) { + BSByteStream bsb(out, blocksize); + bsb.copy(*in); + } else { + BSByteStream bsb(in); + out->copy(bsb); + } + \end{verbatim} + Due to the block oriented nature of the Burrows-Wheeler transform, there + is a very significant latency between the data input and the data output. + You can use function #flush# to force data output at the expense of + compression efficiency. + + You should never directly access a ByteStream object connected to a valid + BSByteStream object. The ByteStream object can be accessed again after the + destruction of the BSByteStream object. Note that the encoder always + flushes its internal buffers and writes a few final code bytes when the + BSByteStream object is destroyed. Note also that the decoder often reads + a few bytes beyond the last code byte written by the encoder. This lag + means that you must reposition the ByteStream after the destruction of the + BSByteStream object and before re-using the ByteStream object (see + \Ref{IFFByteStream}.) +*/ +class BSByteStream : public ByteStream +{ +public: +// Limits on block sizes + enum { MINBLOCK=10, MAXBLOCK=4096 }; + +// Sorting tresholds + enum { FREQMAX=4, CTXIDS=3 }; + + class Decode; + class Encode; +protected: + BSByteStream(GP<ByteStream> bs); + +public: + /** Creates a BSByteStream. + The BSByteStream will be used for decompressing data. + \begin{description} + \item[Decompression] + The BSByteStream is created and the decompressor initializes. Chunks of + data will be read from ByteStream #bs# and decompressed into an internal + buffer. Function #read# can be used to access the decompressed data. + \end{description} */ + static GP<ByteStream> create(GP<ByteStream> bs); + + /** Constructs a BSByteStream. + The BSByteStream will be used for compressing data. + \begin{description} + \item[Compression] + Set #blocksize# to a positive number smaller than 4096 to + initialize the compressor. Data written to the BSByteStream will be + accumulated into an internal buffer. The buffered data will be + compressed and written to ByteStream #bs# whenever the buffer sizes + reaches the maximum value specified by argument #blocksize# (in + kilobytes). Using a larger block size usually increases the compression + ratio at the expense of computation time. There is no need however to + specify a block size larger than the total number of bytes to compress. + Setting #blocksize# to #1024# is a good starting point. A minimal block + size of 10 is silently enforced. + \end{description} */ + static GP<ByteStream> create(GP<ByteStream> bs, const int blocksize); + + // ByteStream Interface + ~BSByteStream(); + virtual long tell(void) const; + virtual void flush(void) = 0; +protected: + // Data + long offset; + int bptr; + unsigned int blocksize; + int size; + ByteStream *bs; + GP<ByteStream> gbs; + unsigned char *data; + GPBuffer<unsigned char> gdata; + // Coder + GP<ZPCodec> gzp; + BitContext ctx[300]; +private: + // Cancel C++ default stuff + BSByteStream(const BSByteStream &); + BSByteStream & operator=(const BSByteStream &); + BSByteStream(ByteStream *); + BSByteStream(ByteStream *, int); +}; + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/BSEncodeByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/BSEncodeByteStream.cpp new file mode 100644 index 00000000..9d5b726d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/BSEncodeByteStream.cpp @@ -0,0 +1,1010 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: BSEncodeByteStream.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 07/1998 + + + +#include "BSByteStream.h" +#include "GString.h" +#undef BSORT_TIMER +#ifdef BSORT_TIMER +#include "GOS.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ======================================== +// --- Assertion + +#define ASSERT(expr) do{if(!(expr))G_THROW("assertion ("#expr") failed");}while(0) + + + +// ======================================== +// --- Global Definitions + + +#ifdef OVERFLOW +#undef OVERFLOW +#endif +// Overflow required when encoding +static const int OVERFLOW=32; + +// Sorting tresholds +static const int RANKSORT_THRESH=10; +static const int QUICKSORT_STACK=512; +static const int PRESORT_THRESH=10; +static const int PRESORT_DEPTH=8; +static const int RADIX_THRESH=32768; + +static const int FREQS0=100000; +static const int FREQS1=1000000; + +// ======================================== +// -- Sorting Routines + + +class _BSort // DJVU_CLASS +{ +public: + ~_BSort(); + _BSort(unsigned char *data, int size); + void run(int &markerpos); +private: + // Members + int size; + unsigned char *data; + unsigned int *posn; + GPBuffer<unsigned int> gposn; + int *rank; + GPBuffer<int> grank; + // Helpers + inline int GT(int p1, int p2, int depth); + inline int GTD(int p1, int p2, int depth); + // -- final in-depth sort + void ranksort(int lo, int hi, int d); + // -- doubling sort + int pivot3r(int *rr, int lo, int hi); + void quicksort3r(int lo, int hi, int d); + // -- presort to depth PRESORT_DEPTH + unsigned char pivot3d(unsigned char *dd, int lo, int hi); + void quicksort3d(int lo, int hi, int d); + // -- radixsort + void radixsort16(void); + void radixsort8(void); +}; + + +// blocksort -- the main entry point + +static void +blocksort(unsigned char *data, int size, int &markerpos) +{ + _BSort bsort(data, size); + bsort.run(markerpos); +} + + +// _BSort construction + +_BSort::_BSort(unsigned char *xdata, int xsize) + : size(xsize), data(xdata), gposn(posn,xsize), grank(rank,xsize+1) +{ + ASSERT(size>0 && size<0x1000000); + rank[size] = -1; +} + +_BSort::~_BSort() +{ +} + + + +// GT -- compare suffixes using rank information + +inline int +_BSort::GT(int p1, int p2, int depth) +{ + int r1, r2; + int twod = depth + depth; + while (1) + { + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + }; +} + + +// _BSort::ranksort -- +// -- a simple insertion sort based on GT + +void +_BSort::ranksort(int lo, int hi, int depth) +{ + int i,j; + for (i=lo+1; i<=hi; i++) + { + int tmp = posn[i]; + for(j=i-1; j>=lo && GT(posn[j], tmp, depth); j--) + posn[j+1] = posn[j]; + posn[j+1] = tmp; + } + for(i=lo;i<=hi;i++) + rank[posn[i]]=i; +} + +// pivot -- return suitable pivot + +inline int +_BSort::pivot3r(int *rr, int lo, int hi) +{ + int c1, c2, c3; + if (hi-lo > 256) + { + c1 = pivot3r(rr, lo, (6*lo+2*hi)/8); + c2 = pivot3r(rr, (5*lo+3*hi)/8, (3*lo+5*hi)/8); + c3 = pivot3r(rr, (2*lo+6*hi)/8, hi); + } + else + { + c1 = rr[posn[lo]]; + c2 = rr[posn[(lo+hi)/2]]; + c3 = rr[posn[hi]]; + } + // Extract median + if (c1>c3) + { int tmp=c1; c1=c3; c3=tmp; } + if (c2<=c1) + return c1; + else if (c2>=c3) + return c3; + else + return c2; +} + + +// _BSort::quicksort3r -- Three way quicksort algorithm +// Sort suffixes based on rank at pos+depth +// The algorithm breaks into ranksort when size is +// smaller than RANKSORT_THRESH + +static inline int +mini(int a, int b) +{ + return (a<=b) ? a : b; +} + +static inline void +vswap(int i, int j, int n, unsigned int *x) +{ + while (n-- > 0) + { int tmp = x[i]; x[i++]=x[j]; x[j++]=tmp; } +} + +void +_BSort::quicksort3r(int lo, int hi, int depth) +{ + /* Initialize stack */ + int slo[QUICKSORT_STACK]; + int shi[QUICKSORT_STACK]; + int sp = 1; + slo[0] = lo; + shi[0] = hi; + // Recursion elimination loop + while (--sp>=0) + { + lo = slo[sp]; + hi = shi[sp]; + // Test for insertion sort + if (hi-lo<RANKSORT_THRESH) + { + ranksort(lo, hi, depth); + } + else + { + int tmp; + int *rr=rank+depth; + int med = pivot3r(rr,lo,hi); + // -- positions are organized as follows: + // [lo..l1[ [l1..l[ ]h..h1] ]h1..hi] + // = < > = + int l1 = lo; + int h1 = hi; + while (rr[posn[l1]]==med && l1<h1) { l1++; } + while (rr[posn[h1]]==med && l1<h1) { h1--; } + int l = l1; + int h = h1; + // -- partition set + for (;;) + { + while (l<=h) + { + int c = rr[posn[l]] - med; + if (c > 0) break; + if (c == 0) { tmp=posn[l]; posn[l]=posn[l1]; posn[l1++]=tmp; } + l++; + } + while (l<=h) + { + int c = rr[posn[h]] - med; + if (c < 0) break; + if (c == 0) { tmp=posn[h]; posn[h]=posn[h1]; posn[h1--]=tmp; } + h--; + } + if (l>h) break; + tmp=posn[l]; posn[l]=posn[h]; posn[h]=tmp; + } + // -- reorganize as follows + // [lo..l1[ [l1..h1] ]h1..hi] + // < = > + tmp = mini(l1-lo, l-l1); + vswap(lo, l-tmp, tmp, posn); + l1 = lo + (l-l1); + tmp = mini(hi-h1, h1-h); + vswap(hi-tmp+1, h+1, tmp, posn); + h1 = hi - (h1-h); + // -- process segments + ASSERT(sp+2<QUICKSORT_STACK); + // ----- middle segment (=?) [l1, h1] + for(int i=l1;i<=h1;i++) + rank[posn[i]] = h1; + // ----- lower segment (<) [lo, l1[ + if (l1 > lo) + { + for(int i=lo;i<l1;i++) + rank[posn[i]]=l1-1; + slo[sp]=lo; + shi[sp]=l1-1; + if (slo[sp] < shi[sp]) + sp++; + } + // ----- upper segment (>) ]h1, hi] + if (h1 < hi) + { + slo[sp]=h1+1; + shi[sp]=hi; + if (slo[sp] < shi[sp]) + sp++; + } + } + } +} + + + + + + +// GTD -- compare suffixes using data information +// (up to depth PRESORT_DEPTH) + +inline int +_BSort::GTD(int p1, int p2, int depth) +{ + unsigned char c1, c2; + p1+=depth; p2+=depth; + while (depth < PRESORT_DEPTH) + { + // Perform two + c1=data[p1]; c2=data[p2]; + if (c1!=c2) + return (c1>c2); + c1=data[p1+1]; c2=data[p2+1]; + p1+=2; p2+=2; depth+=2; + if (c1!=c2) + return (c1>c2); + } + if (p1<size && p2<size) + return 0; + return (p1<p2); +} + +// pivot3d -- return suitable pivot + +inline unsigned char +_BSort::pivot3d(unsigned char *rr, int lo, int hi) +{ + unsigned char c1, c2, c3; + if (hi-lo > 256) + { + c1 = pivot3d(rr, lo, (6*lo+2*hi)/8); + c2 = pivot3d(rr, (5*lo+3*hi)/8, (3*lo+5*hi)/8); + c3 = pivot3d(rr, (2*lo+6*hi)/8, hi); + } + else + { + c1 = rr[posn[lo]]; + c2 = rr[posn[(lo+hi)/2]]; + c3 = rr[posn[hi]]; + } + // Extract median + if (c1>c3) + { int tmp=c1; c1=c3; c3=tmp; } + if (c2<=c1) + return c1; + else if (c2>=c3) + return c3; + else + return c2; +} + + +// _BSort::quicksort3d -- Three way quicksort algorithm +// Sort suffixes based on strings until reaching +// depth rank at pos+depth +// The algorithm breaks into ranksort when size is +// smaller than PRESORT_THRESH + +void +_BSort::quicksort3d(int lo, int hi, int depth) +{ + /* Initialize stack */ + int slo[QUICKSORT_STACK]; + int shi[QUICKSORT_STACK]; + int sd[QUICKSORT_STACK]; + int sp = 1; + slo[0] = lo; + shi[0] = hi; + sd[0] = depth; + // Recursion elimination loop + while (--sp>=0) + { + lo = slo[sp]; + hi = shi[sp]; + depth = sd[sp]; + // Test for insertion sort + if (depth >= PRESORT_DEPTH) + { + for (int i=lo; i<=hi; i++) + rank[posn[i]] = hi; + } + else if (hi-lo<PRESORT_THRESH) + { + int i,j; + for (i=lo+1; i<=hi; i++) + { + int tmp = posn[i]; + for(j=i-1; j>=lo && GTD(posn[j], tmp, depth); j--) + posn[j+1] = posn[j]; + posn[j+1] = tmp; + } + for(i=hi;i>=lo;i=j) + { + int tmp = posn[i]; + rank[tmp] = i; + for (j=i-1; j>=lo && !GTD(tmp,posn[j],depth); j--) + rank[posn[j]] = i; + } + } + else + { + int tmp; + unsigned char *dd=data+depth; + unsigned char med = pivot3d(dd,lo,hi); + // -- positions are organized as follows: + // [lo..l1[ [l1..l[ ]h..h1] ]h1..hi] + // = < > = + int l1 = lo; + int h1 = hi; + while (dd[posn[l1]]==med && l1<h1) { l1++; } + while (dd[posn[h1]]==med && l1<h1) { h1--; } + int l = l1; + int h = h1; + // -- partition set + for (;;) + { + while (l<=h) + { + int c = (int)dd[posn[l]] - (int)med; + if (c > 0) break; + if (c == 0) { tmp=posn[l]; posn[l]=posn[l1]; posn[l1++]=tmp; } + l++; + } + while (l<=h) + { + int c = (int)dd[posn[h]] - (int)med; + if (c < 0) break; + if (c == 0) { tmp=posn[h]; posn[h]=posn[h1]; posn[h1--]=tmp; } + h--; + } + if (l>h) break; + tmp=posn[l]; posn[l]=posn[h]; posn[h]=tmp; + } + // -- reorganize as follows + // [lo..l1[ [l1..h1] ]h1..hi] + // < = > + tmp = mini(l1-lo, l-l1); + vswap(lo, l-tmp, tmp, posn); + l1 = lo + (l-l1); + tmp = mini(hi-h1, h1-h); + vswap(hi-tmp+1, h+1, tmp, posn); + h1 = hi - (h1-h); + // -- process segments + ASSERT(sp+3<QUICKSORT_STACK); + // ----- middle segment (=?) [l1, h1] + l = l1; h = h1; + if (med==0) // special case for marker [slow] + for (int i=l; i<=h; i++) + if ((int)posn[i]+depth == size-1) + { + tmp=posn[i]; posn[i]=posn[l]; posn[l]=tmp; + rank[tmp]=l++; break; + } + if (l<h) + { slo[sp] = l; shi[sp] = h; sd[sp++] = depth+1; } + else if (l==h) + { rank[posn[h]] = h; } + // ----- lower segment (<) [lo, l1[ + l = lo; + h = l1-1; + if (l<h) + { slo[sp] = l; shi[sp] = h; sd[sp++] = depth; } + else if (l==h) + { rank[posn[h]] = h; } + // ----- upper segment (>) ]h1, hi] + l = h1+1; + h = hi; + if (l<h) + { slo[sp] = l; shi[sp] = h; sd[sp++] = depth; } + else if (l==h) + { rank[posn[h]] = h; } + } + } +} + + + + +// _BSort::radixsort8 -- 8 bit radix sort + +void +_BSort::radixsort8(void) +{ + int i; + // Initialize frequency array + int lo[256], hi[256]; + for (i=0; i<256; i++) + hi[i] = lo[i] = 0; + // Count occurences + for (i=0; i<size-1; i++) + hi[data[i]] ++; + // Compute positions (lo) + int last = 1; + for (i=0; i<256; i++) + { + lo[i] = last; + hi[i] = last + hi[i] - 1; + last = hi[i] + 1; + } + for (i=0; i<size-1; i++) + { + posn[ lo[data[i]]++ ] = i; + rank[ i ] = hi[data[i]]; + } + // Process marker "$" + posn[0] = size-1; + rank[size-1] = 0; + // Extra element + rank[size] = -1; +} + + +// _BSort::radixsort16 -- 16 bit radix sort + +void +_BSort::radixsort16(void) +{ + int i; + // Initialize frequency array + int *ftab; + GPBuffer<int> gftab(ftab,65536); + for (i=0; i<65536; i++) + ftab[i] = 0; + // Count occurences + unsigned char c1 = data[0]; + for (i=0; i<size-1; i++) + { + unsigned char c2 = data[i+1]; + ftab[(c1<<8)|c2] ++; + c1 = c2; + } + // Generate upper position + for (i=1;i<65536;i++) + ftab[i] += ftab[i-1]; + // Fill rank array with upper bound + c1 = data[0]; + for (i=0; i<size-2; i++) + { + unsigned char c2 = data[i+1]; + rank[i] = ftab[(c1<<8)|c2]; + c1 = c2; + } + // Fill posn array (backwards) + c1 = data[size-2]; + for (i=size-3; i>=0; i--) + { + unsigned char c2 = data[i]; + posn[ ftab[(c2<<8)|c1]-- ] = i; + c1 = c2; + } + // Fixup marker stuff + ASSERT(data[size-1]==0); + c1 = data[size-2]; + posn[0] = size-1; + posn[ ftab[(c1<<8)] ] = size-2; + rank[size-1] = 0; + rank[size-2] = ftab[(c1<<8)]; + // Extra element + rank[size] = -1; +} + + + +// _BSort::run -- main sort loop + +void +_BSort::run(int &markerpos) +{ + int lo, hi; + ASSERT(size>0); + ASSERT(data[size-1]==0); +#ifdef BSORT_TIMER + long start = GOS::ticks(); +#endif + // Step 1: Radix sort + int depth = 0; + if (size > RADIX_THRESH) + { + radixsort16(); + depth=2; + } + else + { + radixsort8(); + depth=1; + } + // Step 2: Perform presort to depth PRESORT_DEPTH + for (lo=0; lo<size; lo++) + { + hi = rank[posn[lo]]; + if (lo < hi) + quicksort3d(lo, hi, depth); + lo = hi; + } + depth = PRESORT_DEPTH; +#ifdef BSORT_TIMER + long middle = GOS::ticks(); +#endif + // Step 3: Perform rank doubling + int again = 1; + while (again) + { + again = 0; + int sorted_lo = 0; + for (lo=0; lo<size; lo++) + { + hi = rank[posn[lo]&0xffffff]; + if (lo == hi) + { + lo += (posn[lo]>>24) & 0xff; + } + else + { + if (hi-lo < RANKSORT_THRESH) + { + ranksort(lo, hi, depth); + } + else + { + again += 1; + while (sorted_lo < lo-1) + { + int step = mini(255, lo-1-sorted_lo); + posn[sorted_lo] = (posn[sorted_lo]&0xffffff) | (step<<24); + sorted_lo += step+1; + } + quicksort3r(lo, hi, depth); + sorted_lo = hi + 1; + } + lo = hi; + } + } + // Finish threading + while (sorted_lo < lo-1) + { + int step = mini(255, lo-1-sorted_lo); + posn[sorted_lo] = (posn[sorted_lo]&0xffffff) | (step<<24); + sorted_lo += step+1; + } + // Double depth + depth += depth; + } + // Step 4: Permute data + int i; + markerpos = -1; + for (i=0; i<size; i++) + rank[i] = data[i]; + for (i=0; i<size; i++) + { + int j = posn[i] & 0xffffff; + if (j>0) + { + data[i] = rank[j-1]; + } + else + { + data[i] = 0; + markerpos = i; + } + } + ASSERT(markerpos>=0 && markerpos<size); +#ifdef BSORT_TIMER + long end = GOS::ticks(); + DjVuPrintErrorUTF8("Sorting time: %d bytes in %ld + %ld = %ld ms\n", + size-1, middle-start, end-middle, end-start); +#endif +} + + +// ======================================== +// -- Encoding + +static void +encode_raw(ZPCodec &zp, int bits, int x) +{ + int n = 1; + int m = (1<<bits); + while (n < m) + { + x = (x & (m-1)) << 1; + int b = (x >> bits); + zp.encoder(b); + n = (n<<1) | b; + } +} + +static inline void +encode_binary(ZPCodec &zp, BitContext *ctx, int bits, int x) +{ + // Require 2^bits-1 contexts + int n = 1; + int m = (1<<bits); + ctx = ctx - 1; + while (n < m) + { + x = (x & (m-1)) << 1; + int b = (x >> bits); + zp.encoder(b, ctx[n]); + n = (n<<1) | b; + } +} + +class BSByteStream::Encode : public BSByteStream +{ +public: + /** Creates a Static object for allocating the memory area of + length #sz# starting at address #buffer#. */ + Encode(GP<ByteStream> bs); + ~Encode(); + void init(const int encoding); + // Virtual functions + virtual size_t write(const void *buffer, size_t sz); + virtual void flush(void); +protected: + unsigned int encode(void); +}; + +unsigned int +BSByteStream::Encode::encode() +{ + ///////////////////////////////// + //////////// Block Sort Tranform + + int markerpos = size-1; + blocksort(data,size,markerpos); + + ///////////////////////////////// + //////////// Encode Output Stream + + // Header + ZPCodec &zp=*gzp; + encode_raw(zp, 24, size); + // Determine and Encode Estimation Speed + int fshift = 0; + if (size < FREQS0) + { fshift=0; zp.encoder(0); } + else if (size < FREQS1) + { fshift = 1; zp.encoder(1); zp.encoder(0); } + else + { fshift = 2; zp.encoder(1); zp.encoder(1); } + // MTF + unsigned char mtf[256]; + unsigned char rmtf[256]; + unsigned int freq[FREQMAX]; + int m = 0; + for (m=0; m<256; m++) + mtf[m] = m; + for (m=0; m<256; m++) + rmtf[mtf[m]] = m; + int fadd = 4; + for (m=0; m<FREQMAX; m++) + freq[m] = 0; + // Encode + int i; + int mtfno = 3; + for (i=0; i<size; i++) + { + // Get MTF data + int c = data[i]; + int ctxid = CTXIDS-1; + if (ctxid>mtfno) ctxid=mtfno; + mtfno = rmtf[c]; + if (i==markerpos) + mtfno = 256; + // Encode using ZPCoder + int b; + BitContext *cx = ctx; + b = (mtfno==0); + zp.encoder(b, cx[ctxid]); + if (b) goto rotate; cx+=CTXIDS; + b = (mtfno==1); + zp.encoder(b, cx[ctxid]); + if (b) goto rotate; cx+=CTXIDS; + b = (mtfno<4); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,1,mtfno-2); goto rotate; } + cx+=1+1; + b = (mtfno<8); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,2,mtfno-4); goto rotate; } + cx+=1+3; + b = (mtfno<16); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,3,mtfno-8); goto rotate; } + cx+=1+7; + b = (mtfno<32); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,4,mtfno-16); goto rotate; } + cx+=1+15; + b = (mtfno<64); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,5,mtfno-32); goto rotate; } + cx+=1+31; + b = (mtfno<128); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,6,mtfno-64); goto rotate; } + cx+=1+63; + b = (mtfno<256); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,7,mtfno-128); goto rotate; } + continue; + // Rotate MTF according to empirical frequencies (new!) + rotate: + // Adjust frequencies for overflow + fadd = fadd + (fadd>>fshift); + if (fadd > 0x10000000) + { + fadd = fadd>>24; + freq[0] >>= 24; + freq[1] >>= 24; + freq[2] >>= 24; + freq[3] >>= 24; + for (int k=4; k<FREQMAX; k++) + freq[k] = freq[k]>>24; + } + // Relocate new char according to new freq + unsigned int fc = fadd; + if (mtfno < FREQMAX) + fc += freq[mtfno]; + int k; + for (k=mtfno; k>=FREQMAX; k--) + { + mtf[k] = mtf[k-1]; + rmtf[mtf[k]] = k; + } + for (; k>0 && fc>=freq[k-1]; k--) + { + mtf[k] = mtf[k-1]; + freq[k] = freq[k-1]; + rmtf[mtf[k]] = k; + } + mtf[k] = c; + freq[k] = fc; + rmtf[mtf[k]] = k; + } + // Terminate + return 0; +} + +// ======================================== +// --- Construction + +BSByteStream::Encode::Encode(GP<ByteStream> xbs) +: BSByteStream(xbs) {} + +void +BSByteStream::Encode::init(const int xencoding) +{ + gzp=ZPCodec::create(gbs,true,true); + const int encoding=(xencoding<MINBLOCK)?MINBLOCK:xencoding; + if (encoding > MAXBLOCK) + G_THROW( ERR_MSG("ByteStream.blocksize") "\t" + GUTF8String(MAXBLOCK) ); + // Record block size + blocksize = encoding * 1024; + // Initialize context array +} + +BSByteStream::Encode::~Encode() +{ + // Flush + flush(); + // Encode EOF marker + encode_raw(*gzp, 24, 0); + // Free allocated memory +} + +GP<ByteStream> +BSByteStream::create(GP<ByteStream> xbs,const int blocksize) +{ + BSByteStream::Encode *rbs=new BSByteStream::Encode(xbs); + GP<ByteStream> retval=rbs; + rbs->init(blocksize); + return retval; +} + +// ======================================== +// -- ByteStream interface + +void +BSByteStream::Encode::flush() +{ + if (bptr>0) + { + ASSERT(bptr<(int)blocksize); + memset(data+bptr, 0, OVERFLOW); + size = bptr+1; + encode(); + } + size = bptr = 0; +} + +size_t +BSByteStream::Encode::write(const void *buffer, size_t sz) +{ + // Trivial checks + if (sz == 0) + return 0; + // Loop + int copied = 0; + while (sz > 0) + { + // Initialize + if (!data) + { + bptr = 0; + gdata.resize(blocksize+OVERFLOW); + } + // Compute remaining + int bytes = blocksize - 1 - bptr; + if (bytes > (int)sz) + bytes = sz; + // Store date (todo: rle) + memcpy(data+bptr, buffer, bytes); + buffer = (void*)((char*)buffer + bytes); + bptr += bytes; + sz -= bytes; + copied += bytes; + offset += bytes; + // Flush when needed + if (bptr + 1 >= (int)blocksize) + flush(); + } + // return + return copied; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp new file mode 100644 index 00000000..011b348d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp @@ -0,0 +1,1445 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ByteStream.cpp,v 1.18 2004/08/06 14:50:05 leonb Exp $ +// $Name: release_3_5_15 $ + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 04/1997 + +#include "DjVuGlobal.h" +#include "ByteStream.h" +#include "GOS.h" +#include "GURL.h" +#include "DjVuMessage.h" +#include <fcntl.h> +#if defined(WIN32) || defined(__CYGWIN32__) +# include <io.h> +#endif + +#ifdef UNIX +# ifndef HAS_MEMMAP +# define HAS_MEMMAP 1 +# endif +#endif + +#if defined(UNIX) +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +# include <errno.h> +# ifdef HAS_MEMMAP +# include <sys/mman.h> +# endif +#elif defined(macintosh) +# include <unistd.h> +_MSL_IMP_EXP_C int _dup(int); +_MSL_IMP_EXP_C int _dup2(int,int); +_MSL_IMP_EXP_C int _close(int); +__inline int dup(int _a ) { return _dup(_a);} +__inline int dup2(int _a, int _b ) { return _dup2(_a, _b);} +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +const char *ByteStream::EndOfFile=ERR_MSG("EOF"); + +/** ByteStream interface for stdio files. + The virtual member functions #read#, #write#, #tell# and #seek# are mapped + to the well known stdio functions #fread#, #fwrite#, #ftell# and #fseek#. + @see Unix man page fopen(3), fread(3), fwrite(3), ftell(3), fseek(3) */ + +class ByteStream::Stdio : public ByteStream { +public: + Stdio(void); + + /** Constructs a ByteStream for accessing the file named #url#. + Arguments #url# and #mode# are similar to the arguments of the well + known stdio function #fopen#. In addition a url of #-# will be + interpreted as the standard output or the standard input according to + #mode#. This constructor will open a stdio file and construct a + ByteStream object accessing this file. Destroying the ByteStream object + will flush and close the associated stdio file. Returns an error code + if the stdio file cannot be opened. */ + GUTF8String init(const GURL &url, const char * const mode); + + /** Constructs a ByteStream for accessing the stdio file #f#. + Argument #mode# indicates the type of the stdio file, as in the + well known stdio function #fopen#. Destroying the ByteStream + object will not close the stdio file #f# unless closeme is true. */ + GUTF8String init(FILE * const f, const char * const mode="rb", const bool closeme=false); + + /** Initializes from stdio */ + GUTF8String init(const char mode[]); + + // Virtual functions + ~Stdio(); + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual void flush(void); + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + virtual long tell(void) const; +private: + // Cancel C++ default stuff + Stdio(const Stdio &); + Stdio & operator=(const Stdio &); +private: + // Implementation + bool can_read; + bool can_write; + bool must_close; +protected: + FILE *fp; + long pos; +}; + +inline GUTF8String +ByteStream::Stdio::init(FILE * const f,const char mode[],const bool closeme) +{ + fp=f; + must_close=closeme; + return init(mode); +} + + +/** ByteStream interface managing a memory buffer. + Class #ByteStream::Memory# manages a dynamically resizable buffer from + which data can be read or written. The buffer itself is organized as an + array of blocks of 4096 bytes. */ + +class ByteStream::Memory : public ByteStream +{ +public: + /** Constructs an empty ByteStream::Memory. + The buffer is initially empty. You must first use function #write# + to store data into the buffer, use function #seek# to rewind the + current position, and function #read# to read the data back. */ + Memory(); + /** Constructs a Memory by copying initial data. The + Memory buffer is initialized with #size# bytes copied from the + memory area pointed to by #buffer#. */ + GUTF8String init(const void * const buffer, const size_t size); + // Virtual functions + ~Memory(); + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual int seek(long offset, int whence=SEEK_SET, bool nothrow=false); + virtual long tell(void) const; + /** Erases everything in the Memory. + The current location is reset to zero. */ + void empty(); + /** Returns the total number of bytes contained in the buffer. Valid + offsets for function #seek# range from 0 to the value returned by this + function. */ + virtual int size(void) const; + /** Returns a reference to the byte at offset #n#. This reference can be + used to read (as in #mbs[n]#) or modify (as in #mbs[n]=c#) the contents + of the buffer. */ + char &operator[] (int n); + /** Copies all internal data into \Ref{TArray} and returns it */ +private: + // Cancel C++ default stuff + Memory(const Memory &); + Memory & operator=(const Memory &); + // Current position + int where; +protected: + /** Reads data from a random position. This function reads at most #sz# + bytes at position #pos# into #buffer# and returns the actual number of + bytes read. The current position is unchanged. */ + virtual size_t readat(void *buffer, size_t sz, int pos); + /** Number of bytes in internal buffer. */ + int bsize; + /** Number of 4096 bytes blocks. */ + int nblocks; + /** Pointers (possibly null) to 4096 bytes blocks. */ + char **blocks; + /** Pointers (possibly null) to 4096 bytes blocks. */ + GPBuffer<char *> gblocks; +}; + + + +inline int +ByteStream::Memory::size(void) const +{ + return bsize; +} + +inline char & +ByteStream::Memory::operator[] (int n) +{ + return blocks[n>>12][n&0xfff]; +} + + + +/** Read-only ByteStream interface to a memory area. + Class #ByteStream::Static# implements a read-only ByteStream interface for a + memory area specified by the user at construction time. Calls to function + #read# directly access this memory area. The user must therefore make + sure that its content remain valid long enough. */ + +class ByteStream::Static : public ByteStream +{ +public: + class Allocate; + class Duplicate; + friend class Duplicate; + + /** Creates a Static object for allocating the memory area of + length #sz# starting at address #buffer#. */ + Static(const void * const buffer, const size_t sz); + ~Static(); + // Virtual functions + virtual size_t read(void *buffer, size_t sz); + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + virtual long tell(void) const; + /** Returns the total number of bytes contained in the buffer, file, etc. + Valid offsets for function #seek# range from 0 to the value returned + by this function. */ + virtual int size(void) const; + virtual GP<ByteStream> duplicate(const size_t xsize) const; + /// Returns false, unless a subclass of ByteStream::Static + virtual bool is_static(void) const { return true; } +protected: + const char *data; + int bsize; +private: + int where; +}; + +ByteStream::Static::~Static() {} + +class ByteStream::Static::Allocate : public ByteStream::Static +{ +public: + friend class ByteStream; +protected: + char *buf; + GPBuffer<char> gbuf; +public: + Allocate(const size_t size) : Static(0,size), gbuf(buf,size) { data=buf; } + virtual ~Allocate(); +}; + +ByteStream::Static::Allocate::~Allocate() {} + +inline int +ByteStream::Static::size(void) const +{ + return bsize; +} + +class ByteStream::Static::Duplicate : public ByteStream::Static +{ +protected: + GP<ByteStream> gbs; +public: + Duplicate(const ByteStream::Static &bs, const size_t size); +}; + +ByteStream::Static::Duplicate::Duplicate( + const ByteStream::Static &bs, const size_t xsize) +: ByteStream::Static(0,0) +{ + if(xsize&&(bs.bsize<bs.where)) + { + const size_t bssize=(size_t)bs.bsize-(size_t)bs.where; + bsize=(size_t)((xsize>bssize)?bssize:xsize); + gbs=const_cast<ByteStream::Static *>(&bs); + data=bs.data+bs.where; + } +} + +GP<ByteStream> +ByteStream::Static::duplicate(const size_t xsize) const +{ + return new ByteStream::Static::Duplicate(*this,xsize); +} + +#if HAS_MEMMAP +/** Read-only ByteStream interface to a memmap area. + Class #MemoryMapByteStream# implements a read-only ByteStream interface + for a memory map to a file. */ + +class MemoryMapByteStream : public ByteStream::Static +{ +public: + MemoryMapByteStream(void); + virtual ~MemoryMapByteStream(); +private: + GUTF8String init(const int fd, const bool closeme); + GUTF8String init(FILE *const f,const bool closeme); + friend class ByteStream; +}; +#endif + +//// CLASS BYTESTREAM + + +ByteStream::~ByteStream() +{ +} + +int +ByteStream::scanf(const char *fmt, ...) +{ + G_THROW( ERR_MSG("ByteStream.not_implemented") ); // This is a place holder function. + return 0; +} + +size_t +ByteStream::read(void *buffer, size_t sz) +{ + G_THROW( ERR_MSG("ByteStream.cant_read") ); // Cannot read from a ByteStream created for writing + return 0; +} + +size_t +ByteStream::write(const void *buffer, size_t sz) +{ + G_THROW( ERR_MSG("ByteStream.cant_write") ); // Cannot write from a ByteStream created for reading + return 0; +} + +void +ByteStream::flush() +{ +} + +int +ByteStream::seek(long offset, int whence, bool nothrow) +{ + int nwhere = 0; + int ncurrent = tell(); + switch (whence) + { + case SEEK_SET: + nwhere=0; break; + case SEEK_CUR: + nwhere=ncurrent; break; + case SEEK_END: + { + if(offset) + { + if (nothrow) + return -1; + G_THROW( ERR_MSG("ByteStream.backward") ); + } + char buffer[1024]; + int bytes; + while((bytes=read(buffer, sizeof(buffer)))) + EMPTY_LOOP; + return 0; + } + default: + G_THROW( ERR_MSG("ByteStream.bad_arg") ); // Illegal argument in seek + } + nwhere += offset; + if (nwhere < ncurrent) + { + // Seeking backwards is not supported by this ByteStream + if (nothrow) + return -1; + G_THROW( ERR_MSG("ByteStream.backward") ); + } + while (nwhere>ncurrent) + { + char buffer[1024]; + const int xbytes=(ncurrent+(int)sizeof(buffer)>nwhere) + ?(nwhere - ncurrent):(int)sizeof(buffer); + const int bytes = read(buffer, xbytes); + ncurrent += bytes; + if (!bytes) + G_THROW( ByteStream::EndOfFile ); + // Seeking works funny on this ByteStream (ftell() acts strange) + if (ncurrent!=tell()) + G_THROW( ERR_MSG("ByteStream.seek") ); + } + return 0; +} + +size_t +ByteStream::readall(void *buffer, size_t size) +{ + size_t total = 0; + while (size > 0) + { + int nitems = read(buffer, size); + // Replaced perror() below with G_THROW(). It still makes little sense + // as there is no guarantee, that errno is right. Still, throwing + // exception instead of continuing to loop is better. + // - eaf + if(nitems < 0) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + if (nitems == 0) + break; + total += nitems; + size -= nitems; + buffer = (void*)((char*)buffer + nitems); + } + return total; +} + +size_t +ByteStream::format(const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + return writestring(message); +} + +size_t +ByteStream::writestring(const GNativeString &s) +{ + int retval; + if(cp != UTF8) + { + retval=writall((const char *)s,s.length()); + if(cp == AUTO) + cp=NATIVE; // Avoid mixing string types. + }else + { + const GUTF8String msg(s.getNative2UTF8()); + retval=writall((const char *)msg,msg.length()); + } + return retval; +} + +size_t +ByteStream::writestring(const GUTF8String &s) +{ + int retval; + if(cp != NATIVE) + { + retval=writall((const char *)s,s.length()); + if(cp == AUTO) + cp=UTF8; // Avoid mixing string types. + }else + { + const GNativeString msg(s.getUTF82Native()); + retval=writall((const char *)msg,msg.length()); + } + return retval; +} + +size_t +ByteStream::writall(const void *buffer, size_t size) +{ + size_t total = 0; + while (size > 0) + { + size_t nitems = write(buffer, size); + if (nitems == 0) + G_THROW( ERR_MSG("ByteStream.write_error") ); // Unknown error in write + total += nitems; + size -= nitems; + buffer = (void*)((char*)buffer + nitems); + } + return total; +} + +size_t +ByteStream::copy(ByteStream &bsfrom, size_t size) +{ + size_t total = 0; + const size_t max_buffer_size=200*1024; + const size_t buffer_size=(size>0 && size<max_buffer_size) + ?size:max_buffer_size; + char *buffer; + GPBuffer<char> gbuf(buffer,buffer_size); + for(;;) + { + size_t bytes = buffer_size; + if (size>0 && bytes+total>size) + bytes = size - total; + if (bytes == 0) + break; + bytes = bsfrom.read((void*)buffer, bytes); + if (bytes == 0) + break; + writall((void*)buffer, bytes); + total += bytes; + } + return total; +} + + +void +ByteStream::write8 (unsigned int card) +{ + unsigned char c[1]; + c[0] = (card) & 0xff; + if (write((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +void +ByteStream::write16(unsigned int card) +{ + unsigned char c[2]; + c[0] = (card>>8) & 0xff; + c[1] = (card) & 0xff; + if (writall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +void +ByteStream::write24(unsigned int card) +{ + unsigned char c[3]; + c[0] = (card>>16) & 0xff; + c[1] = (card>>8) & 0xff; + c[2] = (card) & 0xff; + if (writall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +void +ByteStream::write32(unsigned int card) +{ + unsigned char c[4]; + c[0] = (card>>24) & 0xff; + c[1] = (card>>16) & 0xff; + c[2] = (card>>8) & 0xff; + c[3] = (card) & 0xff; + if (writall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +unsigned int +ByteStream::read8 () +{ + unsigned char c[1]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return c[0]; +} + +unsigned int +ByteStream::read16() +{ + unsigned char c[2]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return (c[0]<<8)+c[1]; +} + +unsigned int +ByteStream::read24() +{ + unsigned char c[3]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return (((c[0]<<8)+c[1])<<8)+c[2]; +} + +unsigned int +ByteStream::read32() +{ + unsigned char c[4]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return (((((c[0]<<8)+c[1])<<8)+c[2])<<8)+c[3]; +} + + + +//// CLASS ByteStream::Stdio + +ByteStream::Stdio::Stdio(void) +: can_read(false),can_write(false),must_close(true),fp(0),pos(0) +{} + +ByteStream::Stdio::~Stdio() +{ + if (fp && must_close) + fclose(fp); +} + +GUTF8String +ByteStream::Stdio::init(const char mode[]) +{ + char const *mesg=0; + bool binary=false; + if(!fp) + must_close=false; + for (const char *s=mode; s && *s; s++) + { + switch(*s) + { + case 'r': + can_read=true; + if(!fp) fp=stdin; + break; + case 'w': + case 'a': + can_write=true; + if(!fp) fp=stdout; + break; + case '+': + can_read=can_write=true; + break; + case 'b': + binary=true; + break; + default: + mesg= ERR_MSG("ByteStream.bad_mode"); // Illegal mode in Stdio + } + } + if(binary && fp) { +#if defined(__CYGWIN32__) + setmode(fileno(fp), O_BINARY); +#elif defined(WIN32) + _setmode(_fileno(fp), _O_BINARY); +#endif + } + GUTF8String retval; + if(!mesg) + { + tell(); + }else + { + retval=mesg; + } + if(mesg &&(fp && must_close)) + { + fclose(fp); + fp=0; + must_close=false; + } + return retval; +} + +static FILE * +urlfopen(const GURL &url,const char mode[]) +{ +#ifdef WIN32 + FILE *retval=0; + const GUTF8String filename(url.UTF8Filename()); + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + if(filename.ncopy(wfilename,wfilename_size) > 0) + { + const GUTF8String gmode(mode); + wchar_t *wmode; + const size_t wmode_size=gmode.length()+1; + GPBuffer<wchar_t> gwmode(wmode,wmode_size); + if(gmode.ncopy(wmode,wmode_size) > 0) + { + retval=_wfopen(wfilename,wmode); + } + } + return retval?retval:fopen((const char *)url.NativeFilename(),mode); +#else + return fopen((const char *)url.NativeFilename(),mode); +#endif +} + +#ifdef UNIX +static int +urlopen(const GURL &url, const int mode, const int perm) +{ + return open((const char *)url.NativeFilename(),mode,perm); +} +#endif /* UNIX */ + +GUTF8String +ByteStream::Stdio::init(const GURL &url, const char mode[]) +{ + GUTF8String retval; + if (url.fname() != "-") + { + fp = urlfopen(url,mode); + if (!fp) + { + // Failed to open '%s': %s + G_THROW( ERR_MSG("ByteStream.open_fail") "\t" + url.name() + +"\t"+GNativeString(strerror(errno)).getNative2UTF8()); + } + } + return retval.length()?retval:init(mode); +} + +size_t +ByteStream::Stdio::read(void *buffer, size_t size) +{ + if (!can_read) + G_THROW( ERR_MSG("ByteStream.no_read") ); // Stdio not opened for reading + size_t nitems; + do + { + clearerr(fp); + nitems = fread(buffer, 1, size, fp); + if (nitems<=0 && ferror(fp)) + { +#ifdef EINTR + if (errno!=EINTR) +#endif + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + } + else + break; + } while(true); + pos += nitems; + return nitems; +} + +size_t +ByteStream::Stdio::write(const void *buffer, size_t size) +{ + if (!can_write) + G_THROW( ERR_MSG("ByteStream.no_write") ); // Stdio not opened for writing + size_t nitems; + do + { + clearerr(fp); + nitems = fwrite(buffer, 1, size, fp); + if (nitems<=0 && ferror(fp)) + { +#ifdef EINTR + if (errno!=EINTR) +#endif + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + } + else + break; + } while(true); + pos += nitems; + return nitems; +} + +void +ByteStream::Stdio::flush() +{ + if (fflush(fp) < 0) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +long +ByteStream::Stdio::tell(void) const +{ + long x = ftell(fp); + if (x >= 0) + { + Stdio *sbs=const_cast<Stdio *>(this); + (sbs->pos) = x; + }else + { + x=pos; + } + return x; +} + +int +ByteStream::Stdio::seek(long offset, int whence, bool nothrow) +{ + if (whence==SEEK_SET && offset>=0 && offset==ftell(fp)) + return 0; + clearerr(fp); + if (fseek(fp, offset, whence)) + { + if (nothrow) + return -1; + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + } + return tell(); +} + + + + +///////// ByteStream::Memory + +ByteStream::Memory::Memory() + : where(0), bsize(0), nblocks(0), gblocks(blocks,0) +{ +} + +GUTF8String +ByteStream::Memory::init(void const * const buffer, const size_t sz) +{ + GUTF8String retval; + G_TRY + { + writall(buffer, sz); + where = 0; + } + G_CATCH(ex) // The only error that should be thrown is out of memory... + { + retval=ex.get_cause(); + } + G_ENDCATCH; + return retval; +} + +void +ByteStream::Memory::empty() +{ + for (int b=0; b<nblocks; b++) + { + delete [] blocks[b]; + blocks[b]=0; + } + bsize = 0; + where = 0; + nblocks = 0; +} + +ByteStream::Memory::~Memory() +{ + empty(); +} + +size_t +ByteStream::Memory::write(const void *buffer, size_t sz) +{ + int nsz = (int)sz; + if (nsz <= 0) + return 0; + // check memory + if ( (where+nsz) > ((bsize+0xfff)&~0xfff) ) + { + // reallocate pointer array + if ( (where+nsz) > (nblocks<<12) ) + { + const int old_nblocks=nblocks; + nblocks = (((where+nsz)+0xffff)&~0xffff) >> 12; + gblocks.resize(nblocks); + char const ** eblocks=(char const **)(blocks+old_nblocks); + for(char const * const * const new_eblocks=blocks+nblocks; + eblocks <new_eblocks; eblocks++) + { + *eblocks = 0; + } + } + // allocate blocks + for (int b=(where>>12); (b<<12)<(where+nsz); b++) + { + if (! blocks[b]) + blocks[b] = new char[0x1000]; + } + } + // write data to buffer + while (nsz > 0) + { + int n = (where|0xfff) + 1 - where; + n = ((nsz < n) ? nsz : n); + memcpy( (void*)&blocks[where>>12][where&0xfff], buffer, n); + buffer = (void*) ((char*)buffer + n); + where += n; + nsz -= n; + } + // adjust size + if (where > bsize) + bsize = where; + return sz; +} + +size_t +ByteStream::Memory::readat(void *buffer, size_t sz, int pos) +{ + if ((int) sz > bsize - pos) + sz = bsize - pos; + int nsz = (int)sz; + if (nsz <= 0) + return 0; + // read data from buffer + while (nsz > 0) + { + int n = (pos|0xfff) + 1 - pos; + n = ((nsz < n) ? nsz : n); + memcpy(buffer, (void*)&blocks[pos>>12][pos&0xfff], n); + buffer = (void*) ((char*)buffer + n); + pos += n; + nsz -= n; + } + return sz; +} + +size_t +ByteStream::Memory::read(void *buffer, size_t sz) +{ + sz = readat(buffer,sz,where); + where += sz; + return sz; +} + +long +ByteStream::Memory::tell(void) const +{ + return where; +} + +int +ByteStream::Memory::seek(long offset, int whence, bool nothrow) +{ + int nwhere = 0; + switch (whence) + { + case SEEK_SET: nwhere = 0; break; + case SEEK_CUR: nwhere = where; break; + case SEEK_END: nwhere = bsize; break; + default: G_THROW( ERR_MSG("bad_arg") "\tByteStream::Memory::seek()"); // Illegal argument in ByteStream::Memory::seek() + } + nwhere += offset; + if (nwhere<0) + G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file + where = nwhere; + return 0; +} + + + +/** This function has been moved into Arrays.cpp + In order to avoid dependencies from ByteStream.o + to Arrays.o */ +#ifdef DO_NOT_MOVE_GET_DATA_TO_ARRAYS_CPP +TArray<char> +ByteStream::get_data(void) +{ + TArray<char> data(0, size()-1); + readat((char*)data, size(), 0); + return data; +} +#endif + + +///////// ByteStream::Static + +ByteStream::Static::Static(const void * const buffer, const size_t sz) + : data((const char *)buffer), bsize(sz), where(0) +{ +} + +size_t +ByteStream::Static::read(void *buffer, size_t sz) +{ + int nsz = (int)sz; + if (nsz > bsize - where) + nsz = bsize - where; + if (nsz <= 0) + return 0; + memcpy(buffer, data+where, nsz); + where += nsz; + return nsz; +} + +int +ByteStream::Static::seek(long offset, int whence, bool nothrow) +{ + int nwhere = 0; + switch (whence) + { + case SEEK_SET: nwhere = 0; break; + case SEEK_CUR: nwhere = where; break; + case SEEK_END: nwhere = bsize; break; + default: G_THROW("bad_arg\tByteStream::Static::seek()"); // Illegal argument to ByteStream::Static::seek() + } + nwhere += offset; + if (nwhere<0) + G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file + where = nwhere; + return 0; +} + +long +ByteStream::Static::tell(void) const +{ + return where; +} + +GP<ByteStream> +ByteStream::create(void) +{ + return new Memory(); +} + +GP<ByteStream> +ByteStream::create(void const * const buffer, const size_t size) +{ + Memory *mbs=new Memory(); + GP<ByteStream> retval=mbs; + mbs->init(buffer,size); + return retval; +} + +GP<ByteStream> +ByteStream::create(const GURL &url,char const * const xmode) +{ + GP<ByteStream> retval; + const char *mode = ((xmode) ? xmode : "rb"); +#ifdef UNIX + if (!strcmp(mode,"rb")) + { + int fd = urlopen(url,O_RDONLY,0777); + if (fd >= 0) + { +#if HAS_MEMMAP && defined(S_IFREG) + struct stat buf; + if ( (fstat(fd, &buf) >= 0) && (buf.st_mode & S_IFREG) ) + { + MemoryMapByteStream *rb = new MemoryMapByteStream(); + retval = rb; + GUTF8String errmessage = rb->init(fd,true); + if(errmessage.length()) + retval=0; + } +#endif + if (! retval) + { + FILE *f = fdopen(fd, mode); + if (f) + { + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(f, mode, true); + if(errmessage.length()) + retval=0; + } + } + if (! retval) + close(fd); + } + } +#endif + if (! retval) + { + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(url, mode); + if(errmessage.length()) + G_THROW(errmessage); + } + return retval; +} + +GP<ByteStream> +ByteStream::create(char const * const mode) +{ + GP<ByteStream> retval; + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(mode?mode:"rb"); + if(errmessage.length()) + { + G_THROW(errmessage); + } + return retval; +} + +GP<ByteStream> +ByteStream::create(const int fd,char const * const mode,const bool closeme) +{ + GP<ByteStream> retval; + const char *default_mode="rb"; +#if HAS_MEMMAP + if ( (!mode&&(fd!=0)&&(fd!=1)&&(fd!=2)) + || (mode&&(GUTF8String("rb") == mode))) + { + MemoryMapByteStream *rb=new MemoryMapByteStream(); + retval=rb; + GUTF8String errmessage=rb->init(fd,closeme); + if(errmessage.length()) + { + retval=0; + } + } + if(!retval) +#endif + { + int fd2 = fd; + FILE *f = 0; + if (fd == 0 && !closeme + && (!mode || mode[0]=='r') ) + { + f=stdin; + default_mode = "r"; + fd2=(-1); + } + else if (fd == 1 && !closeme + && (!mode || mode[0]=='a' || mode[0]=='w') ) + { + default_mode = "a"; + f=stdout; + fd2 = -1; + } + else if (fd == 2 && !closeme + && (!mode || mode[0]=='a' || mode[0]=='w') ) + { + default_mode = "a"; + f=stderr; + fd2 = -1; + } + else + { + if (! closeme) + fd2 = dup(fd); + f = fdopen(fd2,(char*)(mode?mode:default_mode)); + } + + if(!f) + { + if ( fd2 >= 0) + close(fd2); + G_THROW( ERR_MSG("ByteStream.open_fail2") ); + } + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(f,mode?mode:default_mode,(fd2>=0)); + if(errmessage.length()) + G_THROW(errmessage); + } + return retval; +} + +GP<ByteStream> +ByteStream::create(FILE * const f,char const * const mode,const bool closeme) +{ + GP<ByteStream> retval; +#if HAS_MEMMAP + if (!mode || (GUTF8String("rb") == mode)) + { + MemoryMapByteStream *rb=new MemoryMapByteStream(); + retval=rb; + GUTF8String errmessage=rb->init(fileno(f),false); + if(errmessage.length()) + { + retval=0; + }else + { + fclose(f); + } + } + if(!retval) +#endif + { + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(f,mode?mode:"rb",closeme); + if(errmessage.length()) + { + G_THROW(errmessage); + } + } + return retval; +} + +GP<ByteStream> +ByteStream::create_static(const void * const buffer, size_t sz) +{ + return new Static(buffer, sz); +} + +GP<ByteStream> +ByteStream::duplicate(const size_t xsize) const +{ + GP<ByteStream> retval; + const long int pos=tell(); + const int tsize=size(); + ByteStream &self=*(const_cast<ByteStream *>(this)); + if(tsize < 0 || pos < 0 || (unsigned int)tsize < 1+(unsigned int)pos) + { + retval=ByteStream::create(); + retval->copy(self,xsize); + retval->seek(0L); + }else + { + const size_t s=(size_t)tsize-(size_t)pos; + const int size=(!xsize||(s<xsize))?s:xsize; + ByteStream::Static::Allocate *bs=new ByteStream::Static::Allocate(size); + retval=bs; + self.readall(bs->buf,size); + } + self.seek(pos,SEEK_SET,true); + return retval; +} + + +#if HAS_MEMMAP +MemoryMapByteStream::MemoryMapByteStream(void) +: ByteStream::Static(0,0) +{} + +GUTF8String +MemoryMapByteStream::init(FILE *const f,const bool closeme) +{ + GUTF8String retval; + retval=init(fileno(f),false); + if(closeme) + { + fclose(f); + } + return retval; +} + +GUTF8String +MemoryMapByteStream::init(const int fd,const bool closeme) +{ + GUTF8String retval; +#if defined(PROT_READ) && defined(MAP_SHARED) + struct stat statbuf; + if(!fstat(fd,&statbuf)) + { + if(statbuf.st_size) + { + bsize=statbuf.st_size; + data=(char *)mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fd,0); + } + }else + { + if(closeme) + { + close(fd); + } + retval= ERR_MSG("ByteStream.open_fail2"); + } +#else + retval= ERR_MSG("ByteStream.open_fail2"); +#endif + if(closeme) + { + close(fd); + } + return retval; +} + +MemoryMapByteStream::~MemoryMapByteStream() +{ + if(data) + { + munmap(const_cast<char *>(data),bsize); + } +} + +#endif + +ByteStream::Wrapper::~Wrapper() {} + + +GP<ByteStream> +ByteStream::get_stdin(char const * const mode) +{ + static GP<ByteStream> gp = ByteStream::create(0,mode,false); + return gp; +} + +GP<ByteStream> +ByteStream::get_stdout(char const * const mode) +{ + static GP<ByteStream> gp = ByteStream::create(1,mode,false); + return gp; +} + +GP<ByteStream> +ByteStream::get_stderr(char const * const mode) +{ + static GP<ByteStream> gp = ByteStream::create(2,mode,false); + return gp; +} + + +/** Looks up the message and writes it to the specified stream. */ +void ByteStream::formatmessage( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + writemessage( message ); +} + +/** Looks up the message and writes it to the specified stream. */ +void ByteStream::writemessage( const char *message ) +{ + writestring( DjVuMessage::LookUpUTF8( message ) ); +} + +static void +read_file(ByteStream &bs,char *&buffer,GPBuffer<char> &gbuffer) +{ + const int size=bs.size(); + int pos=0; + if(size>0) + { + size_t readsize=size+1; + gbuffer.resize(readsize); + for(int i;readsize&&(i=bs.read(buffer+pos,readsize))>0;pos+=i,readsize-=i) + EMPTY_LOOP; + }else + { + const size_t readsize=32768; + gbuffer.resize(readsize); + for(int i;((i=bs.read(buffer+pos,readsize))>0); + gbuffer.resize((pos+=i)+readsize)) + EMPTY_LOOP; + } + buffer[pos]=0; +} + +GNativeString +ByteStream::getAsNative(void) +{ + char *buffer; + GPBuffer<char> gbuffer(buffer); + read_file(*this,buffer,gbuffer); + return GNativeString(buffer); +} + +GUTF8String +ByteStream::getAsUTF8(void) +{ + char *buffer; + GPBuffer<char> gbuffer(buffer); + read_file(*this,buffer,gbuffer); + return GUTF8String(buffer); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +void +DjVuPrintErrorUTF8(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> errout = ByteStream::get_stderr(); + if (errout) + { + errout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + errout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuPrintErrorNative(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> errout = ByteStream::get_stderr(); + if (errout) + { + errout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GNativeString message(fmt,args); + errout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuPrintMessageUTF8(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> strout = ByteStream::get_stdout(); + if (strout) + { + strout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + strout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuPrintMessageNative(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> strout = ByteStream::get_stdout(); + if (strout) + { + strout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GNativeString message(fmt,args); + strout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} diff --git a/kviewshell/plugins/djvu/libdjvu/ByteStream.h b/kviewshell/plugins/djvu/libdjvu/ByteStream.h new file mode 100644 index 00000000..7ecfd8b7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ByteStream.h @@ -0,0 +1,416 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ByteStream.h,v 1.11 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _BYTESTREAM_H +#define _BYTESTREAM_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name ByteStream.h + + Files #"ByteStream.h"# and #"ByteStream.cpp"# define input/output classes + similar in spirit to the well known C++ #iostream# classes. Class + \Ref{ByteStream} is an abstract base class for all byte streams. It + defines a virtual interface and also provides useful functions. These + files provide two subclasses. Class \Ref{ByteStream::Stdio} provides a + simple interface to the Ansi C buffered input/output functions. Class + \Ref{ByteStream::Memory} provides stream-like access to a dynamical array + maintained in memory. Class \Ref{ByteStream::Static} provides read-only + stream-like access to a user allocated data buffer. + + {\bf Notes} --- These classes were partly written because we did not want to + depend on the standard C++ library. The main reason however is related to + the browser interface. We want to have a tight control over the + implementation of subclasses because we want to use a byte stream to + represent data passed by a web browser to a plugin. This operation + involves multi-threading issues that many implementations of the standard + C++ library would squarely ignore. + + @memo + Input/output classes + @author + L\'eon Bottou <leonb@research.att.com> -- initial implementation\\ + Andrei Erofeev <eaf@geocities.com> -- + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + + + @version + #$Id: ByteStream.h,v 1.11 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + +#include "Arrays.h" +#include <stdio.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GURL; +class GUTF8String; +class GNativeString; + +/** Abstract class for a stream of bytes. Class #ByteStream# represent an + object from which (resp. to which) bytes can be read (resp. written) as + with a regular file. Virtual functions #read# and #write# must implement + these two basic operations. In addition, function #tell# returns an + offset identifying the current position, and function #seek# may be used + to change the current position. + + {\bf Note}. Both the copy constructor and the copy operator are declared + as private members. It is therefore not possible to make multiple copies + of instances of this class, as implied by the class semantic. +*/ +class ByteStream : public GPEnabled +{ +public: + class Stdio; + class Static; + class Memory; + class Wrapper; + enum codepage_type {RAW,AUTO,NATIVE,UTF8} cp; + + /** @name Virtual Functions. + These functions are usually implemented by each subclass of #ByteStream#. + */ + //@{ +public: + /** Virtual destructor. */ + virtual ~ByteStream(); + /** Reads data from a ByteStream. This function {\em must} be implemented + by each subclass of #ByteStream#. At most #size# bytes are read from + the ByteStream and stored in the memory area pointed to by #buffer#. + Function #read# returns immediately if #size# is zero. The actual number + of bytes read is returned. Function #read# returns a number of bytes + smaller than #size# if the end-of-file mark is reached before filling + the buffer. Subsequent invocations will always return value #0#. + Function #read# may also return a value greater than zero but smaller + than #size# for internal reasons. Programs must be ready to handle these + cases or use function \Ref{readall}. Exception \Ref{GException} is + thrown with a plain text error message whenever an error occurs. */ + virtual size_t read(void *buffer, size_t size); + /** Writes data to a ByteStream. This function {\em must} be implemented by + each subclass of #ByteStream#. At most #size# bytes from buffer + #buffer# are written to the ByteStream. Function #write# returns + immediately if #size# is zero. The actual number of bytes written is + returned. Function #write# may also return a value greater than zero but + smaller than #size# for internal reasons. Programs must be ready to + handle these cases or use function \Ref{writall}. Exception + \Ref{GException} is thrown with a plain text error message whenever an + error occurs. */ + virtual size_t write(const void *buffer, size_t size); + /** Returns the offset of the current position in the ByteStream. This + function {\em must} be implemented by each subclass of #ByteStream#. */ + virtual long tell(void) const = 0; + /** Sets the current position for reading or writing the ByteStream. Class + #ByteStream# provides a default implementation able to seek forward by + calling function #read# until reaching the desired position. Subclasses + implementing better seek capabilities must override this default + implementation. The new current position is computed by applying + displacement #offset# to the position represented by argument + #whence#. The following values are recognized for argument #whence#: + \begin{description} + \item[#SEEK_SET#] Argument #offset# indicates the position relative to + the beginning of the ByteStream. + \item[#SEEK_CUR#] Argument #offset# is a signed displacement relative to + the current position. + \item[#SEEK_END#] Argument #offset# is a displacement relative to the end + of the file. It is then advisable to provide a negative value for #offset#. + \end{description} + Results are undefined whenever the new position is greater than the + total size of the ByteStream. + + {\bf Error reporting}: + If #seek()# succeeds, #0# is returned. Otherwise it either returns + #-1# (if #nothrow# is set to #FALSE#) or throws the \Ref{GException} + exception. */ + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + /** Flushes all buffers in the ByteStream. Calling this function + guarantees that pending data have been actually written (i.e. passed to + the operating system). Class #ByteStream# provides a default + implementation which does nothing. */ + virtual void flush(void); + //@} + /** @name Utility Functions. + Class #ByteStream# implements these functions using the virtual + interface functions only. All subclasses of #ByteStream# inherit these + functions. */ + //@{ +public: + /** Reads data and blocks until everything has been read. This function is + essentially similar to function #read#. Unlike function #read# however, + function #readall# will never return a value smaller than #size# unless + an end-of-file mark is reached. This is implemented by repeatedly + calling function #read# until everything is read or until we reach an + end-of-file mark. Note that #read# and #readall# are equivalent when + #size# is one. */ + size_t readall(void *buffer, size_t size); + /** Writes data and blocks until everything has been written. This function + is essentially similar to function #write#. Unlike function #write# + however, function #writall# will only return after all #size# bytes have + been written. This is implemented by repeatedly calling function + #write# until everything is written. Note that #write# and #writall# + are equivalent when #size# is one. */ + size_t writall(const void *buffer, size_t size); + /** Copy data from another ByteStream. A maximum of #size# bytes are read + from the ByteStream #bsfrom# and are written to the ByteStream #*this# + at the current position. Less than #size# bytes may be written if an + end-of-file mark is reached on #bsfrom#. This function returns the + total number of bytes copied. Setting argument #size# to zero (the + default value) has a special meaning: the copying process will continue + until reaching the end-of-file mark on ByteStream #bsfrom#, regardless + of the number of bytes transferred. */ + size_t copy(ByteStream &bsfrom, size_t size=0); + /** Create a new #ByteStream# that copies the data from this #ByteStream# + starting from the current position, upto #size# bytes. Setting the + #size# to zero means copy to the end-of-file mark. */ + GP<ByteStream> duplicate(const size_t size=0) const; + /// Allows printf() type operations to a bytestream. + size_t format(const char *fmt, ... ); + /// Allows scanf() type operations on a bytestream. + int scanf(const char *fmt, ... ); + /** Writes the string as is, to the specified stream. */ + size_t writestring(const GUTF8String &s); + /** Writes the string as is, to the specified stream. */ + size_t writestring(const GNativeString &s); + /** Formats the message string, looks up the external representation + and writes it to the specified stream. */ + void formatmessage( const char *fmt, ... ); + /** Looks up the message and writes it to the specified stream. */ + void writemessage( const char *message ); + /** Writes a one-byte integer to a ByteStream. */ + void write8 (unsigned int card8); + /** Writes a two-bytes integer to a ByteStream. + The integer most significant byte is written first, + regardless of the processor endianness. */ + void write16(unsigned int card16); + /** Writes a three-bytes integer to a ByteStream. + The integer most significant byte is written first, + regardless of the processor endianness. */ + void write24(unsigned int card24); + /** Writes a four-bytes integer to a ByteStream. + The integer most significant bytes are written first, + regardless of the processor endianness. */ + void write32(unsigned int card32); + /** Reads a one-byte integer from a ByteStream. */ + unsigned int read8 (); + /** Reads a two-bytes integer from a ByteStream. + The integer most significant byte is read first, + regardless of the processor endianness. */ + unsigned int read16(); + /** Reads a three-bytes integer from a ByteStream. + The integer most significant byte is read first, + regardless of the processor endianness. */ + unsigned int read24(); + /** Reads a four-bytes integer from a ByteStream. + The integer most significant bytes are read first, + regardless of the processor endianness. */ + unsigned int read32(); + /** Returns the total number of bytes contained in the buffer, file, etc. + Valid offsets for function #seek# range from 0 to the value returned + by this function. */ + virtual int size(void) const; + /// Use at your own risk, only guarenteed to work for ByteStream::Memorys. + TArray<char> get_data(void); + /** Reads data from a random position. This function reads at most #sz# + bytes at position #pos# into #buffer# and returns the actual number of + bytes read. The current position is unchanged. */ + virtual size_t readat(void *buffer, size_t sz, int pos); + /// Returns false, unless a subclass of ByteStream::Static + virtual bool is_static(void) const { return false; } + //@} +protected: + ByteStream(void) : cp(AUTO) {}; +private: + // Cancel C++ default stuff + ByteStream(const ByteStream &); + ByteStream & operator=(const ByteStream &); +public: + /** Constructs an empty Memory ByteStream. The buffer itself is organized + as an array of 4096 byte blocks. The buffer is initially empty. You + must first use function #write# to store data into the buffer, use + function #seek# to rewind the current position, and function #read# to + read the data back. */ + static GP<ByteStream> create(void); + /** Constructs a Memory ByteStream by copying initial data. The + Memory buffer is initialized with #size# bytes copied from the + memory area pointed to by #buffer#. */ + static GP<ByteStream> create(void const * const buffer, const size_t size); + /** Constructs a ByteStream for accessing the file named #url#. + Arguments #url# and #mode# are similar to the arguments of the well + known stdio function #fopen#. In addition a url of #-# will be + interpreted as the standard output or the standard input according to + #mode#. This constructor will open a stdio file and construct a + ByteStream object accessing this file. Destroying the ByteStream object + will flush and close the associated stdio file. Exception + \Ref{GException} is thrown with a plain text error message if the stdio + file cannot be opened. */ + static GP<ByteStream> create( + const GURL &url, char const * const mode); + /** Same as the above, but uses stdin or stdout */ + static GP<ByteStream> create( char const * const mode); + + /** Constructs a ByteStream for accessing the stdio file #f#. + Argument #mode# indicates the type of the stdio file, as in the + well known stdio function #fopen#. Destroying the ByteStream + object will not close the stdio file #f# unless closeme is true. */ + static GP<ByteStream> create( + const int fd, char const * const mode, const bool closeme); + + /** Constructs a ByteStream for accessing the stdio file #f#. + Argument #mode# indicates the type of the stdio file, as in the + well known stdio function #fopen#. Destroying the ByteStream + object will not close the stdio file #f# unless closeme is true. */ + static GP<ByteStream> create( + FILE * const f, char const * const mode, const bool closeme); + /** Creates a ByteStream object for allocating the memory area of + length #sz# starting at address #buffer#. This call impliments + a read-only ByteStream interface for a memory area specified by + the user at construction time. Calls to function #read# directly + access this memory area. The user must therefore make sure that its + content remain valid long enough. */ + static GP<ByteStream> create_static( + void const * const buffer, const size_t size); + + /** Easy access to preallocated stdin/stdout/stderr bytestreams */ + static GP<ByteStream> get_stdin(char const * const mode=0); + static GP<ByteStream> get_stdout(char const * const mode=0); + static GP<ByteStream> get_stderr(char const * const mode=0); + + /** This is the conventional name for EOF exceptions */ + static const char *EndOfFile; + /** Returns the contents of the file as a GNativeString */ + GNativeString getAsNative(void); + /** Returns the contents of the file as a GUTF8String */ + GUTF8String getAsUTF8(void); +}; + +inline size_t +ByteStream::readat(void *buffer, size_t sz, int pos) +{ + size_t retval; + long tpos=tell(); + seek(pos, SEEK_SET, true); + retval=readall(buffer,sz); + seek(tpos, SEEK_SET, true); + return retval; +} + +inline int +ByteStream::size(void) const +{ + ByteStream *bs=const_cast<ByteStream *>(this); + int bsize=(-1); + long pos=tell(); + if(bs->seek(0,SEEK_END,true)) + { + bsize=(int)tell(); + (void)(bs->seek(pos,SEEK_SET,false)); + } + return bsize; +} + +/** ByteStream::Wrapper implements wrapping bytestream. This is useful + for derived classes that take a GP<ByteStream> as a creation argument, + and the backwards compatible bytestreams. */ +class ByteStream::Wrapper : public ByteStream +{ +protected: + GP<ByteStream> gbs; + ByteStream *bs; + Wrapper(void) : bs(0) {} + Wrapper(const GP<ByteStream> &xbs) : gbs(xbs), bs(xbs) {} +public: + ~Wrapper(); + ByteStream * operator & () const {return bs;} + ByteStream * operator & () {return bs;} + virtual size_t read(void *buffer, size_t size) + { return bs->read(buffer,size); } + virtual size_t write(const void *buffer, size_t size) + { return bs->write(buffer,size); } + virtual long tell(void) const + { return bs->tell(); } + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false) + { return bs->seek(offset,whence,nothrow); } + virtual void flush(void) + { bs->flush(); } +}; + + +//@} + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DataPool.cpp b/kviewshell/plugins/djvu/libdjvu/DataPool.cpp new file mode 100644 index 00000000..1190292e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DataPool.cpp @@ -0,0 +1,1837 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DataPool.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DataPool.h" +#include "IFFByteStream.h" +#include "GString.h" +#include "GOS.h" +#include "GURL.h" +#include "debug.h" + +#ifndef macintosh +# include <sys/types.h> +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +const char * DataPool::Stop = ERR_MSG("STOP"); + +static void +// call_callback(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) +call_callback(void (* callback)(void *), void *cl_data) +{ + G_TRY + { + if (callback) + callback(cl_data); + } G_CATCH_ALL {} G_ENDCATCH; +} + + +//**************************************************************************** +//****************************** OpenFiles *********************************** +//**************************************************************************** + +#define MAX_OPEN_FILES 15 + +/** The purpose of this class is to limit the number of files open by + connected DataPools. Now, when a DataPool is connected to a file, it + doesn't necessarily has it open. Every time it needs access to data + it's supposed to ask this file for the ByteStream. It should + also inform the class when it's going to die (so that the file can + be closed). OpenFiles makes sure, that the number of open files + doesn't exceed MAX_OPEN_FILES. When it does, it looks for the oldest + file, closes it and asks all DataPools working with it to ZERO + their GP<> pointers. */ +class DataPool::OpenFiles_File : public GPEnabled +{ +public: + GURL url; + GP<ByteStream> stream; // Stream connected to 'url' + GCriticalSection stream_lock; + GPList<DataPool> pools_list; // List of pools using this stream + GCriticalSection pools_lock; + unsigned long open_time; // Time when stream was open + + int add_pool(GP<DataPool> &pool); + int del_pool(GP<DataPool> &pool); + + OpenFiles_File(const GURL &url, GP<DataPool> &pool); + virtual ~OpenFiles_File(void); + void clear_stream(void); +}; + +class DataPool::OpenFiles : public GPEnabled +{ +private: + static OpenFiles * global_ptr; + + GPList<DataPool::OpenFiles_File> files_list; + GCriticalSection files_lock; +public: + static OpenFiles * get(void); + + // Opend the specified file if necessary (or finds an already open one) + // and returns it. The caller (pool) is stored in the list associated + // with the stream. Whenever OpenFiles decides, that this stream + // had better be closed, it will order every pool from the list to + // ZERO their references to it + GP<DataPool::OpenFiles_File> request_stream(const GURL &url, GP<DataPool> pool); + // If there are more than MAX_STREAM_FILES open, close the oldest. + void prune(void); + // Removes the pool from the list associated with the stream. + // If there is nobody else using this stream, the stream will + // be closed too. + void stream_released(GP<ByteStream> &stream, GP<DataPool> pool); + + void close_all(void); +}; + +DataPool::OpenFiles * DataPool::OpenFiles::global_ptr; + +DataPool::OpenFiles_File::OpenFiles_File(const GURL &xurl, GP<DataPool> &pool) : url(xurl) +{ + DEBUG_MSG("DataPool::OpenFiles_File::OpenFiles_File(): Opening file '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + open_time=GOS::ticks(); + stream=ByteStream::create(url,"rb"); + add_pool(pool); +} + +DataPool::OpenFiles_File::~OpenFiles_File(void) +{ + DEBUG_MSG("DataPool::OpenFiles_File::~OpenFiles_File(): Closing file '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + clear_stream(); +} + +void +DataPool::OpenFiles_File::clear_stream(void) +{ + GCriticalSectionLock lock(&pools_lock); + for(GPosition pos=pools_list;pos;++pos) + if(pools_list[pos]) + pools_list[pos]->clear_stream(false); + pools_list.empty(); +} + +int +DataPool::OpenFiles_File::add_pool(GP<DataPool> &pool) +{ + DEBUG_MSG("DataPool::OpenFiles_File::add_pool: pool=" << (void *) pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&pools_lock); + if (!pools_list.contains(pool)) + pools_list.append(pool); + return pools_list.size(); +} + +int +DataPool::OpenFiles_File::del_pool(GP<DataPool> &pool) +{ + DEBUG_MSG("DataPool::OpenFiles_File::del_pool: pool=" << (void *) pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&pools_lock); + GPosition pos; + if (pools_list.search(pool, pos)) + pools_list.del(pos); + return pools_list.size(); +} + +inline DataPool::OpenFiles * +DataPool::OpenFiles::get(void) +{ + DEBUG_MSG("DataPool::OpenFiles::get()\n"); + DEBUG_MAKE_INDENT(3); + if (!global_ptr) + global_ptr=new OpenFiles(); + return global_ptr; +} + +void +DataPool::OpenFiles::prune(void) +{ + DEBUG_MSG("DataPool::OpenFiles::prune(void): "<<files_list.size()<< "\n"); + DEBUG_MAKE_INDENT(3); + while(files_list.size()>MAX_OPEN_FILES) + { + // Too many open files (streams). Get rid of the oldest one. + unsigned long oldest_time=GOS::ticks(); + GPosition oldest_pos=files_list; + for(GPosition pos=files_list;pos;++pos) + { + if (files_list[pos]->open_time<oldest_time) + { + oldest_time=files_list[pos]->open_time; + oldest_pos=pos; + } + } + files_list[oldest_pos]->clear_stream(); + files_list.del(oldest_pos); + } +} + +// GP<ByteStream> & stream, +// GCriticalSection ** stream_lock) +GP<DataPool::OpenFiles_File> +DataPool::OpenFiles::request_stream(const GURL &url, GP<DataPool> pool) +{ + DEBUG_MSG("DataPool::OpenFiles::request_stream(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool::OpenFiles_File> file; + + // Check: maybe the stream has already been open by request of + // another DataPool + GCriticalSectionLock lock(&files_lock); + for(GPosition pos=files_list;pos;++pos) + { + if (files_list[pos]->url==url) + { + DEBUG_MSG("found existing stream\n"); + file=files_list[pos]; + break; + } + } + + // No? Open the stream, but check, that there are not + // too many streams open + if (!file) + { + file=new DataPool::OpenFiles_File(url, pool); + files_list.append(file); + prune(); + } + + file->add_pool(pool); + return file; +} + +void +DataPool::OpenFiles::stream_released(GP<ByteStream> &stream, GP<DataPool> pool) +{ + DEBUG_MSG("DataPool::OpenFiles::stream_release: stream=" + << (void *)stream << " pool=" << (void *)pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&files_lock); + for(GPosition pos=files_list;pos;) + { + GPosition dpos = pos; + ++pos; + GP<DataPool::OpenFiles_File> f=files_list[dpos]; + if ((ByteStream *)(f->stream) == (ByteStream *)stream) + if (f->del_pool(pool)==0) + files_list.del(dpos); + } +} + +// This isn't really an accurate url. The files are not really +// closed. Instead they are dereferenced from the data pool. If +// a there is another reference to the respective bytestream, it +// will remain open until dereferenced. +void +DataPool::OpenFiles::close_all(void) +{ + DEBUG_MSG("DataPool::OpenFiles::close_all\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&files_lock); + files_list.empty(); +} + +//**************************************************************************** +//******************************** FCPools *********************************** +//**************************************************************************** + +/** This class is used to maintain a list of DataPools connected to a file. + It's important to have this list if we want to do something with this file + like to modify it or just erase. Since any modifications of the file + will break DataPools directly connected to it, it would be nice to have + a mechanism for signaling all the related DataPools to read data into + memory. This is precisely the purpose of this class. */ +class FCPools +{ +private: + GMap<GURL, GPList<DataPool> > map; // GMap<GUTF8String, GPList<DataPool>> in fact + GCriticalSection map_lock; + + static FCPools * global_ptr; +public: + static FCPools * get(void); + // Adds the <furl, pool> pair into the list + void add_pool(const GURL &furl, GP<DataPool> pool); + // Removes the <furl, pool> pair from the list + void del_pool(const GURL &furl, GP<DataPool> pool); + // Looks for the list of DataPools connected to 'furl' and makes + // each of them load the contents of the file into memory + void load_file(const GURL &url); + // Retrieve a local URL, if available. + GP<DataPool> get_pool(const GURL &url, int start, int length); + void clean(void); +}; + +void +FCPools::clean(void) +{ + GCriticalSectionLock lock(&map_lock); + static int count=0; + if(! count++) + { + bool restart = true; + while (restart) + { + restart = false; + for (GPosition posmap = map; posmap; ++posmap) + { + GPList<DataPool> *lst; + lst = & map[posmap]; + if (lst->isempty()) + { + map.del(posmap); + restart = true; + break; + } + for (GPosition poslst = *lst; poslst; ++poslst) + if ((*lst)[poslst]->get_count() < 2) + { + lst->del(poslst); + restart = true; + break; + } + if (restart) + break; + } + } + } + --count; +} + +void +FCPools::add_pool(const GURL &url, GP<DataPool> pool) +{ + DEBUG_MSG("FCPools::add_pool: url='" << url << "' pool=" << (void *)pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&map_lock); + + if (url.is_local_file_url()) + { + GPList<DataPool> list; + GPosition pos(map.contains(url)); + if (! pos) + { + map[url]=list; + pos=map.contains(url); + } + GPList<DataPool> &plist=map[pos]; + if (!plist.contains(pool)) + plist.append(pool); + } + clean(); +} + +GP<DataPool> +FCPools::get_pool(const GURL &url, int start, int length) +{ + DEBUG_MSG("FCPools::get_pool: url='" << url << "\n"); + DEBUG_MAKE_INDENT(3); + GP<DataPool> retval; + if (url.is_local_file_url()) + { + GCriticalSectionLock lock(&map_lock); + GPosition pos(map.contains(url)); + if (pos) + { + GPList<DataPool> &plist=map[pos]; + for(pos=plist;pos;++pos) + { + DataPool &pool=*plist[pos]; + if(start == pool.start && (length < 0 || (length == pool.length))) + { + retval=plist[pos]; + break; + } + } + } + clean(); + } + return retval; +} + +void +FCPools::del_pool(const GURL &url, GP<DataPool> pool) +{ + DEBUG_MSG("FCPools::del_pool: url='" << url << "' pool=" << (void *)pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&map_lock); + + clean(); + if (url.is_local_file_url()) + { + GPosition pos; + if (map.contains(url, pos)) + { + GPList<DataPool> &list=map[pos]; + GPosition list_pos; + while(list.search(pool, list_pos)) + list.del(list_pos); + if (list.isempty()) + { + map.del(pos); + } + } + } +} + +void +FCPools::load_file(const GURL &url) +{ + DEBUG_MSG("FCPools::load_file: url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&map_lock); + + clean(); + if (url.is_local_file_url()) + { + GPosition pos; + if (map.contains(url, pos)) + { + // We make here a copy of the list because DataPool::load_file() + // will call FCPools::del_pool(), which will modify the list + GPList<DataPool> list=map[pos]; + for(GPosition list_pos=list;list_pos;++list_pos) + list[list_pos]->load_file(); + } + } +} + +FCPools * FCPools::global_ptr; + +inline FCPools * +FCPools::get(void) +{ + if (!global_ptr) + global_ptr=new FCPools(); + return global_ptr; +} + +//**************************************************************************** +//****************************** BlockList *********************************** +//**************************************************************************** + +// Since data can be added to the DataPool at any offset now, there may +// be white spots, which contain illegal data. This class is to contain +// the list of valid and invalid regions. +// The class is basically a list of integers. Abs(integer)=size of the +// block. If the integer is positive, data for the block is known. +// Otherwise it's unkown. + +class DataPool::BlockList +{ + // See comments in .cpp file. +private: + GCriticalSection lock; + GList<int> list; +public: + BlockList() {}; + void clear(void); + void add_range(int start, int length); + int get_bytes(int start, int length) const; + int get_range(int start, int length) const; +friend class DataPool; +}; + +void +DataPool::BlockList::clear(void) +{ + DEBUG_MSG("DataPool::BlockList::clear()\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lk(&lock); + list.empty(); +} + +void +DataPool::BlockList::add_range(int start, int length) + // Adds range of known data. +{ + DEBUG_MSG("DataPool::BlockList::add_range: start=" << start << " length=" << length << "\n"); + DEBUG_MAKE_INDENT(3); + if (start<0) + G_THROW( ERR_MSG("DataPool.neg_start") ); + if (length<=0) + G_THROW( ERR_MSG("DataPool.bad_length") ); + if (length>0) + { + GCriticalSectionLock lk(&lock); + + // Look thru existing zones, change their sign and split if + // necessary. + GPosition pos=list; + int block_start=0, block_end=0; + while(pos && block_start<start+length) + { + int size=list[pos]; + block_end=block_start+abs(size); + if (size<0) + if (block_start<start) + { + if (block_end>start && block_end<=start+length) + { + list[pos]=-(start-block_start); + list.insert_after(pos, block_end-start); + ++pos; + block_start=start; + } else if (block_end>start+length) + { + list[pos]=-(start-block_start); + list.insert_after(pos, length); + ++pos; + list.insert_after(pos, -(block_end-(start+length))); + ++pos; + block_start=start+length; + } + } else if (block_start>=start && block_start<start+length) + { + if (block_end<=start+length) list[pos]=abs(size); + else + { + list[pos]=start+length-block_start; + list.insert_after(pos, -(block_end-(start+length))); + ++pos; + block_start=start+length; + } + } + block_start=block_end; + ++pos; + } + if (block_end<start) + { + list.append(-(start-block_end)); + list.append(length); + } else if (block_end<start+length) list.append(start+length-block_end); + + // Now merge adjacent areas with the same sign + pos=list; + while(pos) + { + GPosition pos1=pos; ++pos1; + while(pos1) + { + if (list[pos]<0 && list[pos1]>0 || + list[pos]>0 && list[pos1]<0) + break; + list[pos]+=list[pos1]; + GPosition this_pos=pos1; + ++pos1; + list.del(this_pos); + } + pos=pos1; + } + } // if (length>0) +} + +int +DataPool::BlockList::get_bytes(int start, int length) const + // Returns the number of bytes of data available in the range + // [start, start+length[. There may be holes between data chunks +{ + DEBUG_MSG("DataPool::BlockList::get_bytes: start=" << start << " length=" << length << "\n"); + DEBUG_MAKE_INDENT(3); + + if (length<0) + G_THROW( ERR_MSG("DataPool.bad_length") ); + + GCriticalSectionLock lk((GCriticalSection *) &lock); + int bytes=0; + int block_start=0, block_end=0; + for(GPosition pos=list;pos && block_start<start+length;++pos) + { + int size=list[pos]; + block_end=block_start+abs(size); + if (size>0) + if (block_start<start) + { + if (block_end>=start && block_end<start+length) + bytes+=block_end-start; + else if (block_end>=start+length) + bytes+=length; + } else + { + if (block_end<=start+length) + bytes+=block_end-block_start; + else bytes+=start+length-block_start; + } + block_start=block_end; + } + return bytes; +} + +int +DataPool::BlockList::get_range(int start, int length) const + // Finds a range covering offset=start and returns the length + // of intersection of this range with [start, start+length[ + // 0 is returned if nothing can be found +{ + DEBUG_MSG("DataPool::BlockList::get_range: start=" << start << " length=" << length << "\n"); + DEBUG_MAKE_INDENT(3); + if (start<0) + G_THROW( ERR_MSG("DataPool.neg_start") ); + if (length<=0) + G_THROW( ERR_MSG("DataPool.bad_length") ); + + GCriticalSectionLock lk((GCriticalSection *) &lock); + int block_start=0, block_end=0; + for(GPosition pos=list;pos && block_start<start+length;++pos) + { + int size=list[pos]; + block_end=block_start+abs(size); + if (block_start<=start && block_end>start) + if (size<0) return -1; + else + if (block_end>start+length) return length; + else return block_end-start; + block_start=block_end; + } + return 0; +} + +//**************************************************************************** +//******************************* DataPool *********************************** +//**************************************************************************** + +class DataPool::Reader : public GPEnabled +{ +public: + GEvent event; + bool reenter_flag; + int offset; + int size; + Reader() : reenter_flag(false), offset(0), size(-1){}; + Reader(int offset_in, int size_in=-1) : + reenter_flag(false), offset(offset_in), size(size_in) {}; + virtual ~Reader() {}; +}; + +class DataPool::Trigger : public GPEnabled +{ +public: + GSafeFlags disabled; + int start, length; +// void (* callback)(GP<GPEnabled> &); + void (* callback)(void *); +// GP<GPEnabled> cl_data; + void *cl_data; + + Trigger() : start(0), length(-1), callback(0), cl_data(0) {}; + Trigger(int xstart, int xlength, +// void (* xcallback)(GP<GPEnabled> &), GP<GPEnabled> xcl_data) : + void (* xcallback)(void *), void *xcl_data) : + start(xstart), length(xlength), callback(xcallback), cl_data(xcl_data) {}; + virtual ~Trigger() {}; +}; + +class DataPool::Counter +{ +private: + int counter; + GCriticalSection lock; +public: + Counter() : counter(0) {}; + operator int(void) const; + void inc(void); + void dec(void); +}; + +#define DATAPOOL_INIT eof_flag(false),stop_flag(false), \ + stop_blocked_flag(false), \ + add_at(0),start(0),length(-1) + +void +DataPool::init(void) +{ + DEBUG_MSG("DataPool::init(): Initializing\n"); + DEBUG_MAKE_INDENT(3); + start=0; length=-1; add_at=0; + eof_flag=false; + stop_flag=false; + stop_blocked_flag=false; + + active_readers=new Counter; + block_list=0; + G_TRY + { + block_list=new BlockList; + data=ByteStream::create(); + } + G_CATCH_ALL + { + delete block_list; + block_list=0; + delete active_readers; + active_readers=0; + G_RETHROW; + } + G_ENDCATCH; +} + +DataPool::DataPool(void) : DATAPOOL_INIT {} + +GP<DataPool> +DataPool::create(void) +{ + DEBUG_MSG("DataPool::DataPool()\n"); + DEBUG_MAKE_INDENT(3); + DataPool *pool=new DataPool(); + + GP<DataPool> retval=pool; + pool->init(); + + // If we maintain the data ourselves, we want to interpret its + // IFF structure to predict its length + pool->add_trigger(0, 32, static_trigger_cb, pool); + return retval; +} + +GP<DataPool> +DataPool::create(const GP<ByteStream> &gstr) +{ + DEBUG_MSG("DataPool::create: str="<<(ByteStream *)gstr<<"\n"); + DEBUG_MAKE_INDENT(3); + DataPool *pool=new DataPool(); + GP<DataPool> retval=pool; + pool->init(); + + // It's nice to have IFF data analyzed in this case too. + pool->add_trigger(0, 32, static_trigger_cb, pool); + + pool->data=gstr->duplicate(); + pool->added_data(0,pool->data->size()); +// char buffer[1024]; +// int length; +// while((length=str.read(buffer, 1024))) +// pool->add_data(buffer, length); + pool->set_eof(); + return retval; +} + +GP<DataPool> +DataPool::create(const GP<DataPool> & pool, int start, int length) +{ + DEBUG_MSG("DataPool::DataPool: pool=" << (void *)((DataPool *)pool) << " start=" << start << " length= " << length << "\n"); + DEBUG_MAKE_INDENT(3); + + DataPool *xpool=new DataPool(); + GP<DataPool> retval=xpool; + xpool->init(); + xpool->connect(pool, start, length); + return retval; +} + +GP<DataPool> +DataPool::create(const GURL &furl, int start, int length) +{ + DEBUG_MSG("DataPool::DataPool: furl='" << furl << "' start=" << start << " length= " << length << "\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool> retval=FCPools::get()->get_pool(furl,start,length); + if(! retval) + { + DataPool *pool=new DataPool(); + retval=pool; + pool->init(); + pool->connect(furl, start, length); + } + return retval; +} + +void +DataPool::clear_stream(const bool release) +{ + DEBUG_MSG("DataPool::clear_stream()\n"); + DEBUG_MAKE_INDENT(3); + if(fstream) + { + GCriticalSectionLock lock1(&class_stream_lock); + GP<OpenFiles_File> f=fstream; + if(f) + { + GCriticalSectionLock lock2(&(f->stream_lock)); + fstream=0; + if(release) + OpenFiles::get()->stream_released(f->stream, this); + } + } +} + +DataPool::~DataPool(void) +{ + DEBUG_MSG("DataPool::~DataPool()\n"); + DEBUG_MAKE_INDENT(3); + + clear_stream(true); + if (furl.is_local_file_url()) + { + FCPools::get()->del_pool(furl, this); + } + + { + // Wait until the static_trigger_cb() exits + GCriticalSectionLock lock(&trigger_lock); + if (pool) + pool->del_trigger(static_trigger_cb, this); + del_trigger(static_trigger_cb, this); + } + + if (pool) + { + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> trigger=triggers_list[pos]; + pool->del_trigger(trigger->callback, trigger->cl_data); + } + } + delete block_list; + delete active_readers; +} + +void +DataPool::connect(const GP<DataPool> & pool_in, int start_in, int length_in) +{ + DEBUG_MSG("DataPool::connect(): connecting to another DataPool\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) G_THROW( ERR_MSG("DataPool.connected1") ); + if (furl.is_local_file_url()) G_THROW( ERR_MSG("DataPool.connected2") ); + if (start_in<0) G_THROW( ERR_MSG("DataPool.neg_start") ); + + pool=pool_in; + start=start_in; + length=length_in; + + // The following will work for length<0 too + if (pool->has_data(start, length)) + eof_flag=true; + else + pool->add_trigger(start, length, static_trigger_cb, this); + + data=0; + + wake_up_all_readers(); + + // Pass registered trigger callbacks to the DataPool + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> t=triggers_list[pos]; + int tlength=t->length; + if (tlength<0 && length>0) + tlength=length-t->start; + pool->add_trigger(start+t->start, tlength, t->callback, t->cl_data); + } +} + +void +DataPool::connect(const GURL &furl_in, int start_in, int length_in) +{ + DEBUG_MSG("DataPool::connect(): connecting to a file\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) + G_THROW( ERR_MSG("DataPool.connected1") ); + if (furl.is_local_file_url()) + G_THROW( ERR_MSG("DataPool.connected2") ); + if (start_in<0) + G_THROW( ERR_MSG("DataPool.neg_start") ); + + + if (furl_in.name() == "-") + { + DEBUG_MSG("This is stdin => just read the data...\n"); + DEBUG_MAKE_INDENT(3); + char buffer[1024]; + int length; + GP<ByteStream> gstr=ByteStream::create(furl_in, "rb"); + ByteStream &str=*gstr; + while((length=str.read(buffer, 1024))) + add_data(buffer, length); + set_eof(); + } else if(furl_in.is_local_file_url()) + { + // Open the stream (just in this function) too see if + // the file is accessible. In future we will be using 'OpenFiles' + // to request and release streams + GP<ByteStream> str=ByteStream::create(furl_in,"rb"); + str->seek(0, SEEK_END); + int file_size=str->tell(); + + furl=furl_in; + start=start_in; + length=length_in; + if (start>=file_size) + length=0; + else if (length<0 || start+length>=file_size) + length=file_size-start; + + eof_flag=true; + + if(str->is_static()) + { + data=str; + added_data(0,length); + }else + { + data=0; + } + + FCPools::get()->add_pool(furl, this); + + wake_up_all_readers(); + + // Call every trigger callback + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> t=triggers_list[pos]; + call_callback(t->callback, t->cl_data); + } + triggers_list.empty(); + } +} + +int +DataPool::get_length(void) const +{ + // Not connected and length has been guessed + // Or connected to a file + // Or connected to a pool, but length was preset + int retval=(-1); + if (length>=0) + { + retval=length; + }else if (pool) + { + int plength=pool->get_length(); + if (plength>=0) + retval=plength-start; + } + return retval; +} + +int +DataPool::get_size(int dstart, int dlength) const +{ + if (dlength<0 && length>0) + { + dlength=length-dstart; + if (dlength<0) return 0; + } + + if (pool) return pool->get_size(start+dstart, dlength); + else if (furl.is_local_file_url()) + { + if (start+dstart+dlength>length) return length-(start+dstart); + else return dlength; + } else + { + if (dlength<0) + { + GCriticalSectionLock lock((GCriticalSection *) &data_lock); + dlength=data->size()-dstart; + } + return (dlength<0)?0:(block_list->get_bytes(dstart, dlength)); + } +} + +void +DataPool::add_data(const void * buffer, int size) + // This function adds data sequentially at 'add_at' position +{ + DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes of data...\n"); + DEBUG_MAKE_INDENT(3); + + add_data(buffer, add_at, size); + add_at+=size; +} + +void +DataPool::add_data(const void * buffer, int offset, int size) +{ + DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes at pos=" << + offset << "...\n"); + DEBUG_MAKE_INDENT(3); + + if (furl.is_local_file_url() || pool) + G_THROW( ERR_MSG("DataPool.add_data") ); + + // Add data to the data storage + { + GCriticalSectionLock lock(&data_lock); + if (offset>data->size()) + { + char ch=0; + data->seek(0, SEEK_END); + for(int i=data->size();i<offset;i++) + data->write(&ch, 1); + } else + { + data->seek(offset, SEEK_SET); + data->writall(buffer, size); + } + } + + added_data(offset, size); +} + +void +DataPool::added_data(const int offset, const int size) +{ + // Modify map of blocks + block_list->add_range(offset, size); + + // Wake up all threads, which may be waiting for this data + { + GCriticalSectionLock lock(&readers_lock); + for(GPosition pos=readers_list;pos;++pos) + { + GP<Reader> reader=readers_list[pos]; + if (block_list->get_bytes(reader->offset, 1)) + { + DEBUG_MSG("waking up reader: offset=" << reader->offset << + ", size=" << reader->size << "\n"); + DEBUG_MAKE_INDENT(3); + reader->event.set(); + } + } + } + + // And call triggers + check_triggers(); + + // Do not undo the following two lines. The reason why we need them + // here is the connected DataPools, which use 'length' (more exactly + // has_data()) to see if they have all data required. So, right after + // all data has been added to the master DataPool, but before EOF + // is set, the master and slave DataPools disagree regarding if + // all data is there or not. These two lines solve the problem + GCriticalSectionLock lock(&data_lock); + if (length>=0 && data->size()>=length) + set_eof(); +} + +bool +DataPool::has_data(int dstart, int dlength) +{ + if (dlength<0 && length>0) + dlength=length-dstart; + return (pool?(pool->has_data(start+dstart, dlength)) + :((furl.is_local_file_url())?(start+dstart+dlength<=length) + :((dlength<0)?is_eof() + :(block_list->get_bytes(dstart, dlength)==dlength)))); +} + +int +DataPool::get_data(void * buffer, int offset, int sz) +{ + return get_data(buffer, offset, sz, 0); +} + +class DataPool::Incrementor +{ +private: + Counter & counter; +public: + Incrementor(Counter & xcounter) : counter(xcounter) {counter.inc();} + ~Incrementor() {counter.dec();} +}; + +int +DataPool::get_data(void * buffer, int offset, int sz, int level) +{ + DEBUG_MSG("DataPool::get_data()\n"); + DEBUG_MAKE_INDENT(3); + Incrementor inc(*active_readers); + + if (stop_flag) + G_THROW( DataPool::Stop ); + if (stop_blocked_flag && !is_eof() && + !has_data(offset, sz)) + G_THROW( DataPool::Stop ); + + if (sz < 0) + G_THROW( ERR_MSG("DataPool.bad_size") ); + + if (! sz) + return 0; + + if (pool) + { + DEBUG_MSG("DataPool::get_data(): from pool\n"); + DEBUG_MAKE_INDENT(3); + int retval=0; + if (length>0 && offset+sz>length) + sz=length-offset; + if (sz<0) + sz=0; + for(;;) + { + // Ask the underlying (master) DataPool for data. Note, that + // master DataPool may throw the "DATA_POOL_REENTER" exception + // demanding all readers to restart. This happens when + // a DataPool in the chain of DataPools stops. All readers + // should return to the most upper level and then reenter the + // DataPools hierarchy. Some of them will be stopped by + // DataPool::Stop exception. + G_TRY + { + if(stop_flag||stop_blocked_flag&&!is_eof()&&!has_data(offset, sz)) + G_THROW( DataPool::Stop ); + retval=pool->get_data(buffer, start+offset, sz, level+1); + } + G_CATCH(exc) + { + pool->clear_stream(true); + if ((exc.get_cause() != GUTF8String( ERR_MSG("DataPool.reenter") ) ) || level) + G_RETHROW; + } G_ENDCATCH; + pool->clear_stream(true); + return retval; + } + } + else if(data && data->is_static() && eof_flag) + { + DEBUG_MSG("DataPool::get_data(): static\n"); + DEBUG_MAKE_INDENT(3); + // We're not connected to anybody => handle the data + int size=block_list->get_range(offset, sz); + if (size>0) + { + // Hooray! Some data is there + GCriticalSectionLock lock(&data_lock); + data->seek(offset, SEEK_SET); + return data->readall(buffer, size); + } + return 0; + } + else if (furl.is_local_file_url()) + { + DEBUG_MSG("DataPool::get_data(): from file\n"); + DEBUG_MAKE_INDENT(3); + if (length>0 && offset+sz>length) + sz=length-offset; + if (sz<0) + sz=0; + + GP<OpenFiles_File> f=fstream; + if (!f) + { + GCriticalSectionLock lock(&class_stream_lock); + f=fstream; + if(!f) + { + fstream=f=OpenFiles::get()->request_stream(furl, this); + } + } + GCriticalSectionLock lock2(&(f->stream_lock)); + f->stream->seek(start+offset, SEEK_SET); + return f->stream->readall(buffer, sz); + } + else + { + DEBUG_MSG("DataPool::get_data(): direct\n"); + DEBUG_MAKE_INDENT(3); + // We're not connected to anybody => handle the data + int size=block_list->get_range(offset, sz); + if (size>0) + { + // Hooray! Some data is there + GCriticalSectionLock lock(&data_lock); + data->seek(offset, SEEK_SET); + return data->readall(buffer, size); + } + + // No data available. + + // If there is no data and nothing else is expected, we can do + // two things: throw ByteStream::EndOfFile exception or return ZERO bytes. + // The exception is for the cases when the data flow has been + // terminated in the middle. ZERO bytes is for regular read() beyond + // the boundaries of legal data. The problem is to distinguish + // these two cases. We do it here with the help of analysis of the + // IFF structure of the data (which sets the 'length' variable). + // If we attempt to read beyond the [0, length[, ZERO bytes will be + // returned. Otherwise an ByteStream::EndOfFile exception will be thrown. + if (eof_flag) + { + if (length>0 && offset<length) + { + G_THROW( ByteStream::EndOfFile ); + } + else + { + return 0; + } + } + // Some data is still expected => add this reader to the + // list of readers and call virtual wait_for_data() + DEBUG_MSG("DataPool::get_data(): There is no data in the pool.\n"); + DEBUG_MSG("offset=" << offset << ", size=" << sz << + ", data_size=" << data->size() << "\n"); + GP<Reader> reader=new Reader(offset, sz); + G_TRY + { + { + GCriticalSectionLock slock(&readers_lock); + readers_list.append(reader); + } + wait_for_data(reader); + } + G_CATCH_ALL + { + { + GCriticalSectionLock slock(&readers_lock); + GPosition pos; + if (readers_list.search(reader, pos)) readers_list.del(pos); + } + G_RETHROW; + } + G_ENDCATCH; + + { + GCriticalSectionLock slock(&readers_lock); + GPosition pos; + if (readers_list.search(reader, pos)) readers_list.del(pos); + } + + // This call to get_data() should return immediately as there MUST + // be data in the buffer after wait_for_data(reader) returns + // or eof_flag should be TRUE + return get_data(buffer, reader->offset, reader->size, level); + } + return 0; +} + +void +DataPool::wait_for_data(const GP<Reader> & reader) + // This function may NOT return until there is some data for the + // given reader in the internal buffer +{ + DEBUG_MSG("DataPool::wait_for_data(): waiting for data at offset=" << reader->offset << + ", length=" << reader->size << "\n"); + DEBUG_MAKE_INDENT(3); + +#if THREADMODEL==NOTHREADS + G_THROW( ERR_MSG("DataPool.no_threadless") ); +#else + for(;;) + { + if (stop_flag) + G_THROW( DataPool::Stop ); + if (reader->reenter_flag) + G_THROW( ERR_MSG("DataPool.reenter") ); + if (eof_flag || block_list->get_bytes(reader->offset, 1)) + return; + if (pool || furl.is_local_file_url()) + return; + + if (stop_blocked_flag) + G_THROW( DataPool::Stop ); + + DEBUG_MSG("calling event.wait()...\n"); + reader->event.wait(); + } +#endif + + DEBUG_MSG("Got some data to read\n"); +} + +void +DataPool::wake_up_all_readers(void) +{ + DEBUG_MSG("DataPool::wake_up_all_readers(): waking up all readers\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&readers_lock); + for(GPosition pos=readers_list;pos;++pos) + readers_list[pos]->event.set(); +} + +void +DataPool::set_eof(void) + // Has no effect on connected DataPools +{ + if (!furl.is_local_file_url() && !pool) + { + eof_flag=true; + + // Can we set the length now? + if (length<0) + { + GCriticalSectionLock lock(&data_lock); + length=data->size(); + } + + // Wake up all readers to let them rescan the flags + wake_up_all_readers(); + + // Activate all trigger callbacks with negative threshold + check_triggers(); + } +} + +void +DataPool::stop(bool only_blocked) +{ + DEBUG_MSG("DataPool::stop(): Stopping this and dependent DataPools, only_blocked=" + << only_blocked << "\n"); + DEBUG_MAKE_INDENT(3); + + if (only_blocked) stop_blocked_flag=true; + else stop_flag=true; + + + wake_up_all_readers(); + + // Now let all readers, which already go thru to the master DataPool, + // come back and reenter. While reentering some of them will go + // thru this DataPool again and will be stopped (DataPool::Stop exception) + // Others (which entered the master DataPool thru other slave DataPools) + // will simply continue waiting for their data. + if (pool) + { + // This loop is necessary because there may be another thread, which + // is going down thru the DataPool chain and did not reach the + // lowest "master" DataPool yet. Since it didn't reach it yet, + // the "pool->restart_readers()" will not restart it. So we're going + // to continue issuing this command until we get rid of all + // "active_readers" + while(*active_readers) + { +#if (THREADMODEL==COTHREADS) || (THREADMODEL==MACTHREADS) + GThread::yield(); +#endif + pool->restart_readers(); + } + } +} + +void +DataPool::restart_readers(void) +{ + DEBUG_MSG("DataPool::restart_readers(): telling all readers to reenter\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock slock(&readers_lock); + for(GPosition pos=readers_list;pos;++pos) + { + GP<Reader> reader=readers_list[pos]; + reader->reenter_flag=true; + reader->event.set(); + } + + if (pool) + pool->restart_readers(); +} + +void +DataPool::load_file(void) +{ + DEBUG_MSG("DataPool::load_file() called\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) + { + DEBUG_MSG("passing the request down.\n"); + pool->load_file(); + } else if (furl.is_local_file_url()) + { + DEBUG_MSG("loading the data from \""<<(const char *)furl<<"\".\n"); + + GCriticalSectionLock lock1(&class_stream_lock); + GP<OpenFiles_File> f=fstream; + if (!f) + { + fstream=f=OpenFiles::get()->request_stream(furl, this); + } + { // Scope to de-allocate lock2 before stream gets released + GCriticalSectionLock lock2(&(f->stream_lock)); + + data=ByteStream::create(); + block_list->clear(); + FCPools::get()->del_pool(furl, this); + furl=GURL(); + + const GP<ByteStream> gbs=f->stream; + gbs->seek(0, SEEK_SET); + data=gbs->duplicate(); + added_data(0,data->size()); + set_eof(); +// char buffer[1024]; +// int length; +// while((length=f->stream->read(buffer, 1024))) +// add_data(buffer, length); + // No need to set EOF. It should already be set. + OpenFiles::get()->stream_released(f->stream, this); + } + fstream=0; + } else { DEBUG_MSG("Not connected\n"); } +} + +void +DataPool::load_file(const GURL &url ) +{ + FCPools::get()->load_file(url); +} + +void +DataPool::check_triggers(void) + // This function is for not connected DataPools only +{ + DEBUG_MSG("DataPool::check_triggers(): calling activated trigger callbacks.\n"); + DEBUG_MAKE_INDENT(3); + + if (!pool && !furl.is_local_file_url()) + while(true) + { + GP<Trigger> trigger; + + // First find a candidate (trigger, which needs to be called) + // Don't remove it from the list yet. del_trigger() should + // be able to find it if necessary and disable. + { + GCriticalSectionLock list_lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> t=triggers_list[pos]; + if (is_eof() || t->length>=0 && + block_list->get_bytes(t->start, t->length)==t->length) + { + trigger=t; + break; + } + } + } + + if (trigger) + { + // Now check that the trigger is not disabled + // and lock the trigger->disabled lock for the duration + // of the trigger. This will block the del_trigger() and + // will postpone client's destruction (usually following + // the call to del_trigger()) + { + GMonitorLock lock(&trigger->disabled); + if (!trigger->disabled) + call_callback(trigger->callback, trigger->cl_data); + } + + // Finally - remove the trigger from the list. + GCriticalSectionLock list_lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + if (triggers_list[pos]==trigger) + { + triggers_list.del(pos); + break; + } + } else break; + } +} + +void +// DataPool::add_trigger(int thresh, void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) +DataPool::add_trigger(int thresh, void (* callback)(void *), void * cl_data) +{ + if (thresh>=0) + add_trigger(0, thresh+1, callback, cl_data); + else + add_trigger(0, -1, callback, cl_data); +} + +void +DataPool::add_trigger(int tstart, int tlength, +// void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) + void (* callback)(void *), void * cl_data) +{ + DEBUG_MSG("DataPool::add_trigger(): start=" << tstart << + ", length=" << tlength << ", func=" << (void *) callback << "\n"); + DEBUG_MAKE_INDENT(3); + + if (callback) + { + if (is_eof()) + { + call_callback(callback, cl_data); + }else + { + if (pool) + { + // We're connected to a DataPool + // Just pass the triggers down remembering it in the list + if (tlength<0 && length>0) tlength=length-tstart; + GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data); + pool->add_trigger(start+tstart, tlength, callback, cl_data); + GCriticalSectionLock lock(&triggers_lock); + triggers_list.append(trigger); + } else if (!furl.is_local_file_url()) + { + // We're not connected to anything and maintain our own data + if (tlength>=0 && block_list->get_bytes(tstart, tlength)==tlength) + call_callback(callback, cl_data); + else + { + GCriticalSectionLock lock(&triggers_lock); + triggers_list.append(new Trigger(tstart, tlength, callback, cl_data)); + } + } + } + } +} + +void +// DataPool::del_trigger(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) +DataPool::del_trigger(void (* callback)(void *), void * cl_data) +{ + DEBUG_MSG("DataPool::del_trigger(): func=" << (void *) callback << "\n"); + DEBUG_MAKE_INDENT(3); + + for(;;) + { + GP<Trigger> trigger; + { + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;) + { + GP<Trigger> t=triggers_list[pos]; + if (t->callback==callback && t->cl_data==cl_data) + { + trigger=t; + GPosition this_pos=pos; + ++pos; + triggers_list.del(this_pos); + break; + } else + ++pos; + } + } + + // Above we removed the trigger from the list and unlocked the list + // Now we will disable it and will wait if necessary (if the + // trigger is currently being processed by check_triggers()) + // check_triggers() locks the trigger for the duration of the + // trigger callback. Thus we will wait for the trigger callback + // to finish and avoid client's destruction. + if (trigger) + trigger->disabled=1; + else + break; + } + + if (pool) + pool->del_trigger(callback, cl_data); +} + +void +// DataPool::static_trigger_cb(GP<GPEnabled> &cl_data) +DataPool::static_trigger_cb(void *cl_data) +{ +// GP<DataPool> d=(DataPool *)(GPEnabled *)cl_data; + GP<DataPool> d=(DataPool *)cl_data; + d->trigger_cb(); +} + +void +DataPool::trigger_cb(void) + // This function may be triggered by the DataPool, which we're + // connected to, or by ourselves, if we're connected to nothing +{ + // Don't want to be destroyed while I'm here. Can't use GP<> life saver + // because it may be called from the constructor + GCriticalSectionLock lock(&trigger_lock); + + DEBUG_MSG("DataPool::trigger_cb() called\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) + { + // Connected to a pool + // We may be here when either EOF is set on the master DataPool + // Or when it may have learnt its length (from IFF or whatever) + if (pool->is_eof() || pool->has_data(start, length)) eof_flag=true; + } else if (!furl.is_local_file_url()) + { + // Not connected to anything => Try to guess the length + if (length<0) analyze_iff(); + + // Failed to analyze? Check, maybe it's EOF already + if (length<0 && is_eof()) + { + GCriticalSectionLock lock(&data_lock); + length=data->size(); + } + } +} + +void +DataPool::analyze_iff(void) + // In order to display decode progress properly, we need to know + // the size of the data. It's trivial to figure it out if is_eof() + // is true. Otherwise we need to make a prediction. Luckily all + // DjVuFiles have IFF structure, which makes it possible to do it. + // If due to some reason we fail, the length will remain -1. +{ + DEBUG_MSG("DataPool::analyze_iff(): Trying to decode IFF structure of " << furl << ".\n"); + DEBUG_MSG("in order to predict the DataPool's size\n"); + DEBUG_MAKE_INDENT(3); + + GP<ByteStream> str=get_stream(); + + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + int size; + if ((size=iff.get_chunk(chkid)) && size>=0) + { + length=size+iff.tell()-4; + DEBUG_MSG("Got size=" << size << ", length=" << length << "\n"); + } +} + + +//**************************************************************************** +//****************************** PoolByteStream ****************************** +//**************************************************************************** + +// This is an internal ByteStream receiving data from the associated DataPool. +// It's just a sequential interface, nothing more. All the job for data +// retrieval, waiting and thread synchronization is done by DataPool + +class PoolByteStream : public ByteStream +{ +public: + PoolByteStream(GP<DataPool> data_pool); + virtual ~PoolByteStream() {}; + + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual long tell(void) const ; + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); +private: + // Don't make data_pool GP<>. The problem is that DataPool creates + // and soon destroys this ByteStream from the constructor. Since + // there are no other pointers to the DataPool created yet, it becomes + // destroyed immediately :( + DataPool * data_pool; + GP<DataPool> data_pool_lock; + long position; + + char buffer[512]; + size_t buffer_size; + size_t buffer_pos; + + // Cancel C++ default stuff + PoolByteStream & operator=(const PoolByteStream &); +}; + +inline +PoolByteStream::PoolByteStream(GP<DataPool> xdata_pool) : + data_pool(xdata_pool), position(0), buffer_size(0), buffer_pos(0) +{ + if (!data_pool) + G_THROW( ERR_MSG("DataPool.zero_DataPool") ); + + // Secure the DataPool if possible. If we're called from DataPool + // constructor (get_count()==0) there is no need to secure at all. + if (data_pool->get_count()) data_pool_lock=data_pool; +} + +size_t +PoolByteStream::read(void *data, size_t size) +{ + if (buffer_pos >= buffer_size) { + if (size >= sizeof(buffer)) { + // Direct read + size = data_pool->get_data(data, position, size); + position += size; + return size; + } else { + // Refill buffer + buffer_size = data_pool->get_data(buffer, position, sizeof(buffer)); + buffer_pos=0; + } + } + if (buffer_pos + size >= buffer_size) + size = buffer_size - buffer_pos; + memcpy(data, buffer+buffer_pos, size); + buffer_pos += size; + position += size; + return size; +} + +size_t +PoolByteStream::write(const void *buffer, size_t size) +{ + G_THROW( ERR_MSG("not_implemented_n") "\tPoolByteStream::write()"); // PoolByteStream::write() is not implemented. + return 0; // For compiler not to bark +} + +long +PoolByteStream::tell(void) const +{ + return position; +} + +int +PoolByteStream::seek(long offset, int whence, bool nothrow) +{ + int retval=(-1); + switch(whence) + { + case SEEK_CUR: + offset+=position; + // fallthrough; + case SEEK_SET: + if(offset<position) + { + if((int)(offset+buffer_pos)>=(int)position) + { + buffer_pos-=position-offset; + }else + { + buffer_size=0; + } + position=offset; + }else if(offset>position) + { + buffer_pos+=(offset-position)-1; + position=offset-1; + unsigned char c; + if(read(&c,1)<1) + { + G_THROW( ByteStream::EndOfFile ); + } + } + retval=0; + break; + case SEEK_END: + if(! nothrow) + G_THROW( ERR_MSG("DataPool.seek_backward") ); + break; + } + return retval; +} + +void +DataPool::close_all(void) +{ + OpenFiles::get()->close_all(); +} + + +GP<ByteStream> +DataPool::get_stream(void) +{ + if(data && data->is_static()) + { + GCriticalSectionLock lock(&data_lock); + data->seek(0, SEEK_SET); + return data->duplicate(length); + }else + { + return new PoolByteStream(this); + } +} + + +inline +DataPool::Counter::operator int(void) const +{ + GCriticalSectionLock lk((GCriticalSection *) &lock); + int cnt=counter; + return cnt; +} + +inline void +DataPool::Counter::inc(void) +{ + GCriticalSectionLock lk(&lock); + counter++; +} + +inline void +DataPool::Counter::dec(void) +{ + GCriticalSectionLock lk(&lock); + counter--; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DataPool.h b/kviewshell/plugins/djvu/libdjvu/DataPool.h new file mode 100644 index 00000000..fb4bea4e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DataPool.h @@ -0,0 +1,627 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DataPool.h,v 1.10 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DATAPOOL_H +#define _DATAPOOL_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GThreads.h" +#include "GString.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DataPool.h + Files #"DataPool.h"# and #"DataPool.cpp"# implement classes \Ref{DataPool} + and \Ref{DataRange} used by DjVu decoder to access data. + + The main goal of class \Ref{DataPool} is to provide concurrent access + to the same data from many threads with a possibility to add data + from yet another thread. It is especially important in the case of the + Netscape plugin when data is not immediately available, but decoding + should be started as soon as possible. In this situation it is vital + to provide transparent access to the data from many threads possibly + blocking readers that try to access information that has not been + received yet. + + When the data is local though, it can be accessed directly using + standard IO mechanism. To provide a uniform interface for decoding + routines, \Ref{DataPool} supports file mode as well. + + @memo Thread safe data storage + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DataPool.h,v 1.10 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** Thread safe data storage. + The purpose of #DataPool# is to provide a uniform interface for + accessing data from decoding routines running in a multi-threaded + environment. Depending on the mode of operation it may contain the + actual data, may be connected to another #DataPool# or may be mapped + to a file. Regardless of the mode, the class returns data in a + thread-safe way, blocking reading threads if there is no data of + interest available. This blocking is especially useful in the + networking environment (plugin) when there is a running decoding thread, + which wants to start decoding as soon as there is just one byte available + blocking if necessary. + + Access to data in a #DataPool# may be direct (Using \Ref{get_data}() + function) or sequential (See \Ref{get_stream}() function). + + If the #DataPool# is not connected to anything, that is it contains + some real data, this data can be added to it by means of two + \Ref{add_data}() functions. One of them adds data sequentially maintaining + the offset of the last block of data added by it. The other can store + data anywhere. Thus it's important to realize, that there may be "white + spots" in the data storage. + + There is also a way to test if data is available for some given data + range (See \Ref{has_data}()). In addition to this mechanism, there are + so-called {\em trigger callbacks}, which are called, when there is + all data available for a given data range. + + Let us consider all modes of operation in details: + + \begin{enumerate} + \item {\bf Not connected #DataPool#}. In this mode the #DataPool# + contains some real data. As mentioned above, it may be added + by means of two functions \Ref{add_data}() operating independent + of each other and allowing to add data sequentially and + directly to any place of data storage. It's important to call + function \Ref{set_eof}() after all data has been added. + + Functions like \Ref{get_data}() or \Ref{get_stream}() can + be used to obtain direct or sequential access to the data. As + long as \Ref{is_eof}() is #FALSE#, #DataPool# will block every + reader, which is trying to read unavailable data until it + really becomes available. But as soon as \Ref{is_eof}() is + #TRUE#, any attempt to read non-existing data will read #0# bytes. + + Taking into account the fact, that #DataPool# was designed to + store DjVu files, which are in IFF formats, it becomes possible + to predict the size of the #DataPool# as soon as the first + #32# bytes have been added. This is invaluable for estimating + download progress. See function \Ref{get_length}() for details. + If this estimate fails (which means, that stored data is not + in IFF format), \Ref{get_length}() returns #-1#. + + Triggers may be added and removed by means of \Ref{add_trigger}() + and \Ref{del_trigger}() functions. \Ref{add_trigger}() takes + a data range. As soon as all data in that data range is + available, the trigger callback will be called. + + All trigger callbacks will be called when #EOF# condition + has been set. + + \item {\bf #DataPool# connected to another #DataPool#}. In this + {\em slave} mode you can map a given #DataPool# to any offsets + range inside another #DataPool#. You can connect the slave + #DataPool# even if there is no data in the master #DataPool#. + Any \Ref{get_data}() request will be forwarded to the master + #DataPool#, and it will be responsible for blocking readers + trying to access unavailable data. + + The usage of \Ref{add_data}() functions is prohibited for + connected #DataPool#s. + + The offsets range used to map a slave #DataPool# can be fully + specified (both start offset and length are positive numbers) + or partially specified (the length is negative). In this mode + the slave #DataPool# is assumed to extend up to the end + of the master #DataPool#. + + Triggers may be used with slave #DataPool#s as well as with + the master ones. + + Calling \Ref{stop}() function of a slave will stop only the slave + (and any other slave connected to it), but not the master. + + \Ref{set_eof}() function is meaningless for slaves. They obtain + the #ByteStream::EndOfFile# status from their master. + + Depending on the offsets range passed to the constructor, + \Ref{get_length}() returns different values. If the length + passed to the constructor was positive, then it is returned + by \Ref{get_length}() all the time. Otherwise the value returned + is either #-1# if master's length is still unknown (it didn't + manage to parse IFF data yet) or it is calculated as + #masters_length-slave_start#. + + \item {\bf #DataPool# connected to a file}. This mode is quite similar + to the case, when the #DataPool# is connected to another + #DataPool#. Similarly, the #DataPool# stores no data inside. + It just forwards all \Ref{get_data}() requests to the underlying + source (a file in this case). Thus these requests will never + block the reader. But they may return #0# if there is no data + available at the requested offset. + + The usage of \Ref{add_data}() functions is meaningless and + is prohibited. + + \Ref{is_eof}() function always returns #TRUE#. Thus \Ref{set_eof}() + us meaningless and does nothing. + + \Ref{get_length}() function always returns the file size. + + Calling \Ref{stop}() function will stop this #DataPool# and + any other slave connected to it. + + Trigger callbacks passed through \Ref{add_trigger}() function + are called immediately. + + This mode is useful to read and decode DjVu files without reading + and storing them in full in memory. + \end{enumerate} +*/ + +class DataPool : public GPEnabled +{ +public: // Classes used internally by DataPool + // These are declared public to support buggy C++ compilers. + class Incrementor; + class Reader; + class Trigger; + class OpenFiles; + class OpenFiles_File; + class BlockList; + class Counter; +protected: + DataPool(void); + +public: + /** @name Initialization */ + //@{ + /** Default creator. Will prepare #DataPool# for accepting data + added through functions \Ref{add_data}(). Use \Ref{connect}() + functions if you want to map this #DataPool# to another or + to a file. */ + static GP<DataPool> create(void); + + /** Creates and initialized the #DataPool# with data from stream #str#. + The constructor will read the stream's contents and add them + to the pool using the \Ref{add_data}() function. Afterwards it + will call \Ref{set_eof}() function, and no other data will be + allowed to be added to the pool. */ + static GP<DataPool> create(const GP<ByteStream> & str); + + /** Initializes the #DataPool# in slave mode and connects it + to the specified offsets range of the specified master #DataPool#. + It is equivalent to calling default constructor and function + \Ref{connect}(). + + @param master_pool Master #DataPool# providing data for this slave + @param start Beginning of the offsets range which the slave is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the master #DataPool#. + */ + static GP<DataPool> create(const GP<DataPool> & master_pool, int start=0, int length=-1); + + /** Initializes the #DataPool# in slave mode and connects it + to the specified offsets range of the specified file. + It is equivalent to calling default constructor and function + \Ref{connect}(). + @param url Name of the file to connect to. + @param start Beginning of the offsets range which the #DataPool# is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the file. + */ + static GP<DataPool> create(const GURL &url, int start=0, int length=-1); + + virtual ~DataPool(); + + /** Switches the #DataPool# to slave mode and connects it to the + specified offsets range of the master #DataPool#. + @param master_pool Master #DataPool# providing data for this slave + @param start Beginning of the offsets range which the slave is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the master #DataPool#. + */ + void connect(const GP<DataPool> & master_pool, int start=0, int length=-1); + /** Connects the #DataPool# to the specified offsets range of + the named #url#. + @param url Name of the file to connect to. + @param start Beginning of the offsets range which the #DataPool# is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the file. + */ + void connect(const GURL &url, int start=0, int length=-1); + //@} + + /** Tells the #DataPool# to stop serving readers. + + If #only_blocked# flag is #TRUE# then only those requests will + be processed, which would not block. Any attempt to get non-existing + data would result in a #STOP# exception (instead of blocking until + data is available). + + If #only_blocked# flag is #FALSE# then any further attempt to read + from this #DataPool# (as well as from any #DataPool# connected + to this one) will result in a #STOP# exception. */ + void stop(bool only_blocked=false); + + /** @name Adding data. + Please note, that these functions are for not connected #DataPool#s + only. You can not add data to a #DataPool#, which is connected + to another #DataPool# or to a file. + */ + //@{ + /** Appends the new block of data to the #DataPool#. There are two + \Ref{add_data}() functions available. One is for adding data + sequentially. It keeps track of the last byte position, which has + been stored {\bf by it} and always appends the next block after + this position. The other \Ref{add_data}() can store data anywhere. + + The function will unblock readers waiting for data if this data + arrives with this block. It may also trigger some {\em trigger + callbacks}, which may have been added by means of \Ref{add_trigger}() + function. + + {\bf Note:} After all the data has been added, it's necessary + to call \Ref{set_eof}() to tell the #DataPool# that nothing else + is expected. + + {\bf Note:} This function may not be called if the #DataPool# + has been connected to something. + + @param buffer data to append + @param size length of the {\em buffer} + */ + void add_data(const void * buffer, int size); + + /** Stores the specified block of data at the specified offset. + Like the function above this one can also unblock readers + waiting for data and engage trigger callbacks. The difference + is that {\bf this} function can store data anywhere. + + {\bf Note:} After all the data has been added, it's necessary + to call \Ref{set_eof}() to tell the #DataPool# that nothing else + is expected. + + {\bf Note:} This function may not be called if the #DataPool# + has been connected to something. + + @param buffer data to store + @param offset where to store the data + @param size length of the {\em buffer} */ + void add_data(const void * buffer, int offset, int size); + + /** Tells the #DataPool# that all data has been added and nothing else + is anticipated. When #EOF# is true, any reader attempting to read + non existing data will not be blocked. It will either read #ZERO# + bytes or will get an #ByteStream::EndOfFile# exception (see \Ref{get_data}()). + Calling this function will also activate all registered trigger + callbacks. + + {\bf Note:} This function is meaningless and does nothing + when the #DataPool# is connected to another #DataPool# or to + a file. */ + void set_eof(void); + //@} + + /** @name Accessing data. + These functions provide direct and sequential access to the + data of the #DataPool#. If the #DataPool# is not connected + (contains some real data) then it handles the requests itself. + Otherwise they are forwarded to the master #DataPool# or the file. + */ + //@{ + /** Attempts to return a block of data at the given #offset# + of the given #size#. + + \begin{enumerate} + \item If the #DataPool# is connected to another #DataPool# or + to a file, the request will just be forwarded to them. + \item If the #DataPool# is not connected to anything and + some of the data requested is in the internal buffer, + the function copies available data to #buffer# and returns + immediately. + + If there is no data available, and \Ref{is_eof}() returns + #FALSE#, the reader (and the thread) will be {\bf blocked} + until the data actually arrives. Please note, that since + the reader is blocked, it should run in a separate thread + so that other threads have a chance to call \Ref{add_data}(). + If there is no data available, but \Ref{is_eof}() is #TRUE# + the behavior is different and depends on the #DataPool#'s + estimate of the file size: + \begin{itemize} + \item If #DataPool# learns from the IFF structure of the + data, that its size should be greater than it + really is, then any attempt to read non-existing + data in the range of {\em valid} offsets will + result in an #ByteStream::EndOfFile# exception. This is done to + indicate, that there was an error in adding data, + and the data requested is {\bf supposed} to be + there, but has actually not been added. + \item If #DataPool#'s expectations about the data size + coincide with the reality then any attempt to + read data beyond the legal range of offsets will + result in #ZERO# bytes returned. + \end{itemize}. + \end{enumerate}. + + @param buffer Buffer to be filled with data + @param offset Offset in the #DataPool# to read data at + @param size Size of the {\em buffer} + @return The number of bytes actually read + @exception STOP The stream has been stopped + @exception EOF The requested data is not there and will not be added, + although it should have been. + */ + int get_data(void * buffer, int offset, int size); + + /** Returns a \Ref{ByteStream} to access contents of the #DataPool# + sequentially. By reading from the returned stream you basically + call \Ref{get_data}() function. Thus, everything said for it + remains true for the stream too. */ + GP<ByteStream> get_stream(void); + //@} + + /** @name State querying functions. */ + //@{ + /** Returns #TRUE# if this #DataPool# is connected to another #DataPool# + or to a file. */ + bool is_connected(void) const; + + /** Returns #TRUE# if all data available for offsets from + #start# till #start+length-1#. If #length# is negative, the + range is assumed to extend up to the end of the #DataPool#. + This function works both for connected and not connected #DataPool#s. + Once it returned #TRUE# for some offsets range, you can be + sure that the subsequent \Ref{get_data}() request will not block. + */ + bool has_data(int start, int length); + + /* Returns #TRUE# if no more data is planned to be added. + + {\bf Note:} This function always returns #TRUE# when the #DataPool# + has been initialized with a file name. */ + bool is_eof(void) const {return eof_flag;} + + /** Returns the {\em length} of data in the #DataPool#. The value + returned depends on the mode of operation: + \begin{itemize} + \item If the #DataPool# is not connected to anything then + the length returned is either calculated by interpreting + the IFF structure of stored data (if successful) or + by calculating the real size of data after \Ref{set_eof}() + has been called. Otherwise it is #-1#. + \item If the #DataPool# is connected to a file, the length + is calculated basing on the length passed to the + \Ref{connect}() function and the file size. + \item If the #DataPool# is connected to a master #DataPool#, + the length is calculated basing on the value returned + by the master's #get_length()# function and the length + passed to the \Ref{connect}() function. + \end{itemize}. */ + int get_length(void) const; + /** Returns the number of bytes of data available in this #DataPool#. + Contrary to the \Ref{get_length}() function, this one doesn't try + to interpret the IFF structure and predict the file length. + It just returns the number of bytes of data really available inside + the #DataPool#, if it contains data, or inside its range, if it's + connected to another #DataPool# or a file. */ + int get_size(void) const {return get_size(0, -1);} + //@} + + /** @name Trigger callbacks. + {\em Trigger callbacks} are special callbacks called when + all data for the given range of offsets has been made available. + Since reading unavailable data may result in a thread block, + which may be bad, the usage of {\em trigger callbacks} appears + to be a convenient way to signal availability of data. + + You can add a trigger callback in two ways: + \begin{enumerate} + \item By specifying a range. This is the most general case + \item By providing just one {\em threshold}. In this case + the range is assumed to start from offset #ZERO# and + last for {\em threshold}+1 bytes. + \end{enumerate} + */ + //@{ + /** Associates the specified {\em trigger callback} with the + given data range. + + {\bf Note:} The callback may be called immediately if all + data for the given range is already available or #EOF# is #TRUE#. + + @param start The beginning of the range for which all data + should be available + @param length If the {\em length} is not negative then the callback + will be called when there is data available for every + offset from {\em start} to {\em start+length-1}. + If {\em thresh} is negative, the callback is called after + #EOF# condition has been set. + @param callback Function to call + @param cl_data Argument to pass to the callback when it's called. */ + void add_trigger(int start, int length, +// void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data); + void (* callback)(void *), void * cl_data); + + /** Associates the specified {\em trigger callback} with the + specified threshold. + + This function is a simplified version of the function above. + The callback will be called when there is data available for + every offset from #0# to #thresh#, if #thresh# is positive, or + when #EOF# condition has been set otherwise. */ +// void add_trigger(int thresh, void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data); + void add_trigger(int thresh, void (* callback)(void *), void * cl_data); + + /** Use this function to unregister callbacks, which are no longer + needed. {\bf Note!} It's important to do it when the client + is about to be destroyed. */ + void del_trigger(void (* callback)(void *), void * cl_data); +// void del_trigger(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data); + //@} + + /** Loads data from the file into memory. This function is only useful + for #DataPool#s getting data from a file. It descends the #DataPool#s + hierarchy until it either reaches a file-connected #DataPool# + or #DataPool# containing the real data. In the latter case it + does nothing, in the first case it makes the #DataPool# read all + data from the file into memory and stop using the file. + + This may be useful when you want to overwrite the file and leave + existing #DataPool#s with valid data. */ + void load_file(void); + /** This function will make every #DataPool# in the program, which + is connected to a file, to load the file contents to the main + memory and close the file. This feature is important when you + want to do something with the file like remove or overwrite it + not affecting the rest of the program. */ + static void load_file(const GURL &url); + + /** This function will remove OpenFiles filelist. */ + static void close_all(void); + + // Internal. Used by 'OpenFiles' + void clear_stream(const bool release = true); + + /** Useful in comparing data pools. Returns true if dirived from + same URL or bytestream. */ + bool simple_compare(DataPool &pool) const; +private: + bool eof_flag; + bool stop_flag; + bool stop_blocked_flag; + + Counter *active_readers; + + // Source or storage of data + GP<DataPool> pool; + GURL furl; + GP<OpenFiles_File> fstream; + GCriticalSection class_stream_lock; + GP<ByteStream> data; + GCriticalSection data_lock; + BlockList *block_list; + int add_at; + int start, length; + + // List of readers waiting for data + GPList<Reader> readers_list; + GCriticalSection readers_lock; + + // Triggers + GPList<Trigger> triggers_list; // List of passed or our triggers + GCriticalSection triggers_lock; // Lock for the list above + GCriticalSection trigger_lock; // Lock for static_trigger_cb() + + void init(void); + void wait_for_data(const GP<Reader> & reader); + void wake_up_all_readers(void); + void check_triggers(void); + int get_data(void * buffer, int offset, int size, int level); + int get_size(int start, int length) const; + void restart_readers(void); + +// static void static_trigger_cb(GP<GPEnabled> &); + static void static_trigger_cb(void *); + void trigger_cb(void); + void analyze_iff(void); + void added_data(const int offset, const int size); +public: + static const char *Stop; + friend class FCPools; +}; + +inline bool +DataPool::simple_compare(DataPool &pool) const +{ + // return true if these pools are identical. False means they may or may + // not be identical. + return (this == &pool) + ||(furl.is_valid()&&!furl.is_empty()&&pool.furl.is_valid()&&(furl == pool.furl)) + ||(data && (data == pool.data)); +} + +inline bool +DataPool::is_connected(void) const +{ + return furl.is_local_file_url() || pool!=0; +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmDir.cpp new file mode 100644 index 00000000..83f9df78 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir.cpp @@ -0,0 +1,839 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir.cpp,v 1.10 2004/05/05 15:12:42 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVmDir.h" +#include "BSByteStream.h" +#include "GURL.h" +#include "debug.h" + +#include <ctype.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GP<DjVmDir::File> +DjVmDir::File::create(const GUTF8String &load_name, + const GUTF8String &save_name, const GUTF8String &title, + const FILE_TYPE file_type) +{ + File *file_ptr=new File(); + GP<File> file=file_ptr; + file_ptr->set_load_name(load_name); + file_ptr->set_save_name(save_name); + file_ptr->set_title(title); + file_ptr->flags=(file_type & TYPE_MASK); + return file; +} + +const GUTF8String & +DjVmDir::File::check_save_name(const bool xis_bundled) +{ + if(!xis_bundled && !valid_name) + { + GUTF8String retval=name.length()?name:id; + if(GUTF8String(GNativeString(retval)) != retval) + { + const_cast<bool &>(valid_name)=true; + char *buf; + GPBuffer<char> gbuf(buf,2*retval.length()+1); + char *s=buf; + int i=0; + for(char c=retval[i++];c;) + { + static const char hex[]="0123456789ABCDEF"; + int len=retval.nextChar(i)-i; + if(len>1 || ((len == 1)&&(c&0x80))) + { + do + { + s++[0]=hex[(c>>4)&0xf]; + s++[0]=hex[(c&0xf)]; + c=retval[i++]; + } while(c && ((--len) > 0)); + }else + { + s++[0]=c; + c=retval[i++]; + } + } + s++[0]=0; + oldname=retval; + name=buf; + } + const_cast<bool &>(valid_name)=true; + } + return *(name.length()?&name:&id); +} + +const GUTF8String & +DjVmDir::File::get_save_name(void) const +{ + return *(name.length()?&name:&id); +} + +void +DjVmDir::File::set_load_name(const GUTF8String &xid) +{ + GURL url=GURL::UTF8(xid); + if(!url.is_valid()) + { + url=GURL::Filename::UTF8(xid); + } + id=url.fname(); +} + +void +DjVmDir::File::set_save_name(const GUTF8String &xname) +{ + GURL url; + valid_name=false; + if(!xname.length()) + { + GURL url=GURL::UTF8(id); + if(!url.is_valid()) + { + name=id; + }else + { + name=url.fname(); + } + }else + { + GURL url=GURL::UTF8(xname); + if(!url.is_valid()) + { + url=GURL::Filename::UTF8(xname); + } + name=url.fname(); + } + oldname=""; +} + +/* DjVmDir::File */ + +DjVmDir::File::File(void) : offset(0), size(0), valid_name(false), + flags(0), page_num(-1) { } + +GUTF8String +DjVmDir::File::get_str_type(void) const +{ + GUTF8String type; + switch(flags & TYPE_MASK) + { + case INCLUDE: + type="INCLUDE"; + break; + case PAGE: + type="PAGE"; + break; + case THUMBNAILS: + type="THUMBNAILS"; + break; + case SHARED_ANNO: + type="SHARED_ANNO"; + break; + default: + // Internal error: please modify DjVmDir::File::get_str_type() + // to contain all possible File types. + G_THROW( ERR_MSG("DjVmDir.get_str_type") ); + } + return type; +} + + +const int DjVmDir::version=1; + +void +DjVmDir::decode(const GP<ByteStream> &gstr) +{ + ByteStream &str=*gstr; + DEBUG_MSG("DjVmDir::decode(): decoding contents of 'DIRM' chunk...\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + GPosition pos; + + files_list.empty(); + page2file.resize(-1); + name2file.empty(); + id2file.empty(); + title2file.empty(); + + int ver=str.read8(); + bool bundled=(ver & 0x80)!=0; + ver&=0x7f; + + DEBUG_MSG("DIRM version=" << ver << ", our version=" << version << "\n"); + if (ver>version) + G_THROW( ERR_MSG("DjVmDir.version_error") "\t" + + GUTF8String(version) + "\t" + GUTF8String(ver)); + // Unable to read DJVM directories of versions higher than xxx + // Data version number is yyy. + DEBUG_MSG("bundled directory=" << bundled << "\n"); + DEBUG_MSG("reading the directory records...\n"); + int files=str.read16(); + DEBUG_MSG("number of files=" << files << "\n"); + + if (files) + { + DEBUG_MSG("reading offsets (and sizes for ver==0)\n"); + for(int nfile=0;nfile<files;nfile++) + { + GP<File> file=new File(); + files_list.append(file); + if (bundled) + { + file->offset=str.read32(); + if (ver==0) + file->size=str.read24(); + if (file->offset==0) + G_THROW( ERR_MSG("DjVmDir.no_indirect") ); + } else + { + file->offset=file->size=0; + } + } + + GP<ByteStream> gbs_str=BSByteStream::create(gstr); + ByteStream &bs_str=*gbs_str; + if (ver>0) + { + DEBUG_MSG("reading and decompressing sizes...\n"); + for(GPosition pos=files_list;pos;++pos) + files_list[pos]->size=bs_str.read24(); + } + + DEBUG_MSG("reading and decompressing flags...\n"); + for(pos=files_list;pos;++pos) + files_list[pos]->flags=bs_str.read8(); + + if (!ver) + { + DEBUG_MSG("converting flags from version 0...\n"); + for(pos=files_list;pos;++pos) + { + unsigned char flags_0=files_list[pos]->flags; + unsigned char flags_1; + flags_1=(flags_0 & File::IS_PAGE_0)?(File::PAGE):(File::INCLUDE); + if (flags_0 & File::HAS_NAME_0) + flags_1|=File::HAS_NAME; + if (flags_0 & File::HAS_TITLE_0) + flags_1|=File::HAS_TITLE; + files_list[pos]->flags=flags_1; + } + } + + DEBUG_MSG("reading and decompressing names...\n"); + GTArray<char> strings; + char buffer[1024]; + int length; + while((length=bs_str.read(buffer, 1024))) + { + int strings_size=strings.size(); + strings.resize(strings_size+length-1); + memcpy((char*) strings+strings_size, buffer, length); + } + DEBUG_MSG("size of decompressed names block=" << strings.size() << "\n"); + + // Copy names into the files + const char * ptr=strings; + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + + file->id=ptr; + ptr+=file->id.length()+1; + if (file->flags & File::HAS_NAME) + { + file->name=ptr; + ptr+=file->name.length()+1; + } else + { + file->name=file->id; + } + if (file->flags & File::HAS_TITLE) + { + file->title=ptr; + ptr+=file->title.length()+1; + } else + file->title=file->id; + /* msr debug: multipage file, file->title is null. + DEBUG_MSG(file->name << ", " << file->id << ", " << file->title << ", " << + file->offset << ", " << file->size << ", " << + file->is_page() << "\n"); */ + } + + // Check that there is only one file with SHARED_ANNO flag on + int shared_anno_cnt=0; + for(pos=files_list;pos;++pos) + { + if (files_list[pos]->is_shared_anno()) + { + shared_anno_cnt++; + } + } + if (shared_anno_cnt>1) + G_THROW( ERR_MSG("DjVmDir.corrupt") ); + + // Now generate page=>file array for direct access + int pages=0; + for(pos=files_list;pos;++pos) + pages+=files_list[pos]->is_page() ? 1 : 0; + DEBUG_MSG("got " << pages << " pages\n"); + page2file.resize(pages-1); + int page_num=0; + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->is_page()) + { + page2file[page_num]=file; + file->page_num=page_num++; + } + } + + // Generate name2file map + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (name2file.contains(file->name)) + G_THROW( ERR_MSG("DjVmDir.dupl_name") "\t" + file->name ); + name2file[file->name]=file; + } + + // Generate id2file map + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (id2file.contains(file->id)) + G_THROW( ERR_MSG("DjVmDir.dupl_id") "\t" + file->id); + id2file[file->id]=file; + } + + // Generate title2file map + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->title.length()) + { + if (title2file.contains(file->title)) + G_THROW( ERR_MSG("DjVmDir.dupl_title") "\t" + file->title); + title2file[file->title]=file; + } + } + } +} + + +void +DjVmDir::encode(const GP<ByteStream> &gstr, const bool do_rename) const +{ + bool bundled = true; + GPosition pos = files_list; + if (files_list.size() && !files_list[pos]->offset) + bundled = false; + for (pos=files_list; pos; ++pos) + if ( !bundled != !files_list[pos]->offset) + // There directory contains both indirect and bundled records. + G_THROW( ERR_MSG("DjVmDir.bad_dir") ); + // Do the real work + encode(gstr, bundled, do_rename); +} + +void +DjVmDir::encode(const GP<ByteStream> &gstr, const bool bundled, const bool do_rename) const +{ + ByteStream &str=*gstr; + DEBUG_MSG("DjVmDir::encode(): encoding contents of the 'DIRM' chunk do_rename=" << do_rename << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + GPosition pos; + + DEBUG_MSG("encoding version number=" << version << ", bundled=" << bundled << "\n"); + str.write8(version | ((int) bundled<< 7)); + + DEBUG_MSG("storing the number of records=" << files_list.size() << "\n"); + str.write16(files_list.size()); + + if (files_list.size()) + { + // Check that there is only one file with shared annotations + int shared_anno_cnt=0; + for (pos=files_list; pos; ++pos) + if (files_list[pos]->is_shared_anno()) + shared_anno_cnt++; + if (shared_anno_cnt>1) + G_THROW( ERR_MSG("DjVmDir.multi_save") ); + + if (bundled) + { + // We need to store offsets uncompressed. That's because when + // we save a DjVmDoc, we first compress the DjVmDir with dummy + // offsets and after computing the real offsets we rewrite the + // DjVmDir, which should not change its size during this operation + DEBUG_MSG("storing offsets for every record\n"); + for (pos=files_list; pos; ++pos) + { + GP<File> file=files_list[pos]; + if (!file->offset) + // The directory contains record without offset + G_THROW( ERR_MSG("DjVmDir.bad_dir") ); + str.write32(file->offset); + } + } + + GP<ByteStream> gbs_str=BSByteStream::create(gstr, 50); + ByteStream &bs_str=*gbs_str; + DEBUG_MSG("storing and compressing sizes for every record\n"); + for (pos=files_list; pos; ++pos) + { + const GP<File> file(files_list[pos]); + bs_str.write24(file->size); + } + DEBUG_MSG("storing and compressing flags for every record\n"); + const bool xdo_rename=(do_rename||!bundled); + for(pos=files_list;pos;++pos) + { + const GP<File> file(files_list[pos]); + if(xdo_rename) + { + const GUTF8String new_id = file->name; + if (! new_id) + if(!file->oldname.length() || file->oldname == new_id) + file->flags &= ~File::HAS_NAME; + else + file->flags |= File::HAS_NAME; + } + else + { + if (!file->name.length() || file->name == file->id) + file->flags &= ~File::HAS_NAME; + else + file->flags |= File::HAS_NAME; + } + if (file->title.length() && (file->title!=file->id)) + file->flags |= File::HAS_TITLE; + else + file->flags &= ~File::HAS_TITLE; + + bs_str.write8(file->flags); + } + + DEBUG_MSG("storing and compressing names...\n"); + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + GUTF8String id; + GUTF8String name; + GUTF8String title; + if (xdo_rename) + { + id = file->name; + if (! id) + id = file->id; + if ((file->flags) & File::HAS_NAME) + name = file->oldname; + } + else + { + id=file->id; + if ((file->flags) & File::HAS_NAME) + name = file->name; + } + if ((file->flags) & File::HAS_TITLE) + title = file->title; + DEBUG_MSG("rename=" <<xdo_rename<<" id='" << id << "' name='" << name << "' title='" << title << "'\n"); + bs_str.writestring(id); + bs_str.write8(0); + if (name.length()) + { + bs_str.writestring(name); + bs_str.write8(0); + } + if (title.length()) + { + bs_str.writestring(title); + bs_str.write8(0); + } + } + } + DEBUG_MSG("done\n"); +} + +GP<DjVmDir::File> +DjVmDir::page_to_file(int page_num) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + return (page_num<page2file.size())?page2file[page_num]:(GP<DjVmDir::File>(0)); +} + +GP<DjVmDir::File> +DjVmDir::name_to_file(const GUTF8String & name) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + return (name2file.contains(name, pos))?name2file[pos]:(GP<DjVmDir::File>(0)); +} + +GP<DjVmDir::File> +DjVmDir::id_to_file(const GUTF8String &id) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + return (id2file.contains(id, pos))?id2file[pos]:(GP<DjVmDir::File>(0)); +} + +GP<DjVmDir::File> +DjVmDir::title_to_file(const GUTF8String &title) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + return (title2file.contains(title, pos))?title2file[pos]:(GP<DjVmDir::File>(0)); +} + +GPList<DjVmDir::File> +DjVmDir::get_files_list(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return files_list; +} + +int +DjVmDir::get_files_num(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return files_list.size(); +} + +int +DjVmDir::get_pages_num(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return page2file.size(); +} + +int +DjVmDir::get_file_pos(const File * f) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + int cnt; + GPosition pos; + for(pos=files_list, cnt=0;pos&&(files_list[pos]!=f);++pos, cnt++) + continue; + return (pos)?cnt:(-1); +} + +int +DjVmDir::get_page_pos(int page_num) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GP<File> file=page_to_file(page_num); + return (file)?get_file_pos(file):(-1); +} + +GP<DjVmDir::File> +DjVmDir::get_shared_anno_file(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GP<File> file; + for(GPosition pos=files_list;pos;++pos) + { + GP<File> frec=files_list[pos]; + if (frec->is_shared_anno()) + { + file=frec; + break; + } + } + return file; +} + +int +DjVmDir::insert_file(const GP<File> & file, int pos_num) +{ + DEBUG_MSG("DjVmDir::insert_file(): name='" << file->name << "', pos=" << pos_num << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + if (pos_num<0) + pos_num=files_list.size(); + + // Modify maps +// if (! File::is_legal_id(file->id)) +// G_THROW( ERR_MSG("DjVmDir.bad_file") "\t" + file->id); + if (id2file.contains(file->id)) + G_THROW( ERR_MSG("DjVmDir.dupl_id2") "\t" + file->id); + if (name2file.contains(file->name)) + G_THROW( ERR_MSG("DjVmDir.dupl_name2") "\t" + file->name); + name2file[file->name]=file; + id2file[file->id]=file; + if (file->title.length()) + { + if (title2file.contains(file->title)) // duplicate titles may become ok some day + G_THROW( ERR_MSG("DjVmDir.dupl_title2") "\t" + file->title); + title2file[file->title]=file; + } + + // Make sure that there is no more than one file with shared annotations + if (file->is_shared_anno()) + { + for(GPosition pos=files_list;pos;++pos) + if (files_list[pos]->is_shared_anno()) + G_THROW( ERR_MSG("DjVmDir.multi_save2") ); + } + + // Add the file to the list + int cnt; + GPosition pos; + for(pos=files_list, cnt=0;pos&&(cnt!=pos_num);++pos, cnt++) + continue; + if (pos) + files_list.insert_before(pos, file); + else + files_list.append(file); + + if (file->is_page()) + { + // This file is also a page + // Count its number + int page_num=0; + for(pos=files_list;pos;++pos) + { + GP<File> &f=files_list[pos]; + if (f==file) + break; + if (f->is_page()) + page_num++; + } + + int i; + page2file.resize(page2file.size()); + for(i=page2file.size()-1;i>page_num;i--) + page2file[i]=page2file[i-1]; + page2file[page_num]=file; + for(i=page_num;i<page2file.size();i++) + page2file[i]->page_num=i; + } + return pos_num; +} + +void +DjVmDir::delete_file(const GUTF8String &id) +{ + DEBUG_MSG("Deleting file with id='" << (const char *)id << "'\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + for(GPosition pos=files_list;pos;++pos) + { + GP<File> & f=files_list[pos]; + if (id == f->id) + { + name2file.del(f->name); + id2file.del(f->id); + title2file.del(f->title); + if (f->is_page()) + { + for(int page=0;page<page2file.size();page++) + { + if (page2file[page]==f) + { + int i; + for(i=page;i<page2file.size()-1;i++) + page2file[i]=page2file[i+1]; + page2file.resize(page2file.size()-2); + for(i=page;i<page2file.size();i++) + page2file[i]->page_num=i; + break; + } + } + } + files_list.del(pos); + break; + } + } +} + +void +DjVmDir::set_file_name(const GUTF8String &id, const GUTF8String &name) +{ + DEBUG_MSG("DjVmDir::set_file_name(): id='" << id << "', name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + + // First see, if the name is unique + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->id!=id && file->name==name) + G_THROW( ERR_MSG("DjVmDir.name_in_use") "\t" + GUTF8String(name)); + } + + // Check if ID is valid + if (!id2file.contains(id, pos)) + G_THROW( ERR_MSG("DjVmDir.no_info") "\t" + GUTF8String(id)); + GP<File> file=id2file[pos]; + name2file.del(file->name); + file->name=name; + name2file[name]=file; +} + +void +DjVmDir::set_file_title(const GUTF8String &id, const GUTF8String &title) +{ + DEBUG_MSG("DjVmDir::set_file_title(): id='" << id << "', title='" << title << "'\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + + // First see, if the title is unique + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->id!=id && file->title==title) + G_THROW( ERR_MSG("DjVmDir.title_in_use") "\t" + GUTF8String(title)); + } + + // Check if ID is valid + if (!id2file.contains(id, pos)) + G_THROW( ERR_MSG("DjVmDir.no_info") "\t" + GUTF8String(id)); + GP<File> file=id2file[pos]; + title2file.del(file->title); + file->title=title; + title2file[title]=file; +} + +GPList<DjVmDir::File> +DjVmDir::resolve_duplicates(const bool save_as_bundled) +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + // Make sure all the filenames are unique. + GPosition pos; + GMap<GUTF8String,void *> save_map; + GMap<GUTF8String,GPList<DjVmDir::File> > conflicts; + for(pos=files_list;pos;++pos) + { + const GUTF8String save_name=files_list[pos]->check_save_name(save_as_bundled).downcase(); + if(save_map.contains(save_name)) + { + conflicts[save_name].append(files_list[pos]); + }else + { + save_map[save_name]=0; + } + } + for(pos=conflicts;pos;++pos) + { + const GUTF8String &save_name=conflicts.key(pos); + const int dot=save_name.rsearch('.',0); + GPList<DjVmDir::File> &cfiles=conflicts[pos]; + int count=1; + for(GPosition qpos=cfiles;qpos;++qpos) + { + GUTF8String new_name=cfiles[qpos]->get_load_name(); + if((new_name != GUTF8String(GNativeString(new_name))) + ||conflicts.contains(new_name)) + { + do + { + new_name=(dot<0) + ?(save_name+"-"+GUTF8String(count++)) + :(save_name.substr(0,dot)+"-"+GUTF8String(count++)+ + save_name.substr(dot,(unsigned int)(-1))); + } while(save_map.contains(new_name.downcase())); + } + cfiles[qpos]->set_save_name(new_name); + save_map[new_name]=0; + } + } + return files_list; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir.h b/kviewshell/plugins/djvu/libdjvu/DjVmDir.h new file mode 100644 index 00000000..86b661e3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir.h @@ -0,0 +1,451 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir.h,v 1.10 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMDIR_H +#define _DJVMDIR_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVmDir.h + Files #"DjVmDir.h"# and #"DjVmDir.cpp"# implement class \Ref{DjVmDir} for + representing the directory of a DjVu multipage document. + + {\bf Bundled vs. Indirect format} --- There are currently two multipage + DjVu formats supported: {\em bundled} and {\em indirect}. In the first + format all component files composing a given document are packaged (or + bundled) into one file, in the second one every page and component is + stored in a separate file and there is one more file, which contains the + list of all others. + + {\bf Multipage DjVu format} --- Multipage DjVu documents follow the EA + IFF85 format (cf. \Ref{IFFByteStream.h}.) A document is composed of a + #"FORM:DJVM"# whose first chunk is a #"DIRM"# chunk containing the {\em + document directory}. This directory lists all component files composing + the given document, helps to access every component file and identify the + pages of the document. + \begin{itemize} + \item In a {\em bundled} multipage file, the component files + are stored immediately after the #"DIRM"# chunk, + within the #"FORM:DJVU"# composite chunk. + \item In an {\em indirect} multipage file, the component files are + stored in different files whose URLs are composed using information + stored in the #"DIRM"# chunk. + \end{itemize} + Most of the component files represent pages of a document. Some files + however represent data shared by several pages. The pages refer to these + supporting files by means of an inclusion chunk (#"INCL"# chunks) + identifying the supporting file. + + {\bf Document Directory} --- Every directory record describes a component + file. Each component file is identified by a small string named the + identifier (ID). Each component file also contains a file name and a + title. The format of the #"DIRM"# chunk is described in section + \Ref{Format of the DIRM chunk.}. + + Theoretically, IDs are used to uniquely identify each component file in + #"INCL"# chunks, names are used to compose the the URLs of the component + files in an indirect multipage DjVu file, and titles are cosmetic names + possibly displayed when viewing a page of a document. There are however + many problems with this scheme, and we {\em strongly suggest}, with the + current implementation to always make the file ID, the file name and the + file title identical. + + @memo Implements DjVu multipage document directory + @author Andrei Erofeev <eaf@geocities.com> + @version + #$Id: DjVmDir.h,v 1.10 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + + +#include "GString.h" +#include "GThreads.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** Implements DjVu multipage document directory. There are currently two + multipage DjVu formats supported: {\em bundled} and {\em indirect}. In + the first format all component files composing a given document are + packaged (or bundled) into one file, in the second one every page and + component is stored in a separate file and there is one more file, which + contains the list of all others. + + The multipage document directory lists all component files composing the + given document, helps to access every file, identify pages and maintain + user-specified shortcuts. Every directory record describes a file + composing the document. Each file is identified by a small string named + the identifier (ID). Each file may also contain a file name and a title. + + The #DjVmDir# class represents a multipage document directory. Its main + purpose is to encode and decode the document directory when writing or + reading the #DIRM# chunk. Normally you don't have to create this class + yourself. It's done automatically when \Ref{DjVmDoc} class initializes + itself. It may be useful though to be able to access records in the + directory because some classes (like \Ref{DjVuDocument} and \Ref{DjVmDoc}) + return a pointer to #DjVmDir# in some cases. */ + +class DjVmDir : public GPEnabled +{ +protected: + /** Class \Ref{DjVmDir::File} represents the directory records + managed by class \Ref{DjVmDir}. */ + DjVmDir(void) { } ; +public: + class File; + + static const int version; + + /** Class \Ref{DjVmDir::File} represents the directory records + managed by class \Ref{DjVmDir}. */ + static GP<DjVmDir> create(void) {return new DjVmDir; } ; + + /** Decodes the directory from the specified stream. */ + void decode(const GP<ByteStream> &stream); + /** Encodes the directory into the specified stream. */ + void encode(const GP<ByteStream> &stream, const bool do_rename=false) const; + /** Encodes the directory into the specified stream, explicitely as bundled or indirect. */ + void encode(const GP<ByteStream> &stream, const bool bundled, const bool do_rename) const; + /** Tests if directory defines an {\em indirect} document. */ + bool is_indirect(void) const; + /** Tests if the directory defines a {\em bundled} document. */ + bool is_bundled(void) const; + /** Translates page numbers to file records. */ + GP<File> page_to_file(int page_num) const; + /** Translates file names to file records. */ + GP<File> name_to_file(const GUTF8String & name) const; + /** Translates file IDs to file records. */ + GP<File> id_to_file(const GUTF8String &id) const; + /** Translates file shortcuts to file records. */ + GP<File> title_to_file(const GUTF8String &title) const; + /** Returns position of the file in the directory. */ + int get_file_pos(const File * f) const; + /** Returns position of the given page in the directory. */ + int get_page_pos(int page_num) const; + /** Check for duplicate names, and resolve them. */ + GPList<File> resolve_duplicates(const bool save_as_bundled); + /** Returns a copy of the list of file records. */ + GPList<File> get_files_list(void) const; + /** Returns the number of file records. */ + int get_files_num(void) const; + /** Returns the number of file records representing pages. */ + int get_pages_num(void) const; + /** Returns back pointer to the file with #SHARED_ANNO# flag. + Note that there may be only one file with shared annotations + in any multipage DjVu document. */ + GP<File> get_shared_anno_file(void) const; + /** Changes the title of the file with ID #id#. */ + void set_file_title(const GUTF8String &id, const GUTF8String &title); + /** Changes the name of the file with ID #id#. */ + void set_file_name(const GUTF8String &id, const GUTF8String &name); + /** Inserts the specified file record at the specified position. + Specifying #pos# equal to #-1# means to append. The actual position + inserted is returned. */ + int insert_file(const GP<File> & file, int pos=-1); + /** Removes a file record with ID #id#. */ + void delete_file(const GUTF8String &id); +private: + GCriticalSection class_lock; + GPList<File> files_list; + GPArray<File> page2file; + GPMap<GUTF8String, File> name2file; + GPMap<GUTF8String, File> id2file; + GPMap<GUTF8String, File> title2file; +private: //dummy stuff + static void decode(ByteStream *); + static void encode(ByteStream *); +}; + +class DjVmDir::File : public GPEnabled +{ +public: + // Out of the record: INCLUDE below must be zero and PAGE must be one. + // This is to avoid problems with the File constructor, which now takes + // 'int file_type' as the last argument instead of 'bool is_page' + + /** File type. Possible file types are: + \begin{description} + \item[PAGE] This is a top level page file. It may include other + #INCLUDE#d files, which may in turn be shared between + different pages. + \item[INCLUDE] This file is included into some other file inside + this document. + \item[THUMBNAILS] This file contains thumbnails for the document + pages. + \item[SHARED_ANNO] This file contains annotations shared by + all the pages. It's supposed to be included into every page + for the annotations to take effect. There may be only one + file with shared annotations in a document. + \end{description} */ + enum FILE_TYPE { INCLUDE=0, PAGE=1, THUMBNAILS=2, SHARED_ANNO=3 }; +protected: + /** Default constructor. */ + File(void); + +public: + static GP<File> create(void) { return new File(); } + static GP<File> create(const GUTF8String &load_name, + const GUTF8String &save_name, const GUTF8String &title, + const FILE_TYPE file_type); + + /** Check for filenames that are not valid for the native encoding, + and change them. */ + const GUTF8String &check_save_name(const bool as_bundled); + + /** File name. The optional file name must be unique and is the name + that will be used when the document is saved to an indirect file. + If not assigned, the value of #id# will be used for this purpose. + By keeping the name in {\em bundled} document we guarantee, that it + can be expanded later into {\em indirect} document and files will + still have the same names, if the name is legal on a given filesystem. + */ + const GUTF8String &get_save_name(void) const; + + /** File identifier. The encoder assigns a unique identifier to each file + in a multipage document. This is the name used when loading files. + Indirection chunks in other files (#"INCL"# chunks) may refer to another + file using its identifier. */ + const GUTF8String &get_load_name(void) const; + void set_load_name(const GUTF8String &id); + + /** File title. The file title is assigned by the user and may be used as + a shortcut for viewing a particular page. Names like #"chapter1"# or + #"appendix"# are appropriate. */ + const GUTF8String &get_title() const; + void set_title(const GUTF8String &id); + + /** Reports an ascii string indicating file type. */ + GUTF8String get_str_type(void) const; + + /** Offset of the file data in a bundled DJVM file. This number is + relevant in the {\em bundled} case only when everything is packed into + one single file. */ + int offset; + + /** Size of the file data in a bundled DJVM file. This number is + relevant in the {\em bundled} case only when everything is + packed into one single file. */ + int size; + + /** Have we checked the saved file name, to see if it is valid on the + local disk? */ + bool valid_name; + + /** Tests if this file represents a page of the document. */ + bool is_page(void) const + { + return (flags & TYPE_MASK)==PAGE; + } + + /** Returns #TRUE# if this file is included into some other files of + this document. */ + bool is_include(void) const + { + return (flags & TYPE_MASK)==INCLUDE; + } + + /** Returns #TRUE# if this file contains thumbnails for the document pages. */ + bool is_thumbnails(void) const + { + return (flags & TYPE_MASK)==THUMBNAILS; + } + + /** Returns the page number of this file. This function returns + #-1# if this file does not represent a page of the document. */ + bool is_shared_anno(void) const + { return (flags & TYPE_MASK)==SHARED_ANNO; } + + int get_page_num(void) const + { return page_num; } +protected: + GUTF8String name; + GUTF8String oldname; + GUTF8String id; + GUTF8String title; + void set_save_name(const GUTF8String &name); +private: + friend class DjVmDir; + enum FLAGS_0 { IS_PAGE_0=1, HAS_NAME_0=2, HAS_TITLE_0=4 }; + enum FLAGS_1 { HAS_NAME=0x80, HAS_TITLE=0x40, TYPE_MASK=0x3f }; + unsigned char flags; + int page_num; +}; + +inline const GUTF8String & +DjVmDir::File::get_load_name(void) const +{ return id; } + +inline const GUTF8String & +DjVmDir::File::get_title() const +{ return *(title.length()?&title:&id); } + +inline void +DjVmDir::File::set_title(const GUTF8String &xtitle) { title=xtitle; } + +/** @name Format of the DIRM chunk. + + {\bf Variants} --- There are two versions of the #"DIRM"# chunk format. + The version number is identified by the seven low bits of the first byte + of the chunk. Version {\bf 0} is obsolete and should never be used. This + section describes version {\bf 1}. There are two major multipage DjVu + formats supported: {\em bundled} and {\em indirect}. The #"DIRM"# chunk + indicates which format is used in the most significant bit of the first + byte of the chunk. The document is bundled when this bit is set. + Otherwise the document is indirect. + + {\bf Unencoded data} --- The #"DIRM"# chunk is composed some unencoded + data followed by \Ref{bzz} encoded data. The unencoded data starts with + the version byte and a 16 bit integer representing the number of component + files. All integers are encoded with the most significant byte first. + \begin{verbatim} + BYTE: Flags/Version: 0x<bundled>0000011 + INT16: Number of component files. + \end{verbatim} + When the document is a bundled document (i.e. the flag #bundled# is set), + this header is followed by the offsets of each of the component files within + the #"FORM:DJVM"#. These offsets allow for random component file access. + \begin{verbatim} + INT32: Offset of first component file. + INT32: Offset of second component file. + ... + INT32: Offset of last component file. + \end{verbatim} + + {\bf BZZ encoded data} --- The rest of the chunk is entirely compressed + with the BZZ general purpose compressor. We describe now the data fed + into (or retrieved from) the BZZ codec (cf. \Ref{BSByteStream}.) First + come the sizes and the flags associated with each component file. + \begin{verbatim} + INT24: Size of the first component file. + INT24: Size of the second component file. + ... + INT24: Size of the last component file. + BYTE: Flag byte for the first component file. + BYTE: Flag byte for the second component file. + ... + BYTE: Flag byte for the last component file. + \end{verbatim} + The flag bytes have the following format: + \begin{verbatim} + 0b<hasname><hastitle>000000 for a file included by other files. + 0b<hasname><hastitle>000001 for a file representing a page. + 0b<hasname><hastitle>000010 for a file containing thumbnails. + \end{verbatim} + Flag #hasname# is set when the name of the file is different from the file + ID. Flag #hastitle# is set when the title of the file is different from + the file ID. These flags are used to avoid encoding the same string three + times. Then come a sequence of zero terminated strings. There are one to + three such strings per component file. The first string contains the ID + of the component file. The second string contains the name of the + component file. It is only present when the flag #hasname# is set. The third + one contains the title of the component file. It is only present when the + flag #hastitle# is set. The \Ref{bzz} encoding system makes sure that + all these strings will be encoded efficiently despite their possible + redundancies. + \begin{verbatim} + ZSTR: ID of the first component file. + ZSTR: Name of the first component file (only if #hasname# is set.) + ZSTR: Title of the first component file (only if #hastitle# is set.) + ... + ZSTR: ID of the last component file. + ZSTR: Name of the last component file (only if #hasname# is set.) + ZSTR: Title of the last component file (only if #hastitle# is set.) + \end{verbatim} + + @memo Description of the format of the DIRM chunk. */ +//@} + + + +// -------------- IMPLEMENTATION + + +inline bool +DjVmDir::is_bundled(void) const +{ + return ! is_indirect(); +} + +inline bool +DjVmDir::is_indirect(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return ( files_list.size() && files_list[files_list] != 0 && + files_list[files_list]->offset==0 ); +} + + + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir0.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.cpp new file mode 100644 index 00000000..62694098 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.cpp @@ -0,0 +1,169 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir0.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVmDir0.h" +#include "ByteStream.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +int +DjVmDir0::get_size(void) const + // WARNING! make sure, that get_size(), encode() and decode() are in sync +{ + int size=0; + + size+=2; // number of files + for(int i=0;i<num2file.size();i++) + { + FileRec & file=*num2file[i]; + size+=file.name.length()+1; // file name + size+=1; // is IFF file + size+=4; // file offset + size+=4; // file size + }; + + return size; +} + +#ifndef NEED_DECODER_ONLY +void +DjVmDir0::encode(ByteStream & bs) + // WARNING! make sure, that get_size(), encode() and decode() are in sync +{ + bs.write16(num2file.size()); + for(int i=0;i<num2file.size();i++) + { + FileRec & file=*num2file[i]; + bs.writestring(file.name); + bs.write8(0); + bs.write8(file.iff_file); + bs.write32(file.offset); + bs.write32(file.size); + } +} +#endif + +void +DjVmDir0::decode(ByteStream & bs) + // WARNING! make sure, that get_size(), encode() and decode() are in sync +{ + name2file.empty(); + num2file.empty(); + + for(int i=bs.read16();i>0;i--) + { + GUTF8String name; + char ch; + while(bs.read(&ch, 1) && ch) name+=ch; + bool iff_file=bs.read8()?true:false; + int offset=bs.read32(); + int size=bs.read32(); + add_file(name, iff_file, offset, size); + }; +} + +GP<DjVmDir0::FileRec> +DjVmDir0::get_file(const GUTF8String &name) +{ + if (name2file.contains(name)) + return name2file[name]; + return 0; +} + +GP<DjVmDir0::FileRec> +DjVmDir0::get_file(int file_num) +{ + if (file_num<num2file.size()) return num2file[file_num]; + return 0; +} + +void +DjVmDir0::add_file( + const GUTF8String &name, bool iff_file, int offset, int size) +{ + DEBUG_MSG("DjVmDir0::add_file(): name='" << name << "', iff=" << iff_file << + ", offset=" << offset << "\n"); + + if (name.search('/') >= 0) + G_THROW( ERR_MSG("DjVmDir0.no_slash") ); + + GP<FileRec> file=new FileRec(name, iff_file, offset, size); + name2file[name]=file; + num2file.resize(num2file.size()); + num2file[num2file.size()-1]=file; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir0.h b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.h new file mode 100644 index 00000000..c17c795e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.h @@ -0,0 +1,217 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir0.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMDIR0_H +#define _DJVMDIR0_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVmDir0.h + + Files #"DjVmDir0.h"# and #"DjVmDir0.cpp"# contain implementation of + \Ref{DjVmDir0} class responsible for reading and writing the directory + of a multipage all-in-one-file DjVu document (usually referred to as + {\bf DjVm} document. + + This is {\bf not} a navigation directory, which lists all the pages + in a multipage document. The navigation directory is supported by class + \Ref{DjVuNavDir}. This is the directory of a DjVm archive. + + + @memo Directory of DjVu all-in-one-file DjVu documents. + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DjVmDir0.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# */ + +//@{ + +/** Directory of all-in-one-file DjVu documents (also known as DjVm documents). + This class can read and write the directory (table of contents, in other + words) of a DjVm document. This table of contents lists all {\bf files} + included into the document, not {\bf pages} like \Ref{DjVuNavDir} does. + It is normally stored in the document inside {\bf DIR0} chunk where + {\bf "0"} refers to the version number. + + An object of this class can be created either as a result of the decoding + of an existing DjVm file, or manually by calling the default constructor + and adding later directory entries by means of \Ref{add_file}() function. + + You normally will not want to create or decode this directory manually. + \Ref{DjVmFile} class will do it for you. */ +class DjVmDir0 : public GPEnabled +{ +public: + /** Describes a file record inside a DjVm document (archive) */ + class FileRec; +private: + GMap<GUTF8String, GP<FileRec> > name2file; + GPArray<FileRec> num2file; +protected: + /// Default constructor + DjVmDir0(void) {}; +public: + /// Copy constructor + DjVmDir0(const DjVmDir0 & d); + + static GP<DjVmDir0> create(void) {return new DjVmDir0;} + + virtual ~DjVmDir0(void) {}; + + /// Returns the number of files in the DjVm archive + int get_files_num(void) const; + + /// Returns the file record with name #name# + GP<FileRec> get_file(const GUTF8String &name); + + /// Returns the file record number #file_num# + GP<FileRec> get_file(int file_num); + + /** Creates a new file record with name #name# at offset + #offset# and size #size#, which is in IFF format if + #iff_file# is #TRUE#. */ + void add_file(const GUTF8String &name, bool iff_file, + int offset=-1, int size=-1); + + /// Returns the size of the directory if it were encoded in #DIR0# chunk + int get_size(void) const; + + /** Encodes the directory in #DIR0# chunk into the specified + \Ref{ByteStream} */ + void encode(ByteStream & bs); + + /** Decodes the directory from #DIR0# chunk from the specified + \Ref{ByteStream} */ + void decode(ByteStream & bs); + +}; + + /** Describes a file record inside a DjVm document (archive) */ +class DjVmDir0::FileRec : public GPEnabled +{ +public: + /// Name of the file. + GUTF8String name; + /// 1 if the file is in IFF format. + bool iff_file; + /// Offset of the file in the archive. + int offset; + /// Size of the file + int size; + + friend int operator==(const FileRec & f1, const FileRec & f2); + + /// Constructs the #FileRec# object + FileRec(const GUTF8String &name, bool iff_file, + int offset=-1, int size=-1); + /// Default constructor + FileRec(void); + virtual ~FileRec(void); +}; + +inline +DjVmDir0::FileRec::FileRec( + const GUTF8String &name_in, bool iff_file_in, int offset_in, int size_in) +: name(name_in), iff_file(iff_file_in), offset(offset_in), size(size_in) +{ +} + +inline +DjVmDir0::FileRec::FileRec(void) : iff_file(0), offset(-1), size(-1) +{ +} + +inline +DjVmDir0::FileRec::~FileRec(void) +{ +} + +inline int +DjVmDir0::get_files_num(void) const +{ + return num2file.size(); +} + +inline +DjVmDir0::DjVmDir0(const DjVmDir0 & d) : + name2file(d.name2file), num2file(d.num2file) +{ +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDoc.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.cpp new file mode 100644 index 00000000..5b851d6e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.cpp @@ -0,0 +1,663 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDoc.cpp,v 1.10 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVmDoc.h" +#include "DjVmNav.h" +#include "DataPool.h" +#include "IFFByteStream.h" +#include "GOS.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +static const char octets[4]={0x41,0x54,0x26,0x54}; + +// Save the file to disk, remapping INCL chunks while saving. +static void +save_file( + IFFByteStream &iff_in, IFFByteStream &iff_out, const DjVmDir &dir, + GMap<GUTF8String,GUTF8String> &incl) +{ + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid,true); + if(!chkid.cmp("FORM:",5)) + { + for(;iff_in.get_chunk(chkid);iff_in.close_chunk()) + { + iff_out.put_chunk(chkid); + if(chkid == "INCL") + { + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + { + incl_str.setat(incl_str.length()-1, 0); + } + GPosition pos=incl.contains(incl_str); + if(pos) + { + iff_out.get_bytestream()->writestring(incl[pos]); + }else + { + GP<DjVmDir::File> incl_file=dir.id_to_file(incl_str); + if(incl_file) + { + DEBUG_MSG("INCL '"<<(const char *)incl_file->get_save_name()<<"'\n"); + const GUTF8String incl_name=incl_file->get_save_name(); + incl[incl_str]=incl_name; + iff_out.get_bytestream()->writestring(incl_name); + }else + { + DEBUG_MSG("BOGUS INCL '"<<(const char *)incl_str<<"'\n"); + iff_out.copy(*iff_in.get_bytestream()); + } + } + }else + { + iff_out.copy(*iff_in.get_bytestream()); + } + iff_out.close_chunk(); + } + }else + { + iff_out.copy(*iff_in.get_bytestream()); + } + iff_out.close_chunk(); + iff_in.close_chunk(); + } +} + +DjVmDoc::DjVmDoc(void) +{ + DEBUG_MSG("DjVmDoc::DjVmDoc(): Constructing empty DjVm document.\n"); + DEBUG_MAKE_INDENT(3); +} + +void +DjVmDoc::init(void) +{ + dir=DjVmDir::create(); +} + +GP<DjVmDoc> +DjVmDoc::create(void) +{ + DjVmDoc *doc=new DjVmDoc(); + GP<DjVmDoc> retval=doc; + doc->init(); + return retval; +} + +void +DjVmDoc::insert_file(const GP<DjVmDir::File> & f, + GP<DataPool> data_pool, int pos) +{ + DEBUG_MSG("DjVmDoc::insert_file(): inserting file '" << f->get_load_name() << + "' at pos " << pos << "\n"); + DEBUG_MAKE_INDENT(3); + + if (!f) + G_THROW( ERR_MSG("DjVmDoc.no_zero_file") ); + if (data.contains(f->get_load_name())) + G_THROW( ERR_MSG("DjVmDoc.no_duplicate") ); + + char buffer[4]; + if (data_pool->get_data(buffer, 0, 4)==4 && !memcmp(buffer, octets, 4)) + { + data_pool=DataPool::create(data_pool, 4, -1); + } + data[f->get_load_name()]=data_pool; + dir->insert_file(f, pos); +} + +void +DjVmDoc::insert_file( + ByteStream &data, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, const GUTF8String &title, + int pos) +{ + const GP<DjVmDir::File> file( + DjVmDir::File::create(name, id, title, file_type)); + const GP<DataPool> pool(DataPool::create()); + // Cannot connect to a bytestream. + // Must copy data into the datapool. + int nbytes; + char buffer[1024]; + while ((nbytes = data.read(buffer, sizeof(buffer)))) + pool->add_data(buffer, nbytes); + pool->set_eof(); + // Call low level insert + insert_file(file, pool, pos); +} + +void +DjVmDoc::insert_file( + const GP<DataPool> &pool, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, const GUTF8String &title, + int pos) +{ + const GP<DjVmDir::File> file( + DjVmDir::File::create(name, id, title, file_type)); + // Call low level insert + insert_file(file, pool, pos); +} + +void +DjVmDoc::delete_file(const GUTF8String &id) +{ + DEBUG_MSG("DjVmDoc::delete_file(): deleting file '" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!data.contains(id)) + G_THROW(GUTF8String( ERR_MSG("DjVmDoc.cant_delete") "\t") + id); + + data.del(id); + dir->delete_file(id); +} + +void +DjVmDoc::set_djvm_nav(GP<DjVmNav> n) +{ + if (n && ! n->isValidBookmark()) + G_THROW("Invalid bookmark data"); + nav = n; +} + +GP<DataPool> +DjVmDoc::get_data(const GUTF8String &id) const +{ + GPosition pos; + if (!data.contains(id, pos)) + G_THROW(GUTF8String( ERR_MSG("DjVmDoc.cant_find") "\t") + id); + const GP<DataPool> pool(data[pos]); + // First check that the file is in IFF format + G_TRY + { + const GP<ByteStream> str_in(pool->get_stream()); + const GP<IFFByteStream> giff_in=IFFByteStream::create(str_in); + IFFByteStream &iff_in=*giff_in; + GUTF8String chkid; + int size=iff_in.get_chunk(chkid); + if (size<0 || size>0x7fffffff) + G_THROW( ERR_MSG("DjVmDoc.not_IFF") "\t" + id); + } + G_CATCH_ALL + { + G_THROW( ERR_MSG("DjVmDoc.not_IFF") "\t" + id); + } + G_ENDCATCH; + return pool; +} + +void +DjVmDoc::write(const GP<ByteStream> &gstr) +{ + const GMap<GUTF8String,void *> reserved; + write(gstr,reserved); +} + +static inline GUTF8String +get_name(const DjVmDir::File &file) +{ + const GUTF8String save_name(file.get_save_name()); + return save_name.length()?save_name:(file.get_load_name()); +} + +void +DjVmDoc::write(const GP<ByteStream> &gstr, + const GMap<GUTF8String,void *> &reserved) +{ + DEBUG_MSG("DjVmDoc::write(): Storing document into the byte stream.\n"); + DEBUG_MAKE_INDENT(3); + + GPList<DjVmDir::File> files_list=dir->resolve_duplicates(true); + bool do_rename=false; + GPosition pos(reserved); + + GMap<GUTF8String,GUTF8String> incl; + DEBUG_MSG("pass 1: looking for reserved names."); + if(pos) + { + // Check if there are any conflicting file names. + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + if((do_rename=(reserved.contains(file->get_load_name())?true:false)) + ||(do_rename=(reserved.contains(file->get_save_name())?true:false))) + { + break; + } + } + // If there are conflicting file names, check if the save names + // are OK. If not, generate new save names. + if(do_rename) + { + DEBUG_MSG("pass 1: renaming reserved names."); + for(;;files_list=dir->resolve_duplicates(true)) + { + GMap<GUTF8String,void *> this_doc; + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + this_doc[::get_name(*file)]=0; + } + bool need_new_list=false; + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + const GUTF8String name(::get_name(*file)); + if(reserved.contains(name)) + { + GUTF8String new_name; + int series=0; + do + { + int dot=name.rsearch('.'); + if(dot>0) + { + new_name=name.substr(0,dot)+ + "_"+GUTF8String(++series)+name.substr(dot,-1); + }else + { + new_name=name+"_"+GUTF8String(++series); + } + } while(reserved.contains(new_name)||this_doc.contains(new_name)); + dir->set_file_name(file->get_load_name(),new_name); + need_new_list=true; + } + } + if(!need_new_list) + break; + } + } + } + + DEBUG_MSG("pass 2: create dummy DIRM chunk and calculate offsets...\n"); + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + file->offset=0xffffffff; + GPosition data_pos=data.contains(file->get_load_name()); + if (!data_pos) + G_THROW( ERR_MSG("DjVmDoc.no_data") "\t" + file->get_load_name()); + if(do_rename) + { + GP<ByteStream> gout(ByteStream::create()); + { + const GP<IFFByteStream> giff_in( + IFFByteStream::create(data[data_pos]->get_stream())); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gout)); + ::save_file(*giff_in,*giff_out,*dir,incl); + } + gout->seek(0L); + data[data_pos]=DataPool::create(gout); + } + file->size=data[data_pos]->get_length(); + if (!file->size) + G_THROW( ERR_MSG("DjVmDoc.zero_file") ); + } + + const GP<ByteStream> tmp_str(ByteStream::create()); + const GP<IFFByteStream> gtmp_iff(IFFByteStream::create(tmp_str)); + IFFByteStream &tmp_iff=*gtmp_iff; + tmp_iff.put_chunk("FORM:DJVM", 1); + tmp_iff.put_chunk("DIRM"); + dir->encode(tmp_iff.get_bytestream(),do_rename); + tmp_iff.close_chunk(); + if (nav) + { + tmp_iff.put_chunk("NAVM"); + nav->encode(tmp_iff.get_bytestream()); + tmp_iff.close_chunk(); + } + tmp_iff.close_chunk(); + int offset=tmp_iff.tell(); + + for(pos=files_list;pos;++pos) + { + if ((offset & 1)!=0) + offset++; + + GP<DjVmDir::File> & file=files_list[pos]; + file->offset=offset; + offset+=file->size; // file->size has been set in the first pass + } + + DEBUG_MSG("pass 3: store the file contents.\n"); + + GP<IFFByteStream> giff=IFFByteStream::create(gstr); + IFFByteStream &iff=*giff; + iff.put_chunk("FORM:DJVM", 1); + iff.put_chunk("DIRM"); + dir->encode(iff.get_bytestream(),do_rename); + iff.close_chunk(); + if (nav) + { + iff.put_chunk("NAVM"); + nav->encode(iff.get_bytestream()); + iff.close_chunk(); + } + + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> & file=files_list[pos]; + + const GP<DataPool> pool=get_data(file->get_load_name()); + const GP<ByteStream> str_in(pool->get_stream()); + if ((iff.tell() & 1)!=0) + { + iff.get_bytestream()->write8(0); + } + iff.copy(*str_in); + } + + iff.close_chunk(); + iff.flush(); + + DEBUG_MSG("done storing DjVm file.\n"); +} + +void +DjVmDoc::read(const GP<DataPool> & pool) +{ + DEBUG_MSG("DjVmDoc::read(): reading the BUNDLED doc contents from the pool\n"); + DEBUG_MAKE_INDENT(3); + + const GP<ByteStream> str(pool->get_stream()); + + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid!="FORM:DJVM") + G_THROW( ERR_MSG("DjVmDoc.no_form_djvm") ); + + iff.get_chunk(chkid); + if (chkid!="DIRM") + G_THROW( ERR_MSG("DjVmDoc.no_dirm_chunk") ); + dir->decode(iff.get_bytestream()); + iff.close_chunk(); + + data.empty(); + + if (dir->is_indirect()) + G_THROW( ERR_MSG("DjVmDoc.cant_read_indr") ); + + GPList<DjVmDir::File> files_list=dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + DjVmDir::File * f=files_list[pos]; + + DEBUG_MSG("reading contents of file '" << f->get_load_name() << "'\n"); + data[f->get_load_name()]=DataPool::create(pool, f->offset, f->size); + } +} + +void +DjVmDoc::read(ByteStream & str_in) +{ + DEBUG_MSG("DjVmDoc::read(): reading the BUNDLED doc contents from the stream\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool> pool=DataPool::create(); + char buffer[1024]; + int length; + while((length=str_in.read(buffer, 1024))) + pool->add_data(buffer, length); + pool->set_eof(); + + read(pool); +} + +void +DjVmDoc::read(const GURL &url) +{ + DEBUG_MSG("DjVmDoc::read(): reading the doc contents from the HDD\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool> pool=DataPool::create(url); + const GP<ByteStream> str(pool->get_stream()); + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid!="FORM:DJVM") + G_THROW( ERR_MSG("DjVmDoc.no_form_djvm2") ); + + iff.get_chunk(chkid); + if (chkid!="DIRM") + G_THROW( ERR_MSG("DjVmDoc.no_dirm_chunk") ); + dir->decode(iff.get_bytestream()); + iff.close_chunk(); + + if (dir->is_bundled()) + read(pool); + else + { +// GUTF8String full_name=GOS::expand_name(name); +// GUTF8String dir_name=GOS::dirname(GOS::url_to_filename(url.base())); + GURL dirbase=url.base(); + + data.empty(); + + GPList<DjVmDir::File> files_list=dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + DjVmDir::File * f=files_list[pos]; + + DEBUG_MSG("reading contents of file '" << f->get_load_name() << "'\n"); + + const GURL::UTF8 url(f->get_load_name(),dirbase); + data[f->get_load_name()]=DataPool::create(url); + } + } +} + +void +DjVmDoc::write_index(const GP<ByteStream> &str) +{ + DEBUG_MSG("DjVmDoc::write_index(): Storing DjVm index file\n"); + DEBUG_MAKE_INDENT(3); + + GPList<DjVmDir::File> files_list=dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + file->offset=0; + + GPosition data_pos=data.contains(file->get_load_name()); + if (!data_pos) + G_THROW( ERR_MSG("DjVmDoc.no_data") "\t" + file->get_load_name()); + file->size=data[data_pos]->get_length(); + if (!file->size) + G_THROW( ERR_MSG("DjVmDoc.zero_file") ); + } + + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + iff.put_chunk("FORM:DJVM", 1); + iff.put_chunk("DIRM"); + dir->encode(iff.get_bytestream()); + iff.close_chunk(); + if (nav) + { + iff.put_chunk("NAVM"); + nav->encode(iff.get_bytestream()); + iff.close_chunk(); + } + iff.close_chunk(); + iff.flush(); +} + +void +DjVmDoc::save_page( + const GURL &codebase, const DjVmDir::File &file) const +{ + GMap<GUTF8String,GUTF8String> incl; + save_file(codebase,file,&incl); +} + +void +DjVmDoc::save_page( + const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl ) const +{ + save_file(codebase,file,&incl); +} + +void +DjVmDoc::save_file( + const GURL &codebase, const DjVmDir::File &file) const +{ + save_file(codebase,file,0); +} + +GUTF8String +DjVmDoc::save_file(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl, const GP<DataPool> &pool) const +{ + const GUTF8String save_name(file.get_save_name()); + const GURL::UTF8 new_url(save_name,codebase); + DEBUG_MSG("storing file '"<<new_url<<"'\n"); + DataPool::load_file(new_url); + const GP<ByteStream> str_in(pool->get_stream()); + const GP<ByteStream> str_out(ByteStream::create(new_url, "wb")); + ::save_file( *IFFByteStream::create(str_in), + *IFFByteStream::create(str_out), *dir, incl); + return save_name; +} + +void +DjVmDoc::save_file( + const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> *incl) const +{ + const GUTF8String load_name=file.get_load_name(); + if(!incl || !incl->contains(load_name)) + { + GMap<GUTF8String,GUTF8String> new_incl; + const GUTF8String save_name( + save_file(codebase,file,new_incl,get_data(load_name))); + + if(incl) + { + (*incl)[load_name]=save_name; + for(GPosition pos=new_incl;pos;++pos) + { + save_file(codebase,file,incl); + } + } + } +} + +void +DjVmDoc::expand(const GURL &codebase, const GUTF8String &idx_name) +{ + DEBUG_MSG("DjVmDoc::expand(): Expanding into '" << codebase << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Resolve any name conflicts + // Find the list of all files. + GPList<DjVmDir::File> files_list=dir->resolve_duplicates(false); + + // store each file + for(GPosition pos=files_list;pos;++pos) + { + save_file(codebase,*files_list[pos]); + } + + if (idx_name.length()) + { + const GURL::UTF8 idx_url(idx_name, codebase); + + DEBUG_MSG("storing index file '" << idx_url << "'\n"); + + DataPool::load_file(idx_url); + GP<ByteStream> str=ByteStream::create(idx_url, "wb"); + write_index(str); + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDoc.h b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.h new file mode 100644 index 00000000..637d0b78 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.h @@ -0,0 +1,274 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDoc.h,v 1.10 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMDOC_H +#define _DJVMDOC_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVmDir.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class DataPool; +class GURL; +class GUTF8String; +class DjVmNav; + +/** @name DjVmDoc.h + Files #"DjVmDoc.h"# and #"DjVmDoc.cpp"# contain implementation of the + \Ref{DjVmDoc} class used to read and write new DjVu multipage documents. + + @memo DjVu multipage documents reader/writer. + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DjVmDoc.h,v 1.10 2005/05/25 20:24:52 leonb Exp $# +*/ + +//@{ + +/** Read/Write DjVu multipage documents. + + The "new" DjVu multipage documents can be of two types: {\em bundled} and + {\em indirect}. In the first case all pages are packed into one file, + which is very like an archive internally. In the second case every page + is stored in a separate file. Plus there can be other components, + included into one or more pages, which also go into separate files. In + addition to pages and components, in the case of the {\em indirect} format + there is one more top-level file with the document directory (see + \Ref{DjVmDir}), which is basically an index file containing the + list of all files composing the document. + + This class can read documents of both formats and can save them under any + format. It is therefore ideal for converting between {\em bundled} and + {\em indirect} formats. It cannot be used however for reading obsolete + formats. The best way to convert obsolete formats consists in reading + them with class \Ref{DjVuDocument} class and saving them using + \Ref{DjVuDocument::write} or \Ref{DjVuDocument::expand}. + + This class can also be used to create and modify multipage documents at + the low level without decoding every page or component (See + \Ref{insert_file}() and \Ref{delete_file}()). +*/ + +class DjVmDoc : public GPEnabled +{ + // Internal function. +protected: + DjVmDoc(void); + void init(void); +public: + /// Creator + static GP<DjVmDoc> create(void); + /** Inserts a file into the document. + @param data ByteStream containing the file data. + @param file_type Describes the type of the file to be inserted. + See \Ref{DjVmDir::File} for details. + @param name Name of the file in the document (e.g. an URL). + @param id Identifier of the file (as used in INCL chunks). + @param title Optional title of the file (shown in browsers). + @param pos Position of the file in the document (default is append). + */ + void insert_file( + ByteStream &data, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, + const GUTF8String &title=GUTF8String(), int pos=-1 ); + /** Inserts a file into the document. + @param pool Data pool containing file data. + @param file_type Describes the type of the file to be inserted. + See \Ref{DjVmDir::File} for details. + @param name Name of the file in the document (e.g. an URL). + @param id Identifier of the file (as used in INCL chunks). + @param title Optional title of the file (shown in browsers). + @param pos Position of the file in the document (default is append). + */ + void insert_file( + const GP<DataPool> &pool, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, + const GUTF8String &title=GUTF8String(), int pos=-1 ); + + /** Inserts a file described by \Ref{DjVmDir::File} structure with + data #data# at position #pos#. If #pos# is negative, the file + will be appended to the document. Otherwise it will be inserted + at position #pos#. */ + void insert_file(const GP<DjVmDir::File> & f, + GP<DataPool> data, int pos=-1); + + /** Removes file with the specified #id# from the document. Every + file inside a new DjVu multipage document has its unique ID + (refer to \Ref{DjVmDir} for details), which is passed to this + function. */ + void delete_file(const GUTF8String &id); + + /** Set the bookmarks */ + void set_djvm_nav(GP<DjVmNav> n); + + /** Returns the directory of the DjVm document (the one which will + be encoded into #DJVM# chunk of the top-level file or the bundle). */ + GP<DjVmDir> get_djvm_dir(void); + + /** Returns contents of file with ID #id# from the document. + Please refer to \Ref{DjVmDir} for the explanation of what + IDs mean. */ + GP<DataPool> get_data(const GUTF8String &id) const; + + /** Reading routines */ + //@{ + /** Reads contents of a {\em bundled} multipage DjVu document from + the stream. */ + void read(ByteStream & str); + /** Reads contents of a {\em bundled} multipage DjVu document from + the \Ref{DataPool}. */ + void read(const GP<DataPool> & data_pool); + /** Reads the DjVu multipage document in either {\em bundled} or + {\em indirect} format. + + {\bf Note:} For {\em bundled} documents the file is not + read into memory. We just open it and access data directly there. + Thus you should not modify the file contents. + + @param name For {\em bundled} documents this is the name + of the document. For {\em indirect} documents this is + the name of the top-level file of the document (containing + the \Ref{DjVmDir} with the list of all files). + The rest of the files are expected to be in the + same directory and will be read by this function as well. */ + void read(const GURL &url); + //@} + + /** Writing routines */ + //@{ + /** Writes the multipage DjVu document in the {\em bundled} format into + the stream. */ + void write(const GP<ByteStream> &str); + /** Writes the multipage DjVu document in the {\em bundled} format into + the stream, reserving any of the specified names. */ + void write(const GP<ByteStream> &str, + const GMap<GUTF8String,void *>& reserved); + /** Stored index (top-level) file of the DjVu document in the {\em + indirect} format into the specified stream. */ + void write_index(const GP<ByteStream> &str); + /** Writes the multipage DjVu document in the {\em indirect} format + into the given directory. Every page and included file will be + stored as a separate file. Besides, one top-level file with + the document directory (named #idx_name#) will be created unless + #idx_name# is empty. + + @param dir_name Name of the directory where files should be + created + @param idx_name Name of the top-level file with the \Ref{DjVmDir} + with the list of files composing the given document. + If empty, the file will not be created. */ + void expand(const GURL &codebase, const GUTF8String &idx_name); + + /** Writes an individual file, and all included files. + INCL chunks will be remapped as appropriate. */ + void save_page(const GURL &codebase, const DjVmDir::File &file) const; + + /** Writes an individual file if not mapped, and all included files. + INCL chunks will be remapped as appropriate. All pages saved + are added to the #incl# map. */ + void save_page(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl) const; + + /** Writes an individual file specified, remapping INCL chunks as + appropriate. Included files will not be saved. */ + void save_file(const GURL &codebase, const DjVmDir::File &file) const; + + /** Writes the specified file from the given #pool#. */ + GUTF8String save_file(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl, + const GP<DataPool> &pool) const; + //@} +private: + void save_file(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> *incl) const; + GP<DjVmDir> dir; + GP<DjVmNav> nav; + GPMap<GUTF8String, DataPool > data; +private: // dummy stuff + static void write(ByteStream *); + static void write_index(ByteStream *); +}; + +inline GP<DjVmDir> +DjVmDoc::get_djvm_dir(void) +{ + return dir; +} + + +//@} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmNav.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmNav.cpp new file mode 100644 index 00000000..9e8b5fd7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmNav.cpp @@ -0,0 +1,338 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmNav.cpp,v 1.1 2005/05/25 17:36:53 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include <ctype.h> + +#include "DjVuDocument.h" +#include "DjVmNav.h" +#include "BSByteStream.h" +#include "GURL.h" +#include "debug.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GP<DjVmNav::DjVuBookMark> +DjVmNav::DjVuBookMark::create(void) +{ + return new DjVuBookMark(); +} + +GP<DjVmNav::DjVuBookMark> +DjVmNav::DjVuBookMark::create(const unsigned short count, + const GUTF8String &displayname, + const GUTF8String &url) +{ + DjVuBookMark *pvm=new DjVuBookMark(); + GP<DjVuBookMark> bookmark=pvm; + pvm->count=count; + pvm->displayname=displayname; + pvm->url=url; + return bookmark; +} + +DjVmNav::DjVuBookMark::DjVuBookMark(void) + : count(0), displayname(), url() +{ +} + +GP<DjVmNav> +DjVmNav::create(void) +{ + return new DjVmNav; +} + +// Decode the input bytestream and populate this object +void +DjVmNav::DjVuBookMark::decode(const GP<ByteStream> &gstr) +{ + int textsize=0, readsize=0; + char *buffer=0; + ByteStream &bs=*gstr; + count = bs.read8(); + displayname.empty(); +#ifdef DJVMNAV_WITH_256LIMIT + textsize = bs.read24(); +#else + int counthi = bs.read8(); + count = (counthi<<8)+ count; + textsize = bs.read16(); +#endif + if (textsize) + { + buffer = displayname.getbuf(textsize); + readsize = bs.read(buffer,textsize); + buffer[readsize] = 0; + } + url.empty(); + textsize = bs.read24(); + if (textsize) + { + buffer = url.getbuf(textsize); + readsize = bs.read(buffer,textsize); + buffer[readsize] = 0; + } +} + +// Serialize this object to the output bytestream +void +DjVmNav::DjVuBookMark::encode(const GP<ByteStream> &gstr) +{ + int textsize=0; + ByteStream &bs=*gstr; +#ifdef DJVMNAV_WITH_256LIMIT + if (count>255) + G_THROW("Excessive number of children in bookmark tree"); + bs.write8(count); + textsize = displayname.length(); + bs.write24( textsize ); +#else + if (count>65535) + G_THROW("Excessive number of children in bookmark tree"); + bs.write8( count & 0xff ); + bs.write8( (count>>8) & 0xff ); + textsize = displayname.length(); + bs.write16( textsize ); +#endif + bs.writestring(displayname); + textsize = url.length(); + bs.write24( textsize ); + bs.writestring(url); +} + +// Text dump of this object to the output bytestream +void +DjVmNav::DjVuBookMark::dump(const GP<ByteStream> &gstr) +{ + int textsize=0; + ByteStream &bs=*gstr; + bs.format("\n count=%d\n",count); + textsize = displayname.length(); + bs.format(" (%d) %s\n",textsize, displayname.getbuf()); + textsize = url.length(); + bs.format(" (%d) %s\n",textsize, url.getbuf()); +} + +// Decode the input bytestream and populate this object +void +DjVmNav::decode(const GP<ByteStream> &gstr) +{ + //ByteStream &str=*gstr; + GP<ByteStream> gpBSByteStream = BSByteStream::create(gstr); + GCriticalSectionLock lock(&class_lock); + bookmark_list.empty(); + int nbookmarks=gpBSByteStream->read16(); + if (nbookmarks) + { + for(int bookmark=0;bookmark<nbookmarks;bookmark++) + { + GP<DjVuBookMark> pBookMark=DjVuBookMark::create(); + pBookMark->decode(gpBSByteStream); + bookmark_list.append(pBookMark); + } + } +} + +// Serialize this object to the output stream +void +DjVmNav::encode(const GP<ByteStream> &gstr) +{ + //ByteStream &str=*gstr; + GP<ByteStream> gpBSByteStream = BSByteStream::create(gstr, 1024); + GCriticalSectionLock lock(&class_lock); + int nbookmarks=bookmark_list.size(); + gpBSByteStream->write16(nbookmarks); + if (nbookmarks) + { + GPosition pos; + int cnt=0; + for (pos = bookmark_list; pos; ++pos) + { + bookmark_list[pos]->encode(gpBSByteStream); + cnt++; + } + if (nbookmarks != cnt) + { + GUTF8String msg; + msg.format("Corrupt bookmarks found during encode: %d of %d \n", + cnt, nbookmarks); + G_THROW (msg); + } + } +} + +int +DjVmNav::getBookMarkCount() +{ + return(bookmark_list.size()); +} + +void +DjVmNav::append (const GP<DjVuBookMark> &gpBookMark) +{ + bookmark_list.append(gpBookMark); +} + +bool +DjVmNav::getBookMark(GP<DjVuBookMark> &gpBookMark, int iPos) +{ + GPosition pos = bookmark_list.nth(iPos); + if (pos) + gpBookMark = bookmark_list[pos]; + else + gpBookMark = 0; + return (gpBookMark?true:false); +} + + +// A text dump of this object +void +DjVmNav::dump(const GP<ByteStream> &gstr) +{ + ByteStream &str=*gstr; + GCriticalSectionLock lock(&class_lock); + int nbookmarks=bookmark_list.size(); + str.format("%d bookmarks:\n",nbookmarks); + if (nbookmarks) + { + GPosition pos; + int cnt=0; + for (pos = bookmark_list; pos; ++pos) + { + bookmark_list[pos]->dump(&str); + cnt++; + } + if (nbookmarks != cnt) + { + GUTF8String msg; + msg.format("Corrupt bookmarks found during encode: %d of %d \n", + cnt,nbookmarks); + G_THROW (msg); + } + } +} + +bool +DjVmNav::isValidBookmark() +{ + //test if the bookmark is properly given + //for example: (4, "A", urla) + // (0, "B", urlb) + // (0, "C", urlc) + //is not a bookmark since A suppose to have 4 decendents, it only get one. + int bookmark_totalnum=getBookMarkCount(); + GP<DjVuBookMark> gpBookMark; + int* count_array=(int*)malloc(sizeof(int)*bookmark_totalnum); + for(int i=0;i<bookmark_totalnum;i++) + { + getBookMark(gpBookMark, i); + count_array[i]=gpBookMark->count; + } + int index=0; + int trees=0; + int* treeSizes=(int*)malloc(sizeof(int)*bookmark_totalnum); + while(index<bookmark_totalnum) + { + int treeSize=get_tree(index,count_array,bookmark_totalnum); + if(treeSize>0) //is a tree + { + index+=treeSize; + treeSizes[trees++]=treeSize; + } + else //not a tree + break; + } + free(count_array); + free(treeSizes); + return true; +} + +int +DjVmNav::get_tree(int index, int* count_array, int count_array_size) +{ + int i=index; + int accumulate_count=0; + while(i<count_array_size) + { + accumulate_count+=count_array[i]; + if(accumulate_count==0) + return 1; + else if(accumulate_count == i-index) //get a tree + return accumulate_count; + i++; + } + return 0; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmNav.h b/kviewshell/plugins/djvu/libdjvu/DjVmNav.h new file mode 100644 index 00000000..46c5d57f --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmNav.h @@ -0,0 +1,146 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmNav.h,v 1.1 2005/05/25 17:36:53 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMNAV_H +#define _DJVMNAV_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include "DjVuGlobal.h" +#include "GString.h" +#include "GThreads.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class ByteStream; + +/** The NAVM chunk. + The optional #"NAVM"# chunk which follows the DIRM chunk describes + how the user can navigate the document. + This is a list of DjVuBookMarks. +**/ + +class DjVmNav : public GPEnabled +{ +public: + /** Class \Ref{DjVmNav::DjVuBookMark} represents a entry in the + hierarchy of contents. */ + class DjVuBookMark; + + static GP<DjVmNav> create(void); + /** Decodes the directory from the specified stream. */ + void decode(const GP<ByteStream> &stream); + /** Encodes the directory into the specified stream. */ + void encode(const GP<ByteStream> &stream) ; + void dump(const GP<ByteStream> &stream) ; + /** Return bookmark at zero-based position i */ + bool getBookMark(GP<DjVuBookMark> &gpBookMark, int i) ; + int getBookMarkCount(); + /** Append the BookMark to the end of the list */ + void append (const GP<DjVuBookMark> &gpBookMark) ; + /** This function will check the given bookmark is valid or not */ + bool isValidBookmark(); + /** This function determines if the given count_array is a tree + sequence, that is if it fits a tree. */ + int get_tree(int index, int* count_array, int count_array_size); +protected: + DjVmNav(void) { } ; +private: + GCriticalSection class_lock; + GPList<DjVuBookMark> bookmark_list; +}; + +/** The DjVuBookMark. + Each entry in the Navigation chunk (NAVM) is a bookmark. A bookmark + contains a count of immediate children, a display string and a url. +**/ + +class DjVmNav::DjVuBookMark : public GPEnabled +{ +protected: + /** Default constructor. */ + DjVuBookMark(void); +public: + static GP<DjVuBookMark> create(void); + static GP<DjVuBookMark> create(const unsigned short count, + const GUTF8String &displayname, + const GUTF8String &url); + void encode(const GP<ByteStream> &stream); + void dump(const GP<ByteStream> &stream); + void decode(const GP<ByteStream> &stream); + int count; // count of immediate children. + GUTF8String displayname; // example: "Section 3.5 - Encryption" + GUTF8String url; // url, may be blank or relative. +}; + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp new file mode 100644 index 00000000..a6772e61 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp @@ -0,0 +1,1514 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuAnno.cpp,v 1.12 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuAnno.h" +#include "GContainer.h" +#include "GException.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "GMapAreas.h" + +#include "debug.h" + +#include <ctype.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// GLParser.h and GLParser.cpp used to be separate files capable to decode +// that weird ANTa chunk format into C++ structures and lists. But since +// its implementation is temporary and is used only in this file (DjVuAnno.cpp) +// it appears reasonable to build it in here. + +//*************************************************************************** +//****************************** GLParser.h ********************************* +//*************************************************************************** + + +class GLObject : public GPEnabled +{ +public: + enum GLObjectType { INVALID=0, NUMBER=1, STRING=2, SYMBOL=3, LIST=4 }; + static const char * const GLObjectString[LIST+1]; + + GLObject(int _number=0); + GLObject(GLObjectType type, const char * str); + GLObject(const char * name, const GPList<GLObject> & list); + virtual ~GLObject(void); + + int get_number(void) const; + GUTF8String get_string(void) const; + GUTF8String get_symbol(void) const; + GPList<GLObject> & get_list(void); + GP<GLObject> operator[](int n) const; + + GLObjectType get_type(void) const; + GUTF8String get_name(void) const; + void print(ByteStream & str, int compact=1, int indent=0, int * cur_pos=0) const; +private: + GLObjectType type; + GUTF8String name; + + int number; + GUTF8String string; + GUTF8String symbol; + GPList<GLObject> list; + void throw_can_not_convert_to(const GLObjectType to) const; +}; + +const char * const GLObject::GLObjectString[]= + {"invalid", "number", "string", "symbol", "list"}; + +inline GLObject::GLObjectType +GLObject::get_type(void) const { return type; } + +inline +GLObject::~GLObject(void) {} + +class GLToken +{ +public: + enum GLTokenType { OPEN_PAR, CLOSE_PAR, OBJECT }; + GLTokenType type; + GP<GLObject> object; + + GLToken(GLTokenType type, const GP<GLObject> & object); +}; + +inline +GLToken::GLToken(GLTokenType xtype, const GP<GLObject> & xobject) : + type(xtype), object(xobject) {} + +class GLParser +{ +public: + void parse(const char * str); + GPList<GLObject> & get_list(void); + GP<GLObject> get_object(const char * name, bool last=true); + void print(ByteStream & str, int compact=1); + + GLParser(void); + GLParser(const char * str); + ~GLParser(void); +private: + GPList<GLObject> list; + + bool compat; + void skip_white_space(const char * & start); + void check_compat(const char *str); + GLToken get_token(const char * & start); + void parse(const char * cur_name, GPList<GLObject> & list, + const char * & start); +}; + +GLParser::GLParser(void) + : compat(false) +{ +} + +GLParser::~GLParser(void) +{ +} + +GPList<GLObject> & +GLParser::get_list(void) +{ + return list; +} + +GLParser::GLParser(const char * str) + : compat(false) +{ + parse(str); +} + + +//*************************************************************************** +//***************************** GLParser.cpp ******************************** +//*************************************************************************** + + +GLObject::GLObject(int xnumber) : type(NUMBER), number(xnumber) {} + +GLObject::GLObject(GLObjectType xtype, const char * str) : type(xtype) +{ + if (type!=STRING && type!=SYMBOL) + G_THROW( ERR_MSG("DjVuAnno.bad_type") ); + if (type==STRING) + string=str; + else symbol=str; +} + +GLObject::GLObject(const char * xname, const GPList<GLObject> & xlist) : + type(LIST), name(xname), list(xlist) {} + +void +GLObject::print(ByteStream & str, int compact, int indent, int * cur_pos) const +{ + int local_cur_pos = 0; + if (!cur_pos) { cur_pos = &local_cur_pos; } + + GUTF8String buffer; + const char * to_print=0; + switch(type) + { + case NUMBER: + to_print=buffer.format("%d",number); + break; + case STRING: + { + int length = string.length(); + const char *data = (const char*)string; + buffer = GUTF8String("\""); + while (*data && length>0) + { + int span = 0; + while (span<length && (unsigned char)(data[span])>=0x20 && + data[span]!=0x7f && data[span]!='"' && data[span]!='\\' ) + span++; + if (span > 0) + { + buffer = buffer + GUTF8String(data, span); + data += span; + length -= span; + } + else + { + char buf[8]; + static char *tr1 = "\"\\tnrbf"; + static char *tr2 = "\"\\\t\n\r\b\f"; + sprintf(buf,"\\%03o", (int)(((unsigned char*)data)[span])); + for (int i=0; tr2[i]; i++) + if (data[span] == tr2[i]) + buf[1] = tr1[i]; + if (buf[1]<'0' || buf[1]>'3') + buf[2] = 0; + buffer = buffer + GUTF8String(buf); + data += 1; + length -= 1; + } + } + buffer = buffer + GUTF8String("\""); + to_print = buffer; + } + break; + case SYMBOL: + to_print=buffer.format("%s",(const char *)symbol); + break; + case LIST: + to_print=buffer.format("(%s",(const char *)name); + break; + case INVALID: + break; + } + if (!compact && *cur_pos+strlen(to_print)>70) + { + char ch='\n'; + str.write(&ch, 1); + ch=' '; + for(int i=0;i<indent;i++) str.write(&ch, 1); + *cur_pos=indent; + } + str.write(to_print, strlen(to_print)); + char ch=' '; + str.write(&ch, 1); + *cur_pos+=strlen(to_print)+1; + if (type==LIST) + { + int indent=*cur_pos-strlen(to_print); + for(GPosition pos=list;pos;++pos) + list[pos]->print(str, compact, indent, cur_pos); + str.write(") ", 2); + *cur_pos+=2; + } +} + +// This function constructs message names for external lookup. +// The message names are constructed to avoid the problems of concatenating +// phrases (which does not translate well into other languages). The +// message names that can be generated are (listed here to appease the +// auditing program which reads comments): +// ERR_MSG("DjVuAnno.invalid2number"), ERR_MSG("DjVuAnno.string2number"), +// ERR_MSG("DjVuAnno.symbol2number"), ERR_MSG("DjVuAnno.list2number") +// ERR_MSG("DjVuAnno.invalid2string"), ERR_MSG("DjVuAnno.number2string"), +// ERR_MSG("DjVuAnno.symbol2string"), ERR_MSG("DjVuAnno.list2string") +// ERR_MSG("DjVuAnno.invalid2symbol"), ERR_MSG("DjVuAnno.number2symbol"), +// ERR_MSG("DjVuAnno.string2symbol"), ERR_MSG("DjVuAnno.list2symbol") +// ERR_MSG("DjVuAnno.invalid2list"), ERR_MSG("DjVuAnno.number2list"), +// ERR_MSG("DjVuAnno.string2list"), ERR_MSG("DjVuAnno.symbol2list") +void +GLObject::throw_can_not_convert_to(const GLObjectType to) const +{ + static const GUTF8String two('2'); + static const GUTF8String tab('\t'); + GUTF8String mesg("DjVuAnno."); + switch(type) + { + case NUMBER: + mesg+=GLObjectString[NUMBER]+two+GLObjectString[to]+tab+GUTF8String(number); + break; + case STRING: + mesg+=GLObjectString[STRING]+two+GLObjectString[to]+tab+string; + break; + case SYMBOL: + mesg+=GLObjectString[SYMBOL]+two+GLObjectString[to]+tab+symbol; + break; + case LIST: + mesg+=GLObjectString[LIST]+two+GLObjectString[to]+tab+name; + break; + default: + mesg+=GLObjectString[INVALID]+two+GLObjectString[to]; + break; + } + G_THROW(mesg); +} + +GUTF8String +GLObject::get_string(void) const +{ + if (type!=STRING) + { + throw_can_not_convert_to(STRING); + } + return string; +} + +GUTF8String +GLObject::get_symbol(void) const +{ + if (type!=SYMBOL) + { + throw_can_not_convert_to(SYMBOL); + } + return symbol; +} + +int +GLObject::get_number(void) const +{ + if (type!=NUMBER) + { + throw_can_not_convert_to(NUMBER); + } + return number; +} + +GUTF8String +GLObject::get_name(void) const +{ + if (type!=LIST) + { + throw_can_not_convert_to(LIST); + } + return name; +} + +GP<GLObject> +GLObject::operator[](int n) const +{ + if (type!=LIST) + { + throw_can_not_convert_to(LIST); + } + if (n>=list.size()) G_THROW( ERR_MSG("DjVuAnno.too_few") "\t"+name); + int i; + GPosition pos; + for(i=0, pos=list;i<n && pos;i++, ++pos) + continue; + return list[pos]; +} + +GPList<GLObject> & +GLObject::get_list(void) +{ + if (type!=LIST) + { + throw_can_not_convert_to(LIST); + } + return list; +} + +//********************************** GLParser ********************************* + +void +GLParser::skip_white_space(const char * & start) +{ + while(*start && isspace(*start)) start++; + if (!*start) + G_THROW( ByteStream::EndOfFile ); +} + +GLToken +GLParser::get_token(const char * & start) +{ + skip_white_space(start); + char c = *start; + if (c == '(') + { + start++; + return GLToken(GLToken::OPEN_PAR, 0); + } + else if (c == ')') + { + start++; + return GLToken(GLToken::CLOSE_PAR, 0); + } + else if (c=='-' || (c>='0' && c<='9')) + { + return GLToken(GLToken::OBJECT, + new GLObject(strtol(start, (char **) &start, 10))); + } + else if (c=='"') + { + GUTF8String str; + start++; + while(1) + { + int span = 0; + while (start[span] && start[span]!='\\' && start[span]!='\"') + span++; + if (span > 0) + { + str = str + GUTF8String(start,span); + start += span; + } + else if (start[0]=='\"') + { + start += 1; + break; + } + else if (start[0]=='\\' && compat) + { + char c = start[1]; + if (c == '\"') + { + start += 2; + str += '\"'; + } + else + { + start += 1; + str += '\\'; + } + } + else if (start[0]=='\\' && start[1]) + { + char c = *++start; + if (c>='0' && c<='7') + { + int x = 0; + for (int i=0; i<3 && c>='0' && c<='7'; i++) + { + x = x * 8 + c - '0'; + c = *++start; + } + str += (char)(x & 0xff); + } + else + { + static char *tr1 = "tnrbfva"; + static char *tr2 = "\t\n\r\b\f\013\007"; + for (int i=0; tr1[i]; i++) + if (c == tr1[i]) + c = tr2[i]; + start += 1; + str += c; + } + } + else + { + G_THROW( ByteStream::EndOfFile ); + } + } + return GLToken(GLToken::OBJECT, + new GLObject(GLObject::STRING, str)); + } + else + { + GUTF8String str; + while(1) + { + char ch=*start++; + if (!ch) + G_THROW( ByteStream::EndOfFile ); + if (ch==')') { start--; break; } + if (isspace(ch)) break; + str+=ch; + } + return GLToken(GLToken::OBJECT, new GLObject(GLObject::SYMBOL, str)); + } +} + +void +GLParser::parse(const char * cur_name, GPList<GLObject> & list, + const char * & start) +{ + DEBUG_MSG("GLParse::parse(): Parsing contents of object '" << cur_name << "'\n"); + DEBUG_MAKE_INDENT(3); + + while(1) + { + GLToken token=get_token(start); + if (token.type==GLToken::OPEN_PAR) + { + if (isspace(*start)) + { + GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.paren") "\t")+cur_name; + G_THROW(mesg); + } + + GLToken tok=get_token(start); + GP<GLObject> object=tok.object; // This object should be SYMBOL + // We will convert it to LIST later + if (tok.type!=GLToken::OBJECT || object->get_type()!=GLObject::SYMBOL) + { + if (tok.type==GLToken::OPEN_PAR || + tok.type==GLToken::CLOSE_PAR) + { + GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.no_paren") "\t")+cur_name; + G_THROW(mesg); + } + if (tok.type==GLToken::OBJECT) + { + GLObject::GLObjectType type=object->get_type(); + if (type==GLObject::NUMBER) + { + GUTF8String mesg( ERR_MSG("DjVuAnno.no_number") "\t"); + mesg += cur_name; + G_THROW(mesg); + } + else if (type==GLObject::STRING) + { + GUTF8String mesg( ERR_MSG("DjVuAnno.no_string") "\t"); + mesg += cur_name; + G_THROW(mesg); + } + } + } + + // OK. Get the object contents + GPList<GLObject> new_list; + G_TRY + { + parse(object->get_symbol(), new_list, start); + } + G_CATCH(exc) + { + if (exc.cmp_cause(ByteStream::EndOfFile)) + G_RETHROW; + } + G_ENDCATCH; + list.append(new GLObject(object->get_symbol(), new_list)); + continue; + } + if (token.type==GLToken::CLOSE_PAR) + return; + list.append(token.object); + } +} + +void +GLParser::check_compat(const char *s) +{ + int state = 0; + while (s && *s && !compat) + { + switch(state) + { + case 0: + if (*s == '\"') + state = '\"'; + break; + case '\"': + if (*s == '\"') + state = 0; + else if (*s == '\\') + state = '\\'; + else if ((unsigned char)(*s)<0x20 || *s==0x7f) + compat = true; + break; + case '\\': + if (!strchr("01234567tnrbfva\"\\",*s)) + compat = true; + state = '\"'; + break; + } + s += 1; + } +} + +void +GLParser::parse(const char * str) +{ + DEBUG_MSG("GLParser::parse(): parsing string contents\n"); + DEBUG_MAKE_INDENT(3); + + G_TRY + { + check_compat(str); + parse("toplevel", list, str); + } G_CATCH(exc) + { + if (exc.cmp_cause(ByteStream::EndOfFile)) + G_RETHROW; + } G_ENDCATCH; +} + +void +GLParser::print(ByteStream & str, int compact) +{ + for(GPosition pos=list;pos;++pos) + list[pos]->print(str, compact); +} + +GP<GLObject> +GLParser::get_object(const char * name, bool last) +{ + GP<GLObject> object; + for(GPosition pos=list;pos;++pos) + { + GP<GLObject> obj=list[pos]; + if (obj->get_type()==GLObject::LIST && + obj->get_name()==name) + { + object=obj; + if (!last) break; + } + } + return object; +} + +//*************************************************************************** +//********************************** ANT ************************************ +//*************************************************************************** + +static const char *zoom_strings[]={ + "default","page","width","one2one","stretch"}; +static const int zoom_strings_size=sizeof(zoom_strings)/sizeof(const char *); + +static const char *mode_strings[]={ + "default","color","fore","back","bw"}; +static const int mode_strings_size=sizeof(mode_strings)/sizeof(const char *); + +static const char *align_strings[]={ + "default","left","center","right","top","bottom"}; +static const int align_strings_size=sizeof(align_strings)/sizeof(const char *); + +#define PNOTE_TAG "pnote" +#define BACKGROUND_TAG "background" +#define ZOOM_TAG "zoom" +#define MODE_TAG "mode" +#define ALIGN_TAG "align" +#define HALIGN_TAG "halign" +#define VALIGN_TAG "valign" +#define METADATA_TAG "metadata" + +static const unsigned long default_bg_color=0xffffffff; + +DjVuANT::DjVuANT(void) +{ + bg_color=default_bg_color; + zoom=0; + mode=MODE_UNSPEC; + hor_align=ver_align=ALIGN_UNSPEC; +} + +DjVuANT::~DjVuANT() +{ +} + +GUTF8String +DjVuANT::get_paramtags(void) const +{ + GUTF8String retval; + if(zoom > 0) + { + retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom)+="\" />\n"; + }else if(zoom && ((-zoom)<zoom_strings_size)) + { + retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom_strings[-zoom])+"\" />\n"; + } + if((mode>0)&&(mode<mode_strings_size)) + { + retval+="<PARAM name=\"" MODE_TAG "\" value=\""+GUTF8String(mode_strings[mode])+"\" />\n"; + } + if((hor_align>ALIGN_UNSPEC)&&(hor_align<align_strings_size)) + { + retval+="<PARAM name=\"" HALIGN_TAG "\" value=\""+GUTF8String(align_strings[hor_align])+"\" />\n"; + } + if((ver_align>ALIGN_UNSPEC)&&(ver_align<align_strings_size)) + { + retval+="<PARAM name=\"" VALIGN_TAG "\" value=\""+GUTF8String(align_strings[ver_align])+"\" />\n"; + } + if((bg_color&0xffffff) == bg_color) + { + retval+="<PARAM name=\"" BACKGROUND_TAG "\" value=\""+GUTF8String().format("#%06lX",bg_color)+"\" />\n"; + } + return retval; +} + +void +DjVuANT::writeParam(ByteStream &str_out) const +{ + str_out.writestring(get_paramtags()); +} + +GUTF8String +DjVuANT::get_xmlmap(const GUTF8String &name,const int height) const +{ + GUTF8String retval("<MAP name=\""+name.toEscaped()+"\" >\n"); + for(GPosition pos(map_areas);pos;++pos) + { + retval+=map_areas[pos]->get_xmltag(height); + } + return retval+"</MAP>\n"; +} + +void +DjVuANT::writeMap( + ByteStream &str_out,const GUTF8String &name,const int height) const +{ + str_out.writestring("<MAP name=\""+name.toEscaped()+"\" >\n"); + for(GPosition pos(map_areas);pos;++pos) + { + str_out.writestring(GUTF8String(map_areas[pos]->get_xmltag(height))); + } + str_out.writestring(GUTF8String("</MAP>\n")); +} + +GUTF8String +DjVuANT::read_raw(ByteStream & str) +{ + GUTF8String raw; + char buffer[1024]; + int length; + while((length=str.read(buffer, 1024))) + raw+=GUTF8String(buffer, length); + return raw; +} + +void +DjVuANT::decode(class GLParser & parser) +{ + bg_color=get_bg_color(parser); + zoom=get_zoom(parser); + mode=get_mode(parser); + hor_align=get_hor_align(parser); + ver_align=get_ver_align(parser); + map_areas=get_map_areas(parser); +#ifndef NO_METADATA_IN_ANT_CHUNK + metadata=get_metadata(parser); +#endif +} + + +void +DjVuANT::decode(ByteStream & str) +{ + GLParser parser(read_raw(str)); + decode(parser); +} + +void +DjVuANT::merge(ByteStream & str) +{ + GLParser parser(encode_raw()); + GUTF8String add_raw=read_raw(str); + parser.parse(add_raw); + decode(parser); +} + +void +DjVuANT::encode(ByteStream &bs) +{ + GUTF8String raw=encode_raw(); + bs.writall((const char*) raw, raw.length()); +} + +unsigned int +DjVuANT::get_memory_usage() const +{ + return sizeof(DjVuANT); +} + +unsigned char +DjVuANT::decode_comp(char ch1, char ch2) +{ + unsigned char dig1=0; + if (ch1) + { + ch1=toupper(ch1); + if (ch1>='0' && ch1<='9') dig1=ch1-'0'; + if (ch1>='A' && ch1<='F') dig1=10+ch1-'A'; + + unsigned char dig2=0; + if (ch2) + { + ch2=toupper(ch2); + if (ch2>='0' && ch2<='9') dig2=ch2-'0'; + if (ch2>='A' && ch2<='F') dig2=10+ch2-'A'; + return (dig1 << 4) | dig2; + } + return dig1; + } + return 0; +} + +unsigned long int +DjVuANT::cvt_color(const char * color, unsigned long int def) +{ + if (color[0]!='#') return def; + + unsigned long int color_rgb=0; + color++; + const char * start, * end; + + // Do blue + end=color+strlen(color); start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0); + + // Do green + end=color+strlen(color)-2; start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 8; + + // Do red + end=color+strlen(color)-4; start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 16; + + // Do the fourth byte + end=color+strlen(color)-6; start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 24; + + return color_rgb; +} + +unsigned long int +DjVuANT::get_bg_color(GLParser & parser) +{ + unsigned long retval=default_bg_color; + DEBUG_MSG("DjVuANT::get_bg_color(): getting background color ...\n"); + DEBUG_MAKE_INDENT(3); + G_TRY + { + GP<GLObject> obj=parser.get_object(BACKGROUND_TAG); + if (obj && obj->get_list().size()==1) + { + GUTF8String color=(*obj)[0]->get_symbol(); + DEBUG_MSG("color='" << color << "'\n"); + retval=cvt_color(color, 0xffffff); + } +#ifndef NDEBUG + if(retval == default_bg_color) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == default_bg_color) + { + DEBUG_MSG("resetting color to 0xffffffff (UNSPEC)\n"); + } +#endif // NDEBUG + return retval; +} + +int +DjVuANT::get_zoom(GLParser & parser) + // Returns: + // <0 - special zoom (like ZOOM_STRETCH) + // =0 - not set + // >0 - numeric zoom (%%) +{ + int retval=ZOOM_UNSPEC; + DEBUG_MSG("DjVuANT::get_zoom(): getting zoom factor ...\n"); + DEBUG_MAKE_INDENT(3); + G_TRY + { + GP<GLObject> obj=parser.get_object(ZOOM_TAG); + if (obj && obj->get_list().size()==1) + { + const GUTF8String zoom((*obj)[0]->get_symbol()); + DEBUG_MSG("zoom='" << zoom << "'\n"); + + for(int i=0;(i<zoom_strings_size);++i) + { + if(zoom == zoom_strings[i]) + { + retval=(-i); + break; + } + } + if(retval == ZOOM_UNSPEC) + { + if (zoom[0]!='d') + { + G_THROW( ERR_MSG("DjVuAnno.bad_zoom") ); + }else + { + retval=zoom.substr(1, zoom.length()).toInt(); //atoi((const char *) zoom+1); + } + } + } +#ifndef NDEBUG + if(retval == ZOOM_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == ZOOM_UNSPEC) + { + DEBUG_MSG("resetting zoom to 0 (UNSPEC)\n"); + } +#endif // NDEBUG + return retval; +} + +int +DjVuANT::get_mode(GLParser & parser) +{ + int retval=MODE_UNSPEC; + DEBUG_MSG("DjVuAnt::get_mode(): getting default mode ...\n"); + DEBUG_MAKE_INDENT(3); + G_TRY + { + GP<GLObject> obj=parser.get_object(MODE_TAG); + if (obj && obj->get_list().size()==1) + { + const GUTF8String mode((*obj)[0]->get_symbol()); + DEBUG_MSG("mode='" << mode << "'\n"); + for(int i=0;(i<mode_strings_size);++i) + { + if(mode == mode_strings[i]) + { + retval=i; + break; + } + } + } +#ifndef NDEBUG + if(retval == MODE_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == MODE_UNSPEC) + { + DEBUG_MSG("resetting mode to MODE_UNSPEC\n"); + } +#endif // NDEBUG + return retval; +} + +static inline DjVuANT::alignment +legal_halign(const int i) +{ + DjVuANT::alignment retval; + switch((DjVuANT::alignment)i) + { + case DjVuANT::ALIGN_LEFT: + case DjVuANT::ALIGN_CENTER: + case DjVuANT::ALIGN_RIGHT: + retval=(DjVuANT::alignment)i; + break; + default: + retval=DjVuANT::ALIGN_UNSPEC; + break; + } + return retval; +} + +static inline DjVuANT::alignment +legal_valign(const int i) +{ + DjVuANT::alignment retval; + switch((DjVuANT::alignment)i) + { + case DjVuANT::ALIGN_CENTER: + case DjVuANT::ALIGN_TOP: + case DjVuANT::ALIGN_BOTTOM: + retval=(DjVuANT::alignment)i; + break; + default: + retval=DjVuANT::ALIGN_UNSPEC; + break; + } + return retval; +} + +DjVuANT::alignment +DjVuANT::get_hor_align(GLParser & parser) +{ + DEBUG_MSG("DjVuAnt::get_hor_align(): getting hor page alignemnt ...\n"); + DEBUG_MAKE_INDENT(3); + alignment retval=ALIGN_UNSPEC; + G_TRY + { + GP<GLObject> obj=parser.get_object(ALIGN_TAG); + if (obj && obj->get_list().size()==2) + { + const GUTF8String align((*obj)[0]->get_symbol()); + DEBUG_MSG("hor_align='" << align << "'\n"); + + for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i) + { + const alignment j=legal_halign(i); + if((i == (int)j)&&(align == align_strings[i])) + { + retval=j; + break; + } + } + } +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n"); + } +#endif // NDEBUG + return retval; +} + +DjVuANT::alignment +DjVuANT::get_ver_align(GLParser & parser) +{ + DEBUG_MSG("DjVuAnt::get_ver_align(): getting vert page alignemnt ...\n"); + DEBUG_MAKE_INDENT(3); + alignment retval=ALIGN_UNSPEC; + G_TRY + { + GP<GLObject> obj=parser.get_object(ALIGN_TAG); + if (obj && obj->get_list().size()==2) + { + const GUTF8String align((*obj)[1]->get_symbol()); + DEBUG_MSG("ver_align='" << align << "'\n"); + for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i) + { + const alignment j=legal_valign(i); + if((i == (int)j)&&(align == align_strings[i])) + { + retval=j; + break; + } + } + } +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n"); + } +#endif // NDEBUG + return retval; +} + +#ifndef NO_METADATA_IN_ANT_CHUNK +GMap<GUTF8String, GUTF8String> +DjVuANT::get_metadata(GLParser & parser) +{ + DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n"); + DEBUG_MAKE_INDENT(3); + + GMap<GUTF8String, GUTF8String> mdata; + + GPList<GLObject> list=parser.get_list(); + for(GPosition pos=list;pos;++pos) + { + GLObject & obj=*list[pos]; + if (obj.get_type()==GLObject::LIST && obj.get_name()==METADATA_TAG) + { + G_TRY + { + for(int obj_num=0;obj_num<obj.get_list().size();obj_num++) + { + GLObject & el=*obj[obj_num]; + const int type = el.get_type(); + if (type == GLObject::LIST) + { + const GUTF8String & name=el.get_name(); + mdata[name]=(el[0])->get_string(); + } + } + } + G_CATCH_ALL { } G_ENDCATCH; + } + } + return mdata; +} +#endif + +GPList<GMapArea> +DjVuANT::get_map_areas(GLParser & parser) +{ + DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n"); + DEBUG_MAKE_INDENT(3); + + GPList<GMapArea> map_areas; + + GPList<GLObject> list=parser.get_list(); + + for(GPosition pos=list;pos;++pos) + { + GLObject & obj=*list[pos]; + const int type=obj.get_type(); + if (type == GLObject::LIST) + { + const GUTF8String name=obj.get_name(); + if(name == GMapArea::MAPAREA_TAG) + { + G_TRY { + // Getting the url + GUTF8String url; + GUTF8String target=GMapArea::TARGET_SELF; + GLObject & url_obj=*(obj[0]); + if (url_obj.get_type()==GLObject::LIST) + { + if (url_obj.get_name()!=GMapArea::URL_TAG) + G_THROW( ERR_MSG("DjVuAnno.bad_url") ); + url=(url_obj[0])->get_string(); + target=(url_obj[1])->get_string(); + } else url=url_obj.get_string(); + + // Getting the comment + GUTF8String comment=(obj[1])->get_string(); + + DEBUG_MSG("found maparea '" << comment << "' (" << + url << ":" << target << ")\n"); + + GLObject * shape=obj[2]; + GP<GMapArea> map_area; + if (shape->get_type()==GLObject::LIST) + { + if (shape->get_name()==GMapArea::RECT_TAG) + { + DEBUG_MSG("it's a rectangle.\n"); + GRect grect((*shape)[0]->get_number(), + (*shape)[1]->get_number(), + (*shape)[2]->get_number(), + (*shape)[3]->get_number()); + GP<GMapRect> map_rect=GMapRect::create(grect); + map_area=(GMapRect *)map_rect; + } else if (shape->get_name()==GMapArea::POLY_TAG) + { + DEBUG_MSG("it's a polygon.\n"); + int points=shape->get_list().size()/2; + GTArray<int> xx(points-1), yy(points-1); + for(int i=0;i<points;i++) + { + xx[i]=(*shape)[2*i]->get_number(); + yy[i]=(*shape)[2*i+1]->get_number(); + } + GP<GMapPoly> map_poly=GMapPoly::create(xx,yy,points); + map_area=(GMapPoly *)map_poly; + } else if (shape->get_name()==GMapArea::OVAL_TAG) + { + DEBUG_MSG("it's an ellipse.\n"); + GRect grect((*shape)[0]->get_number(), + (*shape)[1]->get_number(), + (*shape)[2]->get_number(), + (*shape)[3]->get_number()); + GP<GMapOval> map_oval=GMapOval::create(grect); + map_area=(GMapOval *)map_oval; + } + } + + if (map_area) + { + map_area->url=url; + map_area->target=target; + map_area->comment=comment; + for(int obj_num=3;obj_num<obj.get_list().size();obj_num++) + { + GLObject * el=obj[obj_num]; + if (el->get_type()==GLObject::LIST) + { + const GUTF8String & name=el->get_name(); + if (name==GMapArea::BORDER_AVIS_TAG) + map_area->border_always_visible=true; + else if (name==GMapArea::HILITE_TAG) + { + GLObject * obj=el->get_list()[el->get_list().firstpos()]; + if (obj->get_type()==GLObject::SYMBOL) + map_area->hilite_color=cvt_color(obj->get_symbol(), 0xff); + } else + { + int border_type= + name==GMapArea::NO_BORDER_TAG ? GMapArea::NO_BORDER : + name==GMapArea::XOR_BORDER_TAG ? GMapArea::XOR_BORDER : + name==GMapArea::SOLID_BORDER_TAG ? GMapArea::SOLID_BORDER : + name==GMapArea::SHADOW_IN_BORDER_TAG ? GMapArea::SHADOW_IN_BORDER : + name==GMapArea::SHADOW_OUT_BORDER_TAG ? GMapArea::SHADOW_OUT_BORDER : + name==GMapArea::SHADOW_EIN_BORDER_TAG ? GMapArea::SHADOW_EIN_BORDER : + name==GMapArea::SHADOW_EOUT_BORDER_TAG ? GMapArea::SHADOW_EOUT_BORDER : -1; + if (border_type>=0) + { + map_area->border_type=(GMapArea::BorderType) border_type; + for(GPosition pos=el->get_list();pos;++pos) + { + GLObject * obj=el->get_list()[pos]; + if (obj->get_type()==GLObject::SYMBOL) + map_area->border_color=cvt_color(obj->get_symbol(), 0xff); + if (obj->get_type()==GLObject::NUMBER) + map_area->border_width=obj->get_number(); + } + } + } + } // if (el->get_type()==...) + } // for(int obj_num=...) + map_areas.append(map_area); + } // if (map_area) ... + } G_CATCH_ALL {} G_ENDCATCH; + } + } + } // while(item==...) + + DEBUG_MSG("map area list size = " << list.size() << "\n"); + + return map_areas; +} + +void +DjVuANT::del_all_items(const char * name, GLParser & parser) +{ + GPList<GLObject> & list=parser.get_list(); + GPosition pos=list; + while(pos) + { + GLObject & obj=*list[pos]; + if (obj.get_type()==GLObject::LIST && + obj.get_name()==name) + { + GPosition this_pos=pos; + ++pos; + list.del(this_pos); + } else ++pos; + } +} + +GUTF8String +DjVuANT::encode_raw(void) const +{ + GUTF8String buffer; + GLParser parser; + + //*** Background color + del_all_items(BACKGROUND_TAG, parser); + if (bg_color!=default_bg_color) + { + buffer.format("(" BACKGROUND_TAG " #%02X%02X%02X)", + (unsigned int)((bg_color & 0xff0000) >> 16), + (unsigned int)((bg_color & 0xff00) >> 8), + (unsigned int)(bg_color & 0xff)); + parser.parse(buffer); + } + + //*** Zoom + del_all_items(ZOOM_TAG, parser); + if (zoom!=ZOOM_UNSPEC) + { + buffer="(" ZOOM_TAG " "; + const int i=1-zoom; + if((i>=0)&& (i<zoom_strings_size)) + { + buffer+=zoom_strings[i]; + }else + { + buffer+="d"+GUTF8String(zoom); + } + buffer+=")"; + parser.parse(buffer); + } + + //*** Mode + del_all_items(MODE_TAG, parser); + if (mode!=MODE_UNSPEC) + { + const int i=mode-1; + if((i>=0)&& (i<mode_strings_size)) + { + buffer="(" MODE_TAG " " + GUTF8String(mode_strings[mode]) + ")"; + } + parser.parse(buffer); + } + + //*** Alignment + del_all_items(ALIGN_TAG, parser); + if (hor_align!=ALIGN_UNSPEC || ver_align!=ALIGN_UNSPEC) + { + buffer= GUTF8String("(" ALIGN_TAG " ") + +align_strings[((hor_align<ALIGN_UNSPEC)|| + (hor_align>=align_strings_size))?ALIGN_UNSPEC:hor_align] + +" "+align_strings[((ver_align<ALIGN_UNSPEC)|| + (ver_align>=align_strings_size))?ALIGN_UNSPEC:ver_align]+")"; + parser.parse(buffer); + } + //*** Metadata +#ifndef NO_METADATA_IN_ANT_CHUNK + del_all_items(METADATA_TAG, parser); + if (!metadata.isempty()) + { + GUTF8String mdatabuffer("("); + mdatabuffer += METADATA_TAG ; + for (GPosition pos=metadata; pos; ++pos) + mdatabuffer +=" (" + metadata.key(pos)+" \""+metadata[pos]+"\")"; + mdatabuffer += " )"; + parser.parse(mdatabuffer); + } +#endif + //*** Mapareas + del_all_items(GMapArea::MAPAREA_TAG, parser); + for(GPosition pos=map_areas;pos;++pos) + parser.parse(map_areas[pos]->print()); + + GP<ByteStream> gstr=ByteStream::create(); + ByteStream &str=*gstr; + parser.print(str, 1); + GUTF8String ans; + int size = str.size(); + str.seek(0); + str.read(ans.getbuf(size), size); + return ans; +} + +bool +DjVuANT::is_empty(void) const +{ + GUTF8String raw=encode_raw(); + for(int i=raw.length()-1;i>=0;i--) + if (isspace(raw[i])) raw.setat(i, 0); + else break; + return raw.length()==0; +} + +GP<DjVuANT> +DjVuANT::copy(void) const +{ + GP<DjVuANT> ant=new DjVuANT(*this); + + + // Now process the list of hyperlinks. + ant->map_areas.empty(); + for(GPosition pos=map_areas;pos;++pos) + ant->map_areas.append(map_areas[pos]->get_copy()); + + return ant; +} + +//*************************************************************************** +//******************************** DjVuAnno ********************************* +//*************************************************************************** + +GUTF8String +DjVuAnno::get_xmlmap(const GUTF8String &name,const int height) const +{ + return ant + ?(ant->get_xmlmap(name,height)) + :("<MAP name=\""+name.toEscaped()+"\"/>\n"); +} + +void +DjVuAnno::writeMap(ByteStream &str_out,const GUTF8String &name,const int height) const +{ + if(ant) + { + ant->writeMap(str_out,name,height); + }else + { + str_out.writestring(get_xmlmap(name,height)); + } +} + +GUTF8String +DjVuAnno::get_paramtags(void) const +{ + return ant + ?(ant->get_paramtags()) + :GUTF8String(); +} + +void +DjVuAnno::writeParam(ByteStream &str_out) const +{ + str_out.writestring(get_paramtags()); +} + + +void +DjVuAnno::decode(const GP<ByteStream> &gbs) +{ + GUTF8String chkid; + GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + while( iff.get_chunk(chkid) ) + { + if (chkid == "ANTa") + { + if (ant) { + ant->merge(*iff.get_bytestream()); + } else { + ant=DjVuANT::create(); + ant->decode(*iff.get_bytestream()); + } + } + else if (chkid == "ANTz") + { + GP<ByteStream> gbsiff=BSByteStream::create(giff->get_bytestream()); + if (ant) { + ant->merge(*gbsiff); + } else { + ant=DjVuANT::create(); + ant->decode(*gbsiff); + } + } + // Add decoding of other chunks here + iff.close_chunk(); + } +} + +void +DjVuAnno::encode(const GP<ByteStream> &gbs) +{ + GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + if (ant) + { +#if 0 + iff.put_chunk("ANTa"); + ant->encode(iff); + iff.close_chunk(); +#else + iff.put_chunk("ANTz"); + { +// GP<ByteStream> bsbinput = giff.get_bytestream(); + GP<ByteStream> bsb = BSByteStream::create(giff->get_bytestream(), 50); + ant->encode(*bsb); + } + iff.close_chunk(); +#endif + } + // Add encoding of other chunks here +} + + +GP<DjVuAnno> +DjVuAnno::copy(void) const +{ + GP<DjVuAnno> anno= new DjVuAnno; + // Copy any primitives (if any) + *anno=*this; + // Copy each substructure + if (ant) anno->ant = ant->copy(); + return anno; +} + +void +DjVuAnno::merge(const GP<DjVuAnno> & anno) +{ + if (anno) + { + GP<ByteStream> gstr=ByteStream::create(); + encode(gstr); + anno->encode(gstr); + gstr->seek(0); + decode(gstr); + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuAnno.h b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.h new file mode 100644 index 00000000..964c6c44 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.h @@ -0,0 +1,295 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuAnno.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUANNO_H +#define _DJVUANNO_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + + +/** @name DjVuAnno.h + + Files #"DjVuAnno.h"# and #"DjVuAnno.cpp"# implement the mechanism for + annotating DjVuImages. Annotations are additional instructions for the + plugin about how the image should be displayed. The exact format of + annotations is not strictly defined. The only requirement is that they + have to be stored as a sequence of chunks inside a #FORM:ANNO#. + + This file implements annotations understood by the DjVu plugins + and encoders. + + + using: contents of #ANT*# chunks. + + Contents of the #FORM:ANNO# should be passed to \Ref{DjVuAnno::decode}() + for parsing, which initializes \Ref{DjVuAnno::ANT} + and fills them with decoded data. + @memo Implements support for DjVuImage annotations + @author Andrei Erofeev <eaf@geocities.com> + @version + #$Id: DjVuAnno.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GMapArea; +class ByteStream; + +// -------- DJVUANT -------- + +/** This class contains some trivial annotations of the page or of the + document such as page border color, page alignment, initial zoom and + display mode, hyperlinks and highlighted areas. All this information is + put inside a textual chunk #ANTa# in pseudo-lisp format. Decoding and + encoding are normally done by \Ref{DjVuANT::decode}() and + \Ref{DjVuANT::encode}() functions. */ + +class DjVuANT : public GPEnabled +{ +protected: + /// Constructs an empty annotation object. + DjVuANT(void); + +public: + enum { MODE_UNSPEC=0, MODE_COLOR, MODE_FORE, MODE_BACK, MODE_BW }; + enum { ZOOM_STRETCH=-4, ZOOM_ONE2ONE=-3, ZOOM_WIDTH=-2, + ZOOM_PAGE=-1, ZOOM_UNSPEC=0 }; + enum alignment { ALIGN_UNSPEC=0, ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, + ALIGN_TOP, ALIGN_BOTTOM }; + + /// Creates an empty annotation object. + static GP<DjVuANT> create(void) { return new DjVuANT; } + virtual ~DjVuANT(); + + /** Background color. Is in #0x00RRBBGG# format. #0xffffffff# if + there were no background color records in the annotation chunk. */ + unsigned long int bg_color; + /** Initial zoom. Possible values are: + \begin{description} + \item[ZOOM_STRETCH] the image is stretched to the viewport. + \item[ZOOM_ONE2ONE] the image is displayed pixel-to-pixel. + \item[ZOOM_WIDTH] "Fit width" mode. + \item[ZOOM_PAGE] "Fit page" mode. + \item[ZOOM_UNSPEC] Annotation does not specify a zoom factor. + \end{description} */ + int zoom; + /** Initial mode. Possible values are: + \begin{description} + \item[MODE_COLOR] color mode. + \item[MODE_FORE] foreground mode. + \item[MODE_BACK] background mode. + \item[MODE_BW] black and white mode. + \item[MODE_UNSPEC] Annotation does not specify a display mode. + \item[Any positive number] Zoom in \%. Please note that + all constants above are either negative or ZERO. Thus + it's possible to distinguish numerical zoom from those + special cases. + \end{description} */ + int mode; + /** Horizontal page alignment. Possible values are #ALIGN_LEFT#, + #ALIGN_CENTER#, #ALIGN_RIGHT# and #ALIGN_UNSPEC#. */ + alignment hor_align; + /** Vertical page alignment. Possible values are #ALIGN_TOP#, + #ALIGN_CENTER#, #ALIGN_BOTTOM# and #ALIGN_UNSPEC#. */ + alignment ver_align; + /** List of defined map areas. They may be just areas of highlighting + or hyperlink. Please refer to \Ref{GMapArea}, \Ref{GMapRect}, + \Ref{GMapPoly} and \Ref{GMapOval} for details. */ + GPList<GMapArea> map_areas; +#ifndef NO_METADATA_IN_ANT_CHUNK + /** Metainformations like title, author ... */ + GMap<GUTF8String,GUTF8String> metadata; +#endif + /** Returns TRUE if no features are specified or specified features + are not different from default ones */ + bool is_empty(void) const; + + /** Decodes contents of annotation chunk #ANTa#. The chunk data is + read from ByteStream #bs# until reaching an end-of-stream marker. + This function is normally called after a call to + \Ref{IFFByteStream::get_chunk}(). */ + void decode(ByteStream &bs); + + /** Same as \Ref{decode}() but adds the new data to what has + been decoded before. */ + void merge(ByteStream & bs); + + /** Encodes the #ANTa# chunk. The annotation data is simply written + into ByteStream #bs# with no IFF header. This function is normally + called after a call to \Ref{IFFByteStream::put_chunk}(). */ + void encode(ByteStream &bs); + + /// Encodes data back into raw annotation data. + GUTF8String encode_raw(void) const; + + /// Returns a copy of this object + GP<DjVuANT> copy(void) const; + + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + unsigned int get_memory_usage() const; + + /// Converts color from string in \#RRGGBB notation to an unsigned integer + static unsigned long int cvt_color(const char * color, unsigned long int def); + /// Obtain the <MAP></MAP> tag for these annotations. + GUTF8String get_xmlmap(const GUTF8String &name, const int height) const; + /// Write the <MAP></MAP> tag for these annotations. + void writeMap( + ByteStream &bs,const GUTF8String &name, const int height) const; + /// Obtain the XML flags for the default specifications. + GUTF8String get_paramtags(void) const; + /// Write the XML flags for the default specifications. + void writeParam(ByteStream &out_str) const; +private: + void decode(class GLParser & parser); + static GUTF8String read_raw(ByteStream & str); + static unsigned char decode_comp(char ch1, char ch2); + static unsigned long int get_bg_color(class GLParser & parser); + static int get_zoom(class GLParser & parser); + static int get_mode(class GLParser & parser); + static alignment get_hor_align(class GLParser & parser); + static alignment get_ver_align(class GLParser & parser); + static GPList<GMapArea> get_map_areas(class GLParser & parser); +#ifndef NO_METADATA_IN_ANT_CHUNK + static GMap<GUTF8String, GUTF8String>get_metadata(GLParser & parser); +#endif + static void del_all_items(const char * name, class GLParser & parser); +}; + +// -------- DJVUANNO -------- + + +/** This is a top-level class containing annotations of a DjVu document (or + just a page). It has only two functions: \Ref{encode}() and + \Ref{decode}(). Both of them work with a sequence of annotation chunks + from #FORM:ANNO# form. Basing on the name of the chunks they call + #encode()# and #decode()# functions of the proper annotation structure + (like \Ref{ANT}). The real work of encoding and decoding is done by + lower-level classes. */ +class DjVuAnno : public GPEnabled +{ +protected: + DjVuAnno(void) {} +public: + /// Creation method. + static GP<DjVuAnno> create(void) { return new DjVuAnno; } + + GP<DjVuANT> ant; + + /** Decodes a sequence of annotation chunks and merges contents of every + chunk with previously decoded information. This function + should be called right after applying \Ref{IFFByteStream::get_chunk}() + to data from #FORM:ANNO#. */ + void decode(const GP<ByteStream> &bs); + + /** Encodes all annotations back into a sequence of chunks to be put + inside a #FORM:ANNO#. */ + void encode(const GP<ByteStream> &bs); + + /// Returns a copy of this object + GP<DjVuAnno> copy(void) const; + + /** Merged the contents of this class and of annotations + pointed by #anno# pointer */ + void merge(const GP<DjVuAnno> & anno); + + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + inline unsigned int get_memory_usage() const; + /// Obtain the <MAP></MAP> tag for these annotations. + GUTF8String get_xmlmap(const GUTF8String &name, const int height) const; + /// Write the <MAP></MAP> tag for these annotations. + void writeMap( + ByteStream &bs,const GUTF8String &name, const int height) const; + /// Obtain the XML flags for the default specifications. + GUTF8String get_paramtags(void) const; + /// Write the XML flags for the default specifications. + void writeParam(ByteStream &out_str) const; +private: // dummy stuff + static void decode(ByteStream *); + static void encode(ByteStream *); +}; + +//@} + +inline unsigned int +DjVuAnno::get_memory_usage() const +{ + return (ant)?(ant->get_memory_usage()):0; +} + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.cpp new file mode 100644 index 00000000..542faa7a --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.cpp @@ -0,0 +1,2193 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocEditor.cpp,v 1.13 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuDocEditor.h" +#include "DjVuImage.h" +#include "IFFByteStream.h" +#include "DataPool.h" +#include "IW44Image.h" +#include "GOS.h" +#include "GURL.h" +#include "DjVuAnno.h" +#include "GRect.h" +#include "DjVmNav.h" + +#include "debug.h" + +#include <ctype.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const char octets[4]={0x41,0x54,0x26,0x54}; + +int DjVuDocEditor::thumbnails_per_file=10; + +// This is a structure for active files and DataPools. It may contain +// a DjVuFile, which is currently being used by someone (I check the list +// and get rid of hanging files from time to time) or a DataPool, +// which is "custom" with respect to the document (was modified or +// inserted), or both. +// +// DjVuFile is set to smth!=0 when it's created using url_to_file(). +// It's reset back to ZERO in clean_files_map() when +// it sees, that a given file is not used by anyone. +// DataPool is updated when a file is inserted +class DjVuDocEditor::File : public GPEnabled +{ +public: + // 'pool' below may be non-zero only if it cannot be retrieved + // by the DjVuDocument, that is it either corresponds to a + // modified DjVuFile or it has been inserted. Otherwise it's ZERO + // Once someone assigns a non-zero DataPool, it remains non-ZERO + // (may be updated if the file gets modified) and may be reset + // only by save() or save_as() functions. + GP<DataPool> pool; + + // If 'file' is non-zero, it means, that it's being used by someone + // We check for unused files from time to time and ZERO them. + // But before we do it, we may save the DataPool in the case if + // file has been modified. + GP<DjVuFile> file; +}; + +void +DjVuDocEditor::check(void) +{ + if (!initialized) G_THROW( ERR_MSG("DjVuDocEditor.not_init") ); +} + +DjVuDocEditor::DjVuDocEditor(void) +{ + initialized=false; + refresh_cb=0; + refresh_cl_data=0; +} + +DjVuDocEditor::~DjVuDocEditor(void) +{ + if (!tmp_doc_url.is_empty()) + { + tmp_doc_url.deletefile(); + } + + GCriticalSectionLock lock(&thumb_lock); + thumb_map.empty(); + DataPool::close_all(); +} + +void +DjVuDocEditor::init(void) +{ + DEBUG_MSG("DjVuDocEditor::init() called\n"); + DEBUG_MAKE_INDENT(3); + + // If you remove this check be sure to delete thumb_map + if (initialized) G_THROW( ERR_MSG("DjVuDocEditor.init") ); + + doc_url=GURL::Filename::UTF8("noname.djvu"); + + const GP<DjVmDoc> doc(DjVmDoc::create()); + const GP<ByteStream> gstr(ByteStream::create()); + doc->write(gstr); + gstr->seek(0, SEEK_SET); + doc_pool=DataPool::create(gstr); + + orig_doc_type=UNKNOWN_TYPE; + orig_doc_pages=0; + + initialized=true; + + DjVuDocument::init(doc_url, this); +} + +void +DjVuDocEditor::init(const GURL &url) +{ + DEBUG_MSG("DjVuDocEditor::init() called: url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // If you remove this check be sure to delete thumb_map + if (initialized) + G_THROW( ERR_MSG("DjVuDocEditor.init") ); + + // First - create a temporary DjVuDocument and check its type + doc_pool=DataPool::create(url); + doc_url=url; + const GP<DjVuDocument> tmp_doc(DjVuDocument::create_wait(doc_url,this)); + if (!tmp_doc->is_init_ok()) + G_THROW( ERR_MSG("DjVuDocEditor.open_fail") "\t" +url.get_string()); + + orig_doc_type=tmp_doc->get_doc_type(); + orig_doc_pages=tmp_doc->get_pages_num(); + if (orig_doc_type==OLD_BUNDLED || + orig_doc_type==OLD_INDEXED || + orig_doc_type==SINGLE_PAGE) + { + // Suxx. I need to convert it NOW. + // We will unlink this file in the destructor + tmp_doc_url=GURL::Filename::Native(tmpnam(0)); + const GP<ByteStream> gstr(ByteStream::create(tmp_doc_url, "wb")); + tmp_doc->write(gstr, true); // Force DJVM format + gstr->flush(); + doc_pool=DataPool::create(tmp_doc_url); + } + + // OK. Now doc_pool contains data of the document in one of the + // new formats. It will be a lot easier to insert/delete pages now. + + // 'doc_url' below of course doesn't refer to the file with the converted + // data, but we will take care of it by redirecting the request_data(). + initialized=true; + DjVuDocument::init(doc_url, this); + + // Cool. Now extract the thumbnails... + GCriticalSectionLock lock(&thumb_lock); + int pages_num=get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + // Call DjVuDocument::get_thumbnail() here to bypass logic + // of DjVuDocEditor::get_thumbnail(). init() is the only safe + // place where we can still call DjVuDocument::get_thumbnail(); + const GP<DataPool> pool(DjVuDocument::get_thumbnail(page_num, true)); + if (pool) + { + thumb_map[page_to_id(page_num)]=pool; + } + } + // And remove then from DjVmDir so that DjVuDocument + // does not try to use them + unfile_thumbnails(); +} + +GP<DataPool> +DjVuDocEditor::request_data(const DjVuPort * source, const GURL & url) +{ + DEBUG_MSG("DjVuDocEditor::request_data(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Check if we have either original data or converted (to new format), + // if all the story is about the DjVuDocument's data + if (url==doc_url) + return doc_pool; + + // Now see if we have any file matching the url + const GP<DjVmDir::File> frec(djvm_dir->name_to_file(url.fname())); + if (frec) + { + GCriticalSectionLock lock(&files_lock); + GPosition pos; + if (files_map.contains(frec->get_load_name(), pos)) + { + const GP<File> f(files_map[pos]); + if (f->file && f->file->get_init_data_pool()) + return f->file->get_init_data_pool();// Favor DjVuFile's knowledge + else if (f->pool) return f->pool; + } + } + + // Finally let DjVuDocument cope with it. It may be a connected DataPool + // for a BUNDLED format. Or it may be a file. Anyway, it was not + // manually included, so it should be in the document. + const GP<DataPool> pool(DjVuDocument::request_data(source, url)); + + // We do NOT update the 'File' structure, because our rule is that + // we keep a separate copy of DataPool in 'File' only if it cannot + // be retrieved from DjVuDocument (like it has been "inserted" or + // corresponds to a modified file). + return pool; +} + +void +DjVuDocEditor::clean_files_map(void) + // Will go thru the map of files looking for unreferenced + // files or records w/o DjVuFile and DataPool. + // These will be modified and/or removed. +{ + DEBUG_MSG("DjVuDocEditor::clean_files_map() called\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&files_lock); + + // See if there are too old items in the "cache", which are + // not referenced by anyone. If the corresponding DjVuFile has been + // modified, obtain the new data and replace the 'pool'. Clear the + // DjVuFile anyway. If both DataPool and DjVuFile are zero, remove + // the entry. + for(GPosition pos=files_map;pos;) + { + const GP<File> f(files_map[pos]); + if (f->file && f->file->get_count()==1) + { + DEBUG_MSG("ZEROing file '" << f->file->get_url() << "'\n"); + if (f->file->is_modified()) + f->pool=f->file->get_djvu_data(false); + f->file=0; + } + if (!f->file && !f->pool) + { + DEBUG_MSG("Removing record '" << files_map.key(pos) << "'\n"); + GPosition this_pos=pos; + ++pos; + files_map.del(this_pos); + } else ++pos; + } +} + +GP<DjVuFile> +DjVuDocEditor::url_to_file(const GURL & url, bool dont_create) const +{ + DEBUG_MSG("DjVuDocEditor::url_to_file(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Check if have a DjVuFile with this url cached (created before + // and either still active or left because it has been modified) + GP<DjVmDir::File> frec; + if((const DjVmDir *)djvm_dir) + frec=djvm_dir->name_to_file(url.fname()); + if (frec) + { + GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock)); + GPosition pos; + if (files_map.contains(frec->get_load_name(), pos)) + { + const GP<File> f(files_map[pos]); + if (f->file) + return f->file; + } + } + + const_cast<DjVuDocEditor *>(this)->clean_files_map(); + + // We don't have the file cached. Let DjVuDocument create the file. + const GP<DjVuFile> file(DjVuDocument::url_to_file(url, dont_create)); + + // And add it to our private "cache" + if (file && frec) + { + GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock)); + GPosition pos; + if (files_map.contains(frec->get_load_name(), pos)) + { + files_map[frec->get_load_name()]->file=file; + }else + { + const GP<File> f(new File()); + f->file=file; + const_cast<DjVuDocEditor *>(this)->files_map[frec->get_load_name()]=f; + } + } + + return file; +} + +GUTF8String +DjVuDocEditor::page_to_id(int page_num) const +{ + if (page_num<0 || page_num>=get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num)); + const GP<DjVmDir::File> f(djvm_dir->page_to_file(page_num)); + if (! f) + G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num)); + + return f->get_load_name(); +} + +GUTF8String +DjVuDocEditor::find_unique_id(GUTF8String id) +{ + const GP<DjVmDir> dir(get_djvm_dir()); + + GUTF8String base, ext; + const int dot=id.rsearch('.'); + if(dot >= 0) + { + base=id.substr(0,dot); + ext=id.substr(dot+1,(unsigned int)-1); + }else + { + base=id; + } + + int cnt=0; + while (!(!dir->id_to_file(id) && + !dir->name_to_file(id) && + !dir->title_to_file(id))) + { + cnt++; + id=base+"_"+GUTF8String(cnt); + if (ext.length()) + id+="."+ext; + } + return id; +} + +GP<DataPool> +DjVuDocEditor::strip_incl_chunks(const GP<DataPool> & pool_in) +{ + DEBUG_MSG("DjVuDocEditor::strip_incl_chunks() called\n"); + DEBUG_MAKE_INDENT(3); + + const GP<IFFByteStream> giff_in( + IFFByteStream::create(pool_in->get_stream())); + + const GP<ByteStream> gbs_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gbs_out)); + + IFFByteStream &iff_in=*giff_in; + IFFByteStream &iff_out=*giff_out; + + bool have_incl=false; + int chksize; + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid); + while((chksize=iff_in.get_chunk(chkid))) + { + if (chkid!="INCL") + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } else + { + have_incl=true; + } + iff_in.close_chunk(); + } + iff_out.close_chunk(); + } + + if (have_incl) + { + gbs_out->seek(0,SEEK_SET); + return DataPool::create(gbs_out); + } else return pool_in; +} + +GUTF8String +DjVuDocEditor::insert_file(const GURL &file_url, const GUTF8String &parent_id, + int chunk_num, DjVuPort *source) + // Will open the 'file_name' and insert it into an existing DjVuFile + // with ID 'parent_id'. Will insert the INCL chunk at position chunk_num + // Will NOT process ANY files included into the file being inserted. + // Moreover it will strip out any INCL chunks in that file... +{ + DEBUG_MSG("DjVuDocEditor::insert_file(): fname='" << file_url << + "', parent_id='" << parent_id << "'\n"); + DEBUG_MAKE_INDENT(3); + const GP<DjVmDir> dir(get_djvm_dir()); + + if(!source) + source=this; + // Create DataPool and see if the file exists + GP<DataPool> file_pool; + if(file_url.is_empty()||file_url.is_local_file_url()) + { + file_pool=DataPool::create(file_url); + }else + { + file_pool=source->request_data(source, file_url); + if(source != this) + { + file_pool=DataPool::create(file_pool->get_stream()->duplicate()); + } + } + if(file_pool && file_url && DjVuDocument::djvu_import_codec) + { + (*DjVuDocument::djvu_import_codec)(file_pool,file_url,needs_compression_flag,can_compress_flag); + } + + // Strip any INCL chunks + file_pool=strip_incl_chunks(file_pool); + + // Check if parent ID is valid + GP<DjVmDir::File> parent_frec(dir->id_to_file(parent_id)); + if (!parent_frec) + parent_frec=dir->name_to_file(parent_id); + if (!parent_frec) + parent_frec=dir->title_to_file(parent_id); + if (!parent_frec) + G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t" +parent_id); + const GP<DjVuFile> parent_file(get_djvu_file(parent_id)); + if (!parent_file) + G_THROW( ERR_MSG("DjVuDocEditor.create_fail") "\t"+parent_id); + + // Now obtain ID for the new file + const GUTF8String id(find_unique_id(file_url.fname())); + + // Add it into the directory + const GP<DjVmDir::File> frec( + DjVmDir::File::create(id, id, id, DjVmDir::File::INCLUDE)); + int pos=dir->get_file_pos(parent_frec); + if (pos>=0) + ++pos; + dir->insert_file(frec, pos); + + // Add it to our "cache" + { + const GP<File> f(new File); + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } + + // And insert it into the parent DjVuFile + parent_file->insert_file(id, chunk_num); + + return id; +} + + // First it will insert the 'file_url' at position 'file_pos'. + // + // Then it will process all the INCL chunks in the file and try to do + // the same thing with the included files. If insertion of an included + // file fails, it will proceed with other INCL chunks until it does + // them all. In the very end we will throw exception to let the caller + // know about problems with included files. + // + // If the name of a file being inserted conflicts with some other + // name, which has been in DjVmDir prior to call to this function, + // it will be modified. name2id is the translation table to + // keep track of these modifications. + // + // Also, if a name is in name2id, we will not insert that file again. + // + // Will return TRUE if the file has been successfully inserted. + // FALSE, if the file contains NDIR chunk and has been skipped. +bool +DjVuDocEditor::insert_file(const GURL &file_url, bool is_page, + int & file_pos, GMap<GUTF8String, GUTF8String> & name2id, + DjVuPort *source) +{ + + DEBUG_MSG("DjVuDocEditor::insert_file(): file_url='" << file_url << + "', is_page='" << is_page << "'\n"); + DEBUG_MAKE_INDENT(3); + if (refresh_cb) + refresh_cb(refresh_cl_data); + + + // We do not want to insert the same file twice (important when + // we insert a group of files at the same time using insert_group()) + // So we check if we already did that and return if so. + if (name2id.contains(file_url.fname())) + return true; + + if(!source) + source=this; + + GP<DataPool> file_pool; + if(file_url.is_empty()||file_url.is_local_file_url()) + { + file_pool=DataPool::create(file_url); + } + else + { + file_pool=source->request_data(source, file_url); + if(source != this) + { + file_pool=DataPool::create(file_pool->get_stream()); + } + } + // Create DataPool and see if the file exists + if(file_pool && !file_url.is_empty() && DjVuDocument::djvu_import_codec) + { + (*DjVuDocument::djvu_import_codec)(file_pool,file_url, + needs_compression_flag, + can_compress_flag); + } + + // Oh. It does exist... Check that it has IFF structure + { + const GP<IFFByteStream> giff( + IFFByteStream::create(file_pool->get_stream())); + IFFByteStream &iff=*giff; + GUTF8String chkid; + + int length; + length=iff.get_chunk(chkid); + if (chkid!="FORM:DJVI" && chkid!="FORM:DJVU" && + chkid!="FORM:BM44" && chkid!="FORM:PM44") + G_THROW( ERR_MSG("DjVuDocEditor.not_1_page") "\t"+file_url.get_string()); + + // Wonderful. It's even a DjVu file. Scan for NDIR chunks. + // If NDIR chunk is found, ignore the file + while(iff.get_chunk(chkid)) + { + if (chkid=="NDIR") + return false; + iff.close_chunk(); + } + } + return insert_file(file_pool,file_url,is_page,file_pos,name2id,source); +} + +bool +DjVuDocEditor::insert_file(const GP<DataPool> &file_pool, + const GURL &file_url, bool is_page, + int & file_pos, GMap<GUTF8String, GUTF8String> & name2id, + DjVuPort *source) +{ + GUTF8String errors; + if(file_pool) + { + const GP<DjVmDir> dir(get_djvm_dir()); + G_TRY + { + // Now get a unique name for this file. + // Check the name2id first... + const GUTF8String name=file_url.fname(); + GUTF8String id; + if (name2id.contains(name)) + { + id=name2id[name]; + }else + { + // Check to see if this page exists with a different name. + if(!is_page) + { + GPList<DjVmDir::File> list(dir->get_files_list()); + for(GPosition pos=list;pos;++pos) + { + DEBUG_MSG("include " << list[pos]->is_include() + << " size=" << list[pos]->size << " length=" + << file_pool->get_length() << "\n"); + if(list[pos]->is_include() + && (!list[pos]->size + || (list[pos]->size == file_pool->get_length()))) + { + id=list[pos]->get_load_name(); + GP<DjVuFile> file(get_djvu_file(id,false)); + const GP<DataPool> pool(file->get_djvu_data(false)); + if(file_pool->simple_compare(*pool)) + { + // The files are the same, so just store the alias. + name2id[name]=id; + } + const GP<IFFByteStream> giff_old(IFFByteStream::create(pool->get_stream())); + const GP<IFFByteStream> giff_new(IFFByteStream::create(file_pool->get_stream())); + file=0; + if(giff_old->compare(*giff_new)) + { + // The files are the same, so just store the alias. + name2id[name]=id; + return true; + } + } + } + } + // Otherwise create a new unique ID and remember the translation + id=find_unique_id(name); + name2id[name]=id; + } + + // Good. Before we continue with the included files we want to + // complete insertion of this one. Notice, that insertion of + // children may fail, in which case we will have to modify + // data for this file to get rid of invalid INCL + + // Create a file record with the chosen ID + const GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id, + is_page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE )); + + // And insert it into the directory + file_pos=dir->insert_file(file, file_pos); + + // And add the File record (containing the file URL and DataPool) + { + const GP<File> f(new File); + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } + + // The file has been added. If it doesn't include anything else, + // that will be enough. Otherwise repeat what we just did for every + // included child. Don't forget to modify the contents of INCL + // chunks due to name2id translation. + // We also want to include here our file with shared annotations, + // if it exists. + GUTF8String chkid; + const GP<IFFByteStream> giff_in( + IFFByteStream::create(file_pool->get_stream())); + IFFByteStream &iff_in=*giff_in; + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + const GP<DjVmDir::File> shared_frec(djvm_dir->get_shared_anno_file()); + + iff_in.get_chunk(chkid); + iff_out.put_chunk(chkid); + while(iff_in.get_chunk(chkid)) + { + if (chkid!="INCL") + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_in.close_chunk(); + iff_out.close_chunk(); + if (shared_frec && chkid=="INFO") + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(shared_frec->get_load_name()); + iff_out.close_chunk(); + } + } else + { + GUTF8String name; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + name+=GUTF8String(buffer, length); + while(isspace(name[0])) + { + name=name.substr(1,(unsigned int)-1); + } + while(isspace(name[(int)name.length()-1])) + { + name.setat(name.length()-1, 0); + } + const GURL::UTF8 full_url(name,file_url.base()); + iff_in.close_chunk(); + + G_TRY { + if (insert_file(full_url, false, file_pos, name2id, source)) + { + // If the child file has been inserted (doesn't + // contain NDIR chunk), add INCL chunk. + GUTF8String id=name2id[name]; + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(id); + iff_out.close_chunk(); + } + } G_CATCH(exc) { + // Should an error occur, we move on. INCL chunk will + // not be copied. + if (errors.length()) + errors+="\n\n"; + errors+=exc.get_cause(); + } G_ENDCATCH; + } + } // while(iff_in.get_chunk(chkid)) + iff_out.close_chunk(); + + // Increment the file_pos past the page inserted. + if (file_pos>=0) file_pos++; + + // We have just inserted every included file. We may have modified + // contents of the INCL chunks. So we need to update the DataPool... + gstr_out->seek(0); + const GP<DataPool> new_file_pool(DataPool::create(gstr_out)); + { + // It's important that we replace the pool here anyway. + // By doing this we load the file into memory. And this is + // exactly what insert_group() wants us to do because + // it creates temporary files. + GCriticalSectionLock lock(&files_lock); + files_map[id]->pool=new_file_pool; + } + } G_CATCH(exc) { + if (errors.length()) + errors+="\n\n"; + errors+=exc.get_cause(); + G_THROW(errors); + } G_ENDCATCH; + + // The only place where we intercept exceptions is when we process + // included files. We want to process all of them even if we failed to + // process one. But here we need to let the exception propagate... + if (errors.length()) + G_THROW(errors); + + return true; + } + return false; +} + +void +DjVuDocEditor::insert_group(const GList<GURL> & file_urls, int page_num, + void (* _refresh_cb)(void *), void * _cl_data) + // The function will insert every file from the list at position + // corresponding to page_num. If page_num is negative, concatenation + // will occur. Included files will be processed as well +{ + refresh_cb=_refresh_cb; + refresh_cl_data=_cl_data; + + G_TRY + { + + // First translate the page_num to file_pos. + const GP<DjVmDir> dir(get_djvm_dir()); + int file_pos; + if (page_num<0 || page_num>=dir->get_pages_num()) + { + file_pos=-1; + } + else + { + file_pos=dir->get_page_pos(page_num); + } + + // Now call the insert_file() for every page. We will remember the + // name2id translation table. Thus insert_file() will remember IDs + // it assigned to shared files + GMap<GUTF8String, GUTF8String> name2id; + + GUTF8String errors; + for(GPosition pos=file_urls;pos;++pos) + { + const GURL &furl=file_urls[pos]; + DEBUG_MSG( "Inserting file '" << furl << "'\n" ); + G_TRY + { + // Check if it's a multipage document... + GP<DataPool> xdata_pool(DataPool::create(furl)); + if(xdata_pool && furl.is_valid() + && furl.is_local_file_url() && DjVuDocument::djvu_import_codec) + { + (*DjVuDocument::djvu_import_codec)(xdata_pool,furl, + needs_compression_flag, + can_compress_flag); + } + GUTF8String chkid; + IFFByteStream::create(xdata_pool->get_stream())->get_chunk(chkid); + if (name2id.contains(furl.fname())||(chkid=="FORM:DJVM")) + { + GMap<GUTF8String,void *> map; + map_ids(map); + DEBUG_MSG("Read DjVuDocument furl='" << furl << "'\n"); + GP<ByteStream> gbs(ByteStream::create()); + GP<DjVuDocument> doca(DjVuDocument::create_noinit()); + doca->set_verbose_eof(verbose_eof); + doca->set_recover_errors(recover_errors); + doca->init(furl /* ,this */ ); + doca->wait_for_complete_init(); + get_portcaster()->add_route(doca,this); + DEBUG_MSG("Saving DjVuDocument url='" << furl << "' with unique names\n"); + doca->write(gbs,map); + gbs->seek(0L); + DEBUG_MSG("Loading unique names\n"); + GP<DjVuDocument> doc(DjVuDocument::create(gbs)); + doc->set_verbose_eof(verbose_eof); + doc->set_recover_errors(recover_errors); + doc->wait_for_complete_init(); + get_portcaster()->add_route(doc,this); + gbs=0; + DEBUG_MSG("Inserting pages\n"); + int pages_num=doc->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + const GURL url(doc->page_to_url(page_num)); + insert_file(url, true, file_pos, name2id, doc); + } + } + else + { + insert_file(furl, true, file_pos, name2id, this); + } + } G_CATCH(exc) + { + if (errors.length()) + { + errors+="\n\n"; + } + errors+=exc.get_cause(); + } + G_ENDCATCH; + } + if (errors.length()) + { + G_THROW(errors); + } + } G_CATCH_ALL + { + refresh_cb=0; + refresh_cl_data=0; + G_RETHROW; + } G_ENDCATCH; + refresh_cb=0; + refresh_cl_data=0; +} + +void +DjVuDocEditor::insert_page(const GURL &file_url, int page_num) +{ + DEBUG_MSG("DjVuDocEditor::insert_page(): furl='" << file_url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GList<GURL> list; + list.append(file_url); + + insert_group(list, page_num); +} + +void +DjVuDocEditor::insert_page(GP<DataPool> & _file_pool, + const GURL & file_url, int page_num) + // Use _file_pool as source of data, create a new DjVuFile + // with name file_name, and insert it as page number page_num +{ + DEBUG_MSG("DjVuDocEditor::insert_page(): pool size='" << + _file_pool->get_size() << "'\n"); + DEBUG_MAKE_INDENT(3); + + const GP<DjVmDir> dir(get_djvm_dir()); + + // Strip any INCL chunks (we do not allow to insert hierarchies + // using this function) + const GP<DataPool> file_pool(strip_incl_chunks(_file_pool)); + + // Now obtain ID for the new file + const GUTF8String id(find_unique_id(file_url.fname())); + + // Add it into the directory + const GP<DjVmDir::File> frec(DjVmDir::File::create( + id, id, id, DjVmDir::File::PAGE)); + int pos=dir->get_page_pos(page_num); + dir->insert_file(frec, pos); + + // Add it to our "cache" + { + GP<File> f=new File; + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } +} + +void +DjVuDocEditor::generate_ref_map(const GP<DjVuFile> & file, + GMap<GUTF8String, void *> & ref_map, + GMap<GURL, void *> & visit_map) + // This private function is used to generate a list (implemented as map) + // of files referencing the given file. To get list of all parents + // for file with ID 'id' iterate map obtained as + // *((GMap<GUTF8String, void *> *) ref_map[id]) +{ + const GURL url=file->get_url(); + const GUTF8String id(djvm_dir->name_to_file(url.fname())->get_load_name()); + if (!visit_map.contains(url)) + { + visit_map[url]=0; + + GPList<DjVuFile> files_list=file->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVuFile> child_file=files_list[pos]; + // First: add the current file to the list of parents for + // the child being processed + GURL child_url=child_file->get_url(); + const GUTF8String child_id( + djvm_dir->name_to_file(child_url.fname())->get_load_name()); + GMap<GUTF8String, void *> * parents=0; + if (ref_map.contains(child_id)) + parents=(GMap<GUTF8String, void *> *) ref_map[child_id]; + else + ref_map[child_id]=parents=new GMap<GUTF8String, void *>(); + (*parents)[id]=0; + // Second: go recursively + generate_ref_map(child_file, ref_map, visit_map); + } + } +} + +void +DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref, + GMap<GUTF8String, void *> & ref_map) + // Private function, which will remove file with ID id. + // + // If will also remove all INCL chunks in parent files pointing + // to this one + // + // Finally, if remove_unref is TRUE, we will go down the files + // hierarchy removing every file, which becomes unreferenced. + // + // ref_map will be used to find out list of parents referencing + // this file (required when removing INCL chunks) +{ + // First get rid of INCL chunks in parents + GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[id]; + if (parents) + { + for(GPosition pos=*parents;pos;++pos) + { + const GUTF8String parent_id((*parents).key(pos)); + const GP<DjVuFile> parent(get_djvu_file(parent_id)); + if (parent) + parent->unlink_file(id); + } + delete parents; + parents=0; + ref_map.del(id); + } + + // We will accumulate errors here. + GUTF8String errors; + + // Now modify the ref_map and process children if necessary + GP<DjVuFile> file=get_djvu_file(id); + if (file) + { + G_TRY { + GPList<DjVuFile> files_list=file->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVuFile> child_file=files_list[pos]; + GURL child_url=child_file->get_url(); + const GUTF8String child_id( + djvm_dir->name_to_file(child_url.fname())->get_load_name()); + GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[child_id]; + if (parents) parents->del(id); + + if (remove_unref && (!parents || !parents->size())) + remove_file(child_id, remove_unref, ref_map); + } + } G_CATCH(exc) { + if (errors.length()) errors+="\n\n"; + errors+=exc.get_cause(); + } G_ENDCATCH; + } + + // Finally remove this file from the directory. + djvm_dir->delete_file(id); + + // And get rid of its thumbnail, if any + GCriticalSectionLock lock(&thumb_lock); + GPosition pos(thumb_map.contains(id)); + if (pos) + { + thumb_map.del(pos); + } + if (errors.length()) + G_THROW(errors); +} + +void +DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref) +{ + DEBUG_MSG("DjVuDocEditor::remove_file(): id='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!djvm_dir->id_to_file(id)) + G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t"+id); + + // First generate a map of references (containing the list of parents + // including this particular file. This will speed things up + // significatly. + GMap<GUTF8String, void *> ref_map; // GMap<GUTF8String, GMap<GUTF8String, void *> *> in fact + GMap<GURL, void *> visit_map; // To avoid loops + + int pages_num=djvm_dir->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + generate_ref_map(get_djvu_file(page_num), ref_map, visit_map); + + // Now call the function, which will do the removal recursively + remove_file(id, remove_unref, ref_map); + + // And clear the ref_map + GPosition pos; + while((pos=ref_map)) + { + GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[pos]; + delete parents; + ref_map.del(pos); + } +} + +void +DjVuDocEditor::remove_page(int page_num, bool remove_unref) +{ + DEBUG_MSG("DjVuDocEditor::remove_page(): page_num=" << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + // Translate the page_num to ID + GP<DjVmDir> djvm_dir=get_djvm_dir(); + if (page_num<0 || page_num>=djvm_dir->get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + // And call general remove_file() + remove_file(djvm_dir->page_to_file(page_num)->get_load_name(), remove_unref); +} + +void +DjVuDocEditor::remove_pages(const GList<int> & page_list, bool remove_unref) +{ + DEBUG_MSG("DjVuDocEditor::remove_pages() called\n"); + DEBUG_MAKE_INDENT(3); + + // First we need to translate page numbers to IDs (they will + // obviously be changing while we're removing pages one after another) + GP<DjVmDir> djvm_dir=get_djvm_dir(); + GPosition pos ; + if (djvm_dir) + { + GList<GUTF8String> id_list; + for(pos=page_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]); + if (frec) + id_list.append(frec->get_load_name()); + } + + for(pos=id_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]); + if (frec) + remove_page(frec->get_page_num(), remove_unref); + } + } +} + +void +DjVuDocEditor::move_file(const GUTF8String &id, int & file_pos, + GMap<GUTF8String, void *> & map) + // NOTE! file_pos here is the desired position in DjVmDir *after* + // the record with ID 'id' is removed. +{ + if (!map.contains(id)) + { + map[id]=0; + + GP<DjVmDir::File> file_rec=djvm_dir->id_to_file(id); + if (file_rec) + { + file_rec=new DjVmDir::File(*file_rec); + djvm_dir->delete_file(id); + djvm_dir->insert_file(file_rec, file_pos); + + if (file_pos>=0) + { + file_pos++; + + // We care to move included files only if we do not append + // This is because the only reason why we move included + // files is to made them available sooner than they would + // be available if we didn't move them. By appending files + // we delay the moment when the data for the file becomes + // available, of course. + GP<DjVuFile> djvu_file=get_djvu_file(id); + if (djvu_file) + { + GPList<DjVuFile> files_list=djvu_file->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + { + const GUTF8String name(files_list[pos]->get_url().fname()); + GP<DjVmDir::File> child_frec=djvm_dir->name_to_file(name); + + // If the child is positioned in DjVmDir AFTER the + // file being processed (position is file_pos or greater), + // move it to file_pos position + if (child_frec) + if (djvm_dir->get_file_pos(child_frec)>file_pos) + move_file(child_frec->get_load_name(), file_pos, map); + } + } + } + } + } +} + +void +DjVuDocEditor::move_page(int page_num, int new_page_num) +{ + DEBUG_MSG("DjVuDocEditor::move_page(): page_num=" << page_num << + ", new_page_num=" << new_page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + if (page_num==new_page_num) return; + + int pages_num=get_pages_num(); + if (page_num<0 || page_num>=pages_num) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + const GUTF8String id(page_to_id(page_num)); + int file_pos=-1; + if (new_page_num>=0 && new_page_num<pages_num) + if (new_page_num>page_num) // Moving toward the end + { + if (new_page_num<pages_num-1) + file_pos=djvm_dir->get_page_pos(new_page_num+1)-1; + } else + file_pos=djvm_dir->get_page_pos(new_page_num); + + GMap<GUTF8String, void *> map; + move_file(id, file_pos, map); +} +#ifdef _WIN32_WCE_EMULATION // Work around odd behavior under WCE Emulation +#define CALLINGCONVENTION __cdecl +#else +#define CALLINGCONVENTION /* */ +#endif + +static int +CALLINGCONVENTION +cmp(const void * ptr1, const void * ptr2) +{ + int num1=*(int *) ptr1; + int num2=*(int *) ptr2; + return num1<num2 ? -1 : num1>num2 ? 1 : 0; +} + +static GList<int> +sortList(const GList<int> & list) +{ + GArray<int> a(list.size()-1); + int cnt; + GPosition pos; + for(pos=list, cnt=0;pos;++pos, cnt++) + a[cnt]=list[pos]; + + qsort((int *) a, a.size(), sizeof(int), cmp); + + GList<int> l; + for(int i=0;i<a.size();i++) + l.append(a[i]); + + return l; +} + +void +DjVuDocEditor::move_pages(const GList<int> & _page_list, int shift) +{ + if (!shift) return; + + GList<int> page_list=sortList(_page_list); + + GList<GUTF8String> id_list; + for(GPosition pos=page_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]); + if (frec) + id_list.append(frec->get_load_name()); + } + + if (shift<0) + { + // We have to start here from the smallest page number + // We will move it according to the 'shift', and all + // further moves are guaranteed not to affect its page number. + + // We will be changing the 'min_page' to make sure that + // pages moved beyond the document will still be in correct order + int min_page=0; + for(GPosition pos=id_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]); + if (frec) + { + int page_num=frec->get_page_num(); + int new_page_num=page_num+shift; + if (new_page_num<min_page) + new_page_num=min_page++; + move_page(page_num, new_page_num); + } + } + } else + { + // We have to start here from the biggest page number + // We will move it according to the 'shift', and all + // further moves will not affect its page number. + + // We will be changing the 'max_page' to make sure that + // pages moved beyond the document will still be in correct order + int max_page=djvm_dir->get_pages_num()-1; + for(GPosition pos=id_list.lastpos();pos;--pos) + { + GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]); + if (frec) + { + int page_num=frec->get_page_num(); + int new_page_num=page_num+shift; + if (new_page_num>max_page) + new_page_num=max_page--; + move_page(page_num, new_page_num); + } + } + } +} + +void +DjVuDocEditor::set_file_name(const GUTF8String &id, const GUTF8String &name) +{ + DEBUG_MSG("DjVuDocEditor::set_file_name(), id='" << id << "', name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + // It's important to get the URL now, because later (after we + // change DjVmDir) id_to_url() will be returning a modified value + GURL url=id_to_url(id); + + // Change DjVmDir. It will check if the name is unique + djvm_dir->set_file_name(id, name); + + // Now find DjVuFile (if any) and rename it + GPosition pos; + if (files_map.contains(id, pos)) + { + GP<File> file=files_map[pos]; + GP<DataPool> pool=file->pool; + if (pool) pool->load_file(); + GP<DjVuFile> djvu_file=file->file; + if (djvu_file) djvu_file->set_name(name); + } +} + +void +DjVuDocEditor::set_page_name(int page_num, const GUTF8String &name) +{ + DEBUG_MSG("DjVuDocEditor::set_page_name(), page_num='" << page_num << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (page_num<0 || page_num>=get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + set_file_name(page_to_id(page_num), name); +} + +void +DjVuDocEditor::set_file_title(const GUTF8String &id, const GUTF8String &title) +{ + DEBUG_MSG("DjVuDocEditor::set_file_title(), id='" << id << "', title='" << title << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Just change DjVmDir. It will check if the title is unique + djvm_dir->set_file_title(id, title); +} + +void +DjVuDocEditor::set_page_title(int page_num, const GUTF8String &title) +{ + DEBUG_MSG("DjVuDocEditor::set_page_title(), page_num='" << page_num << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (page_num<0 || page_num>=get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + set_file_title(page_to_id(page_num), title); +} + +//**************************************************************************** +//************************** Shared annotations ****************************** +//**************************************************************************** + +void +DjVuDocEditor::simplify_anno(void (* progress_cb)(float progress, void *), + void * cl_data) + // It's important that no decoding is done while this function + // is running. Otherwise the DjVuFile's decoding routines and + // this function may attempt to decode/modify a file's + // annotations at the same time. +{ + // Get the name of the SHARED_ANNO file. We will not + // touch that file (will not move annotations from it) + GP<DjVmDir::File> shared_file=djvm_dir->get_shared_anno_file(); + GUTF8String shared_id; + if (shared_file) + shared_id=shared_file->get_load_name(); + + GList<GURL> ignore_list; + if (shared_id.length()) + ignore_list.append(id_to_url(shared_id)); + + // First, for every page get merged (or "flatten" or "projected") + // annotations and store them inside the top-level page file + int pages_num=djvm_dir->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + GP<DjVuFile> djvu_file=get_djvu_file(page_num); + if (!djvu_file) + G_THROW( ERR_MSG("DjVuDocEditor.page_fail") "\t"+page_num); + int max_level=0; + GP<ByteStream> anno; + anno=djvu_file->get_merged_anno(ignore_list, &max_level); + if (anno && max_level>0) + { + // This is the moment when we try to modify DjVuFile's annotations + // Make sure, that it's not being decoded + GSafeFlags & file_flags=djvu_file->get_safe_flags(); + GMonitorLock lock(&file_flags); + while(file_flags & DjVuFile::DECODING) + file_flags.wait(); + + // Merge all chunks in one by decoding and encoding DjVuAnno + const GP<DjVuAnno> dec_anno(DjVuAnno::create()); + dec_anno->decode(anno); + const GP<ByteStream> new_anno(ByteStream::create()); + dec_anno->encode(new_anno); + new_anno->seek(0); + + // And store it in the file + djvu_file->anno=new_anno; + djvu_file->rebuild_data_pool(); + if ((file_flags & (DjVuFile::DECODE_OK | + DjVuFile::DECODE_FAILED | + DjVuFile::DECODE_STOPPED))==0) + djvu_file->anno=0; + } + if (progress_cb) + progress_cb((float)(page_num/2.0/pages_num), cl_data); + } + + // Now remove annotations from every file except for + // the top-level page files and SHARED_ANNO file. + // Unlink empty files too. + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + int cnt; + GPosition pos; + for(pos=files_list, cnt=0;pos;++pos, cnt++) + { + GP<DjVmDir::File> frec=files_list[pos]; + if (!frec->is_page() && frec->get_load_name()!=shared_id) + { + GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name()); + if (djvu_file) + { + djvu_file->remove_anno(); + if (djvu_file->get_chunks_number()==0) + remove_file(frec->get_load_name(), true); + } + } + if (progress_cb) + progress_cb((float)(0.5+cnt/2.0/files_list.size()), cl_data); + } +} + +void +DjVuDocEditor::create_shared_anno_file(void (* progress_cb)(float progress, void *), + void * cl_data) +{ + if (djvm_dir->get_shared_anno_file()) + G_THROW( ERR_MSG("DjVuDocEditor.share_fail") ); + + // Prepare file with ANTa chunk inside + const GP<ByteStream> gstr(ByteStream::create()); + const GP<IFFByteStream> giff(IFFByteStream::create(gstr)); + IFFByteStream &iff=*giff; + iff.put_chunk("FORM:DJVI"); + iff.put_chunk("ANTa"); + iff.close_chunk(); + iff.close_chunk(); + ByteStream &str=*gstr; + str.flush(); + str.seek(0); + const GP<DataPool> file_pool(DataPool::create(gstr)); + + // Get a unique ID for the new file + const GUTF8String id(find_unique_id("shared_anno.iff")); + + // Add it into the directory + GP<DjVmDir::File> frec(DjVmDir::File::create(id, id, id, + DjVmDir::File::SHARED_ANNO)); + djvm_dir->insert_file(frec, 1); + + // Add it to our "cache" + { + GP<File> f=new File; + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } + + // Now include this shared file into every top-level page file + int pages_num=djvm_dir->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + GP<DjVuFile> djvu_file=get_djvu_file(page_num); + djvu_file->insert_file(id, 1); + + if (progress_cb) + progress_cb((float) page_num/pages_num, cl_data); + } +} + +void +DjVuDocEditor::set_djvm_nav(GP<DjVmNav> n) +{ + if (n && ! n->isValidBookmark()) + G_THROW("Invalid bookmark data"); + djvm_nav = n; +} + +GP<DjVuFile> +DjVuDocEditor::get_shared_anno_file(void) +{ + GP<DjVuFile> djvu_file; + + GP<DjVmDir::File> frec=djvm_dir->get_shared_anno_file(); + if (frec) + djvu_file=get_djvu_file(frec->get_load_name()); + + return djvu_file; +} + +GP<DataPool> +DjVuDocEditor::get_thumbnail(int page_num, bool dont_decode) + // We override DjVuDocument::get_thumbnail() here because + // pages may have been shuffled and those "thumbnail file records" + // from the DjVmDir do not describe things correctly. + // + // So, first we will check the thumb_map[] if we have a predecoded + // thumbnail for the given page. If this is the case, we will + // return it. Otherwise we will ask DjVuDocument to generate + // this thumbnail for us. +{ + const GUTF8String id(page_to_id(page_num)); + + GCriticalSectionLock lock(&thumb_lock); + const GPosition pos(thumb_map.contains(id)); + if (pos) + { + // Get the image from the map + return thumb_map[pos]; + } else + { + unfile_thumbnails(); + return DjVuDocument::get_thumbnail(page_num, dont_decode); + } +} + +int +DjVuDocEditor::get_thumbnails_num(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &thumb_lock); + + int cnt=0; + int pages_num=get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + if (thumb_map.contains(page_to_id(page_num))) + cnt++; + } + return cnt; +} + +int +DjVuDocEditor::get_thumbnails_size(void) const +{ + DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &thumb_lock); + + int pages_num=get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + const GPosition pos(thumb_map.contains(page_to_id(page_num))); + if (pos) + { + const GP<ByteStream> gstr(thumb_map[pos]->get_stream()); + GP<IW44Image> iwpix=IW44Image::create_decode(IW44Image::COLOR); + iwpix->decode_chunk(gstr); + + int width=iwpix->get_width(); + int height=iwpix->get_height(); + return width<height ? width : height; + } + } + return -1; +} + +void +DjVuDocEditor::remove_thumbnails(void) +{ + DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n"); + DEBUG_MAKE_INDENT(3); + + unfile_thumbnails(); + + DEBUG_MSG("clearing thumb_map\n"); + GCriticalSectionLock lock(&thumb_lock); + thumb_map.empty(); +} + +void +DjVuDocEditor::unfile_thumbnails(void) + // Will erase all "THUMBNAILS" files from DjVmDir. + // This function is useful when filing thumbnails (to get rid of + // those files, which currently exist: they need to be replaced + // anyway) and when calling DjVuDocument::get_thumbnail() to + // be sure, that it will not use wrong information from DjVmDir +{ + DEBUG_MSG("DjVuDocEditor::unfile_thumbnails(): updating DjVmDir\n"); + DEBUG_MAKE_INDENT(3); + + { + GCriticalSectionLock lock(&threqs_lock); + threqs_list.empty(); + } + if((const DjVmDir *)djvm_dir) + { + GPList<DjVmDir::File> xfiles_list=djvm_dir->get_files_list(); + for(GPosition pos=xfiles_list;pos;++pos) + { + GP<DjVmDir::File> f=xfiles_list[pos]; + if (f->is_thumbnails()) + djvm_dir->delete_file(f->get_load_name()); + } + } +} + +void +DjVuDocEditor::file_thumbnails(void) + // The purpose of this function is to create files containing + // thumbnail images and register them in DjVmDir. + // If some of the thumbnail images are missing, they'll + // be generated with generate_thumbnails() +{ + DEBUG_MSG("DjVuDocEditor::file_thumbnails(): updating DjVmDir\n"); + DEBUG_MAKE_INDENT(3); + unfile_thumbnails(); + + // Generate thumbnails if they're missing due to some reason. + int thumb_num=get_thumbnails_num(); + int size=thumb_num>0 ? get_thumbnails_size() : 128; + if (thumb_num!=get_pages_num()) + { + generate_thumbnails(size); + } + + DEBUG_MSG("filing thumbnails\n"); + + GCriticalSectionLock lock(&thumb_lock); + + // The first thumbnail file always contains only one thumbnail + int ipf=1; + int image_num=0; + int page_num=0, pages_num=djvm_dir->get_pages_num(); + GP<ByteStream> str(ByteStream::create()); + GP<IFFByteStream> iff(IFFByteStream::create(str)); + iff->put_chunk("FORM:THUM"); + for(;;) + { + GUTF8String id(page_to_id(page_num)); + const GPosition pos(thumb_map.contains(id)); + if (! pos) + { + G_THROW( ERR_MSG("DjVuDocEditor.no_thumb") "\t"+GUTF8String(page_num)); + } + iff->put_chunk("TH44"); + iff->copy(*(thumb_map[pos]->get_stream())); + iff->close_chunk(); + image_num++; + page_num++; + if (image_num>=ipf || page_num>=pages_num) + { + int i=id.rsearch('.'); + if(i<=0) + { + i=id.length(); + } + id=id.substr(0,i)+".thumb"; + // Get unique ID for this file + id=find_unique_id(id); + + // Create a file record with the chosen ID + GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id, + DjVmDir::File::THUMBNAILS)); + + // Set correct file position (so that it will cover the next + // ipf pages) + int file_pos=djvm_dir->get_page_pos(page_num-image_num); + djvm_dir->insert_file(file, file_pos); + + // Now add the File record (containing the file URL and DataPool) + // After we do it a simple save_as() will save the document + // with the thumbnails. This is because DjVuDocument will see + // the file in DjVmDir and will ask for data. We will intercept + // the request for data and will provide this DataPool + iff->close_chunk(); + str->seek(0); + const GP<DataPool> file_pool(DataPool::create(str)); + GP<File> f=new File; + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + + // And create new streams + str=ByteStream::create(); + iff=IFFByteStream::create(str); + iff->put_chunk("FORM:THUM"); + image_num=0; + + // Reset ipf to correct value (after we stored first + // "exceptional" file with thumbnail for the first page) + if (page_num==1) ipf=thumbnails_per_file; + if (page_num>=pages_num) break; + } + } +} + +int +DjVuDocEditor::generate_thumbnails(int thumb_size, int page_num) +{ + DEBUG_MSG("DjVuDocEditor::generate_thumbnails(): doing it\n"); + DEBUG_MAKE_INDENT(3); + + if(page_num<(djvm_dir->get_pages_num())) + { + const GUTF8String id(page_to_id(page_num)); + if (!thumb_map.contains(id)) + { + const GP<DjVuImage> dimg(get_page(page_num, true)); + + GRect rect(0, 0, thumb_size, dimg->get_height()*thumb_size/dimg->get_width()); + GP<GPixmap> pm=dimg->get_pixmap(rect, rect, get_thumbnails_gamma()); + if (!pm) + { + const GP<GBitmap> bm(dimg->get_bitmap(rect, rect, sizeof(int))); + if (bm) + pm = GPixmap::create(*bm); + else + pm = GPixmap::create(rect.height(), rect.width(), &GPixel::WHITE); + } + // Store and compress the pixmap + const GP<IW44Image> iwpix(IW44Image::create_encode(*pm)); + const GP<ByteStream> gstr(ByteStream::create()); + IWEncoderParms parms; + parms.slices=97; + parms.bytes=0; + parms.decibels=0; + iwpix->encode_chunk(gstr, parms); + gstr->seek(0L); + thumb_map[id]=DataPool::create(gstr); + } + ++page_num; + } + else + { + page_num = -1; + } + return page_num; +} + +void +DjVuDocEditor::generate_thumbnails(int thumb_size, + bool (* cb)(int page_num, void *), + void * cl_data) +{ + int page_num=0; + do + { + page_num=generate_thumbnails(thumb_size,page_num); + if (cb) if (cb(page_num, cl_data)) return; + } while(page_num>=0); +} + +static void +store_file(const GP<DjVmDir> & src_djvm_dir, const GP<DjVmDoc> & djvm_doc, + GP<DjVuFile> & djvu_file, GMap<GURL, void *> & map) +{ + GURL url=djvu_file->get_url(); + if (!map.contains(url)) + { + map[url]=0; + + // Store included files first + GPList<DjVuFile> djvu_files_list=djvu_file->get_included_files(false); + for(GPosition pos=djvu_files_list;pos;++pos) + store_file(src_djvm_dir, djvm_doc, djvu_files_list[pos], map); + + // Now store contents of this file + GP<DataPool> file_data=djvu_file->get_djvu_data(false); + GP<DjVmDir::File> frec=src_djvm_dir->name_to_file(url.name()); + if (frec) + { + frec=new DjVmDir::File(*frec); + djvm_doc->insert_file(frec, file_data, -1); + } + } +} + +void +DjVuDocEditor::save_pages_as( + const GP<ByteStream> &str, const GList<int> & _page_list) +{ + GList<int> page_list=sortList(_page_list); + + GP<DjVmDoc> djvm_doc=DjVmDoc::create(); + GMap<GURL, void *> map; + for(GPosition pos=page_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]); + if (frec) + { + GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name()); + if (djvu_file) + store_file(djvm_dir, djvm_doc, djvu_file, map); + } + } + djvm_doc->write(str); +} + +void +DjVuDocEditor::save_file(const GUTF8String &file_id, const GURL &codebase, + const bool only_modified, GMap<GUTF8String,GUTF8String> & map) +{ + if(only_modified) + { + for(GPosition pos=files_map;pos;++pos) + { + const GP<File> file_rec(files_map[pos]); + const bool file_modified=file_rec->pool || + (file_rec->file && file_rec->file->is_modified()); + if(!file_modified) + { + const GUTF8String id=files_map.key(pos); + const GUTF8String save_name(djvm_dir->id_to_file(id)->get_save_name()); + if(id == save_name) + { + map[id]=id; + } + } + } + } + save_file(file_id,codebase,map); +} + +void +DjVuDocEditor::save_file( + const GUTF8String &file_id, const GURL &codebase, + GMap<GUTF8String,GUTF8String> & map) +{ + DEBUG_MSG("DjVuDocEditor::save_file(): ID='" << file_id << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!map.contains(file_id)) + { + const GP<DjVmDir::File> file(djvm_dir->id_to_file(file_id)); + + GP<DataPool> file_pool; + const GPosition pos(files_map.contains(file_id)); + if (pos) + { + const GP<File> file_rec(files_map[pos]); + if (file_rec->file) + file_pool=file_rec->file->get_djvu_data(false); + else + file_pool=file_rec->pool; + } + + if (!file_pool) + { + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + file_pool=pcaster->request_data(this, id_to_url(file_id)); + } + + if (file_pool) + { + GMap<GUTF8String,GUTF8String> incl; + map[file_id]=get_djvm_doc()->save_file(codebase,*file,incl,file_pool); + for(GPosition pos=incl;pos;++pos) + { + save_file(incl.key(pos),codebase ,map); + } + }else + { + map[file_id]=file->get_save_name(); + } + } +} + +void +DjVuDocEditor::save(void) +{ + DEBUG_MSG("DjVuDocEditor::save(): saving the file\n"); + DEBUG_MAKE_INDENT(3); + + if (!can_be_saved()) + G_THROW( ERR_MSG("DjVuDocEditor.cant_save") ); + save_as(GURL(), orig_doc_type!=INDIRECT); +} + +void +DjVuDocEditor::write(const GP<ByteStream> &gbs, bool force_djvm) +{ + DEBUG_MSG("DjVuDocEditor::write()\n"); + DEBUG_MAKE_INDENT(3); + if (get_thumbnails_num()==get_pages_num()) + { + file_thumbnails(); + }else + { + remove_thumbnails(); + } + clean_files_map(); + DjVuDocument::write(gbs,force_djvm); +} + +void +DjVuDocEditor::write( + const GP<ByteStream> &gbs,const GMap<GUTF8String,void *> &reserved) +{ + DEBUG_MSG("DjVuDocEditor::write()\n"); + DEBUG_MAKE_INDENT(3); + if (get_thumbnails_num()==get_pages_num()) + { + file_thumbnails(); + }else + { + remove_thumbnails(); + } + clean_files_map(); + DjVuDocument::write(gbs,reserved); +} + +void +DjVuDocEditor::save_as(const GURL &where, bool bundled) +{ + DEBUG_MSG("DjVuDocEditor::save_as(): where='" << where << "'\n"); + DEBUG_MAKE_INDENT(3); + + // First see if we need to generate (or just reshuffle) thumbnails... + // If we have an icon for every page, we will just call + // file_thumbnails(), which will update DjVmDir and will create + // the actual bundles with thumbnails (very fast) + // Otherwise we will remove the thumbnails completely because + // we really don't want to deal with documents, which have only + // some of their pages thumbnailed. + if (get_thumbnails_num()==get_pages_num()) + { + file_thumbnails(); + }else + { + remove_thumbnails(); + } + + GURL save_doc_url; + + if (where.is_empty()) + { + // Assume, that we just want to 'save'. Check, that it's possible + // and proceed. + bool can_be_saved_bundled=orig_doc_type==BUNDLED || + orig_doc_type==OLD_BUNDLED || + orig_doc_type==SINGLE_PAGE || + orig_doc_type==OLD_INDEXED && orig_doc_pages==1; + if ((bundled ^ can_be_saved_bundled)!=0) + G_THROW( ERR_MSG("DjVuDocEditor.cant_save2") ); + save_doc_url=doc_url; + } else + { + save_doc_url=where; + } + + int save_doc_type=bundled ? BUNDLED : INDIRECT; + + clean_files_map(); + + GCriticalSectionLock lock(&files_lock); + + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + + // First consider saving in SINGLE_FILE format (one file) + if(needs_compression()) + { + DEBUG_MSG("Compressing on output\n"); + remove_thumbnails(); + if(! djvu_compress_codec) + { + G_THROW( ERR_MSG("DjVuDocEditor.no_codec") ); + } + const GP<DjVmDoc> doc(get_djvm_doc()); + GP<ByteStream> mbs(ByteStream::create()); + doc->write(mbs); + mbs->flush(); + mbs->seek(0,SEEK_SET); + djvu_compress_codec(mbs,save_doc_url,(!(const DjVmDir *)djvm_dir)||(djvm_dir->get_files_num()==1)||(save_doc_type!=INDIRECT)); + files_map.empty(); + doc_url=GURL(); + }else + { + if (djvm_dir->get_files_num()==1) + { + // Here 'bundled' has no effect: we will save it as one page. + DEBUG_MSG("saving one file...\n"); + GURL file_url=page_to_url(0); + const GUTF8String file_id(djvm_dir->page_to_file(0)->get_load_name()); + GP<DataPool> file_pool; + GPosition pos=files_map.contains(file_id); + if (pos) + { + const GP<File> file_rec(files_map[pos]); + if (file_rec->pool && (!file_rec->file || + !file_rec->file->is_modified())) + { + file_pool=file_rec->pool; + }else if (file_rec->file) + { + file_pool=file_rec->file->get_djvu_data(false); + } + } + // Even if file has not been modified (pool==0) we still want + // to save it. + if (!file_pool) + file_pool=pcaster->request_data(this, file_url); + if (file_pool) + { + DEBUG_MSG("Saving '" << file_url << "' to '" << save_doc_url << "'\n"); + DataPool::load_file(save_doc_url); + const GP<ByteStream> gstr_out(ByteStream::create(save_doc_url, "wb")); + ByteStream &str_out=*gstr_out; + str_out.writall(octets, 4); + const GP<ByteStream> str_in(file_pool->get_stream()); + str_out.copy(*str_in); + } + + // Update the document's DataPool (to save memory) + const GP<DjVmDoc> doc(get_djvm_doc()); + const GP<ByteStream> gstr=ByteStream::create();// One page: we can do it in the memory + doc->write(gstr); + gstr->seek(0, SEEK_SET); + const GP<DataPool> pool(DataPool::create(gstr)); + doc_pool=pool; + init_data_pool=pool; + + // Also update DjVmDir (to reflect changes in offsets) + djvm_dir=doc->get_djvm_dir(); + } else if (save_doc_type==INDIRECT) + { + DEBUG_MSG("Saving in INDIRECT format to '" << save_doc_url << "'\n"); + bool save_only_modified=!(save_doc_url!=doc_url || save_doc_type!=orig_doc_type); + GPList<DjVmDir::File> xfiles_list=djvm_dir->resolve_duplicates(false); + const GURL codebase=save_doc_url.base(); + int pages_num=djvm_dir->get_pages_num(); + GMap<GUTF8String, GUTF8String> map; + // First go thru the pages + for(int page_num=0;page_num<pages_num;page_num++) + { + const GUTF8String id(djvm_dir->page_to_file(page_num)->get_load_name()); + save_file(id, codebase, save_only_modified, map); + } + // Next go thru thumbnails and similar stuff + GPosition pos; + for(pos=xfiles_list;pos;++pos) + save_file(xfiles_list[pos]->get_load_name(), codebase, save_only_modified, map); + + // Finally - save the top-level index file + for(pos=xfiles_list;pos;++pos) + { + const GP<DjVmDir::File> file(xfiles_list[pos]); + file->offset=0; + file->size=0; + } + DataPool::load_file(save_doc_url); + const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb")); + const GP<IFFByteStream> giff(IFFByteStream::create(gstr)); + IFFByteStream &iff=*giff; + + iff.put_chunk("FORM:DJVM", 1); + iff.put_chunk("DIRM"); + djvm_dir->encode(giff->get_bytestream()); + iff.close_chunk(); + iff.close_chunk(); + iff.flush(); + + // Update the document data pool (not required, but will save memory) + doc_pool=DataPool::create(save_doc_url); + init_data_pool=doc_pool; + + // No reason to update DjVmDir as for this format it doesn't + // contain DJVM offsets + } else if (save_doc_type==BUNDLED || save_doc_type==OLD_BUNDLED) + { + DEBUG_MSG("Saving in BUNDLED format to '" << save_doc_url << "'\n"); + + // Can't be very smart here. Simply overwrite the file. + const GP<DjVmDoc> doc(get_djvm_doc()); + DataPool::load_file(save_doc_url); + const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb")); + doc->write(gstr); + gstr->flush(); + + // Update the document data pool (not required, but will save memory) + doc_pool=DataPool::create(save_doc_url); + init_data_pool=doc_pool; + + // Also update DjVmDir (to reflect changes in offsets) + djvm_dir=doc->get_djvm_dir(); + } else + { + G_THROW( ERR_MSG("DjVuDocEditor.cant_save") ); + } + + // Now, after we have saved the document w/o any error, detach DataPools, + // which are in the 'File's list to save memory. Detach everything. + // Even in the case when File->file is non-zero. If File->file is zero, + // remove the item from the list at all. If it's non-zero, it has + // to stay there because by definition files_map[] contains the list + // of all active files and customized DataPools + // + // In addition to it, look thru all active files and change their URLs + // to reflect changes in the document's URL (if there was a change) + // Another reason why file's URLs must be changed is that we may have + // saved the document in a different format, which changes the rules + // of file url composition. + for(GPosition pos=files_map;pos;) + { + const GP<File> file_rec(files_map[pos]); + file_rec->pool=0; + if (file_rec->file==0) + { + GPosition this_pos=pos; + ++pos; + files_map.del(this_pos); + } else + { + // Change the file's url; + if (doc_url!=save_doc_url || + orig_doc_type!=save_doc_type) + if (save_doc_type==BUNDLED) + file_rec->file->move(save_doc_url); + else file_rec->file->move(save_doc_url.base()); + ++pos; + } + } + + } + orig_doc_type=save_doc_type; + doc_type=save_doc_type; + + if (doc_url!=save_doc_url) + { + // Also update document's URL (we moved, didn't we?) + doc_url=save_doc_url; + init_url=save_doc_url; + } +} + +GP<DjVuDocEditor> +DjVuDocEditor::create_wait(void) +{ + DjVuDocEditor *doc=new DjVuDocEditor(); + const GP<DjVuDocEditor> retval(doc); + doc->init(); + return retval; +} + +GP<DjVuDocEditor> +DjVuDocEditor::create_wait(const GURL &url) +{ + DjVuDocEditor *doc=new DjVuDocEditor(); + const GP<DjVuDocEditor> retval(doc); + doc->init(url); + return retval; +} + +bool +DjVuDocEditor::inherits(const GUTF8String &class_name) const +{ + return (class_name == "DjVuDocEditor")||DjVuDocument::inherits(class_name); +} + +int +DjVuDocEditor::get_orig_doc_type(void) const +{ + return orig_doc_type; +} + +bool +DjVuDocEditor::can_be_saved(void) const +{ + return !(needs_rename()||needs_compression()||orig_doc_type==UNKNOWN_TYPE || + orig_doc_type==OLD_INDEXED); +} + +int +DjVuDocEditor::get_save_doc_type(void) const +{ + if (orig_doc_type==SINGLE_PAGE) + if (djvm_dir->get_files_num()==1) + return SINGLE_PAGE; + else + return BUNDLED; + else if (orig_doc_type==INDIRECT) + return INDIRECT; + else if (orig_doc_type==OLD_BUNDLED || orig_doc_type==BUNDLED) + return BUNDLED; + else + return UNKNOWN_TYPE; +} + +GURL +DjVuDocEditor::get_doc_url(void) const +{ + return doc_url.is_empty() ? init_url : doc_url; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.h b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.h new file mode 100644 index 00000000..7bf6124a --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.h @@ -0,0 +1,460 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocEditor.h,v 1.9 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUDOCEDITOR_H +#define _DJVUDOCEDITOR_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuDocument.h" +#include "DjVmDoc.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name DjVuDocEditor.h + Files #"DjVuDocEditor.h"# and #"DjVuDocEditor.cpp"# contain extension + of \Ref{DjVuDocument} class, which can create and modify existing + DjVu document, generate thumbnails, etc. It does {\bf not} do + compression though. + + @memo DjVu document editor class. + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DjVuDocEditor.h,v 1.9 2005/05/25 20:24:52 leonb Exp $# +*/ + +//@{ + +/** #DjVuDocEditor# is an extension of \Ref{DjVuDocument} class with + additional capabilities for editing the document contents. + + It can be used to: + \begin{enumerate} + \item Create (compose) new multipage DjVu documents using single + page DjVu documents. The class does {\bf not} do compression. + \item Insert and remove different pages of multipage DjVu documents. + \item Change attributes ({\em names}, {\em IDs} and {\em titles}) + of files composing the DjVu document. + \item Generate thumbnail images and integrate them into the document. + \end{enumerate} +*/ + +class DjVuDocEditor : public DjVuDocument +{ +public: + static int thumbnails_per_file; + +protected: + /// Default constructor + DjVuDocEditor(void); + + /** Initialization function. Initializes an empty document. + + {\bf Note}: You must call either of the two + available \Ref{init}() function before you start doing + anything else with the #DjVuDocEditor#. */ + void init(void); + + /** Initialization function. Opens document with name #filename#. + + {\bf Note}: You must call either of the two + available \Ref{init}() function before you start doing + anything else with the #DjVuDocEditor#. */ + void init(const GURL &url); + +public: + /** Creates a DjVuDocEditor class and initializes with #fname#. */ + static GP<DjVuDocEditor> create_wait(const GURL &url); + + /** Creates a DjVuDocEditor class and initializes an empty document. */ + static GP<DjVuDocEditor> create_wait(void); + + + /// Destructor + virtual ~DjVuDocEditor(void); + + /** Returns type of open document. #DjVuDocEditor# silently + converts any open DjVu document to #BUNDLED# format (see + \Ref{DjVuDocument}. Thus, \Ref{DjVuDocument::get_doc_type}() + will always be returning #BUNDLED#. Use this function to + learn the original format of the document being edited. */ + int get_orig_doc_type(void) const; + + /** Returns #TRUE# if the document can be "saved" (sometimes + the only possibility is to do a "save as"). The reason why + we have this function is that #DjVuDocEditor# can save + documents in new formats only (#BUNDLED# and #INDIRECT#). + At the same time it recognizes all DjVu formats (#OLD_BUNDLED#, + #OLD_INDEXED#, #BUNDLED#, and #INDIRECT#). + + #OLD_BUNDLED# and #BUNDLED# documents occupy only one file, + so in this case "saving" involves the automatic conversion + to #BUNDLED# format and storing data into the same file. + + #OLD_INDEXED# documents, on the other hand, occupy more + than one file. They could be converted to #INDIRECT# format + if these two formats had the same set of files. Unfortunately, + these formats are too different, and the best thing to do + is to use "save as" capability. */ + bool can_be_saved(void) const; + + /** Returns type of the document, which can be created by + \Ref{save}() function. Can be #INDIRECT#, #BUNDLED#, + #SINGLE_PAGE#, or #UNKNOWN_TYPE#. The latter indicates, + that \Ref{save}() will fail, and that \Ref{save_as}() + should be used instead */ + int get_save_doc_type(void) const; + + /** Saves the document. May generate exception if the document + can not be saved, and \Ref{save_as}() should be used. + See \Ref{can_be_saved}() for details. */ + void save(void); + + /** Saves the document. */ + virtual void save_as(const GURL &where, bool bundled); + + /** Saves the document in the {\em new bundled} format. All the data + is "bundled" into one file and this file is written into the + passed stream. + + If #force_djvm# is #TRUE# then even one page documents will be + saved in the #DJVM BUNDLED# format (inside a #FORM:DJVM#); + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. */ + virtual void write(const GP<ByteStream> &str, bool force_djvm=false); + /** Always save as bundled, renaming any files conflicting with the + the names in the supplied GMap. */ + virtual void write(const GP<ByteStream> &str, + const GMap<GUTF8String,void *> &reserved); + + /** Saves the specified pages in DjVu #BUNDLED# multipage document. */ + void save_pages_as( + const GP<ByteStream> &str, const GList<int> & page_list); + + /** Translates page number #page_num# to ID. If #page_num# is invalid, + an exception is thrown. */ + GUTF8String page_to_id(int page_num) const; + + GUTF8String insert_file(const GURL &url, const GUTF8String &parent_id, + int chunk_num=1, DjVuPort *source=0); + /** Inserts the referenced file into this DjVu document. + + @param fname Name of the top-level file containing the image of + the page to be inserted. This file must be a DjVu file and + may include one or more other DjVu files. + + If it include other DjVu files, the function will try to + insert them into the document too. Should this attempt fail, + the corresponding #INCL# chunk will be removed from the + referencing file and an exception will be thrown. + + When inserting a file, the function may modify its name + to be unique in the DjVu document. + @param page_num Position where the new page should be inserted at. + Negative value means "append" */ + void insert_page(const GURL &fname, int page_num=-1); + /** Inserts a new page with data inside the #data_pool# as page + number #page_num. + + @param data_pool \Ref{DataPool} with data for this page. + @param file_name Name, which will be assigned to this page. + If you try to save the document in #INDIRECT# format, + a file with this name will be created to hold the + page's data. If there is already a file in the document + with the same name, the function will derive a new + unique name from file_name, which will be assigned + to the page. + @param page_num Describes where the page should be inserted. + Negative number means "append". */ + void insert_page(GP<DataPool> & file_pool, + const GURL &fname, int page_num=-1); + /** Inserts a group of pages into this DjVu document. + + Like \Ref{insert_page}() it will insert every page into the document. + The main advantage of calling this function once for the whole + group instead of calling \Ref{insert_page}() for every page is + the processing of included files: + + The group of files may include one or more files, which are thus + shared by them. If you call \Ref{insert_page}() for every page, + this shared file will be inserted into the document more than once + though under different names. This is how \Ref{insert_page}() works: + whenever it inserts something, it checks for duplicate names with + only one purpose: invent a new name if a given one is already in + use. + + On the other hand, if you call #insert_group#(), it will insert + shared included files only once. This is because it can analyze + the group of files before inserting them and figure out what files + are shared and thus should be inserted only once. + + @param fname_list List of top-level files for the pages to be inserted + @param page_num Position where the new pages should be inserted at. + Negative value means "append" */ + void insert_group(const GList<GURL> & furl_list, int page_num=-1, + void (* refresh_cb)(void *)=0, void * cl_data=0); + /** Removes the specified page from the document. If #remove_unref# + is #TRUE#, the function will also remove from the document any file, + which became unreferenced due to the page's removal */ + void remove_page(int page_num, bool remove_unref=true); + /** Removes the specified pages from the document. If #remove_unref# + is #TRUE#, the function will also remove from the document any file, + which became unreferenced due to the pages' removal */ + void remove_pages(const GList<int> & page_list, bool remove_unref=true); + /** Removes a DjVu file with the specified #id#. + + If some other files include this file, the corresponding #INCL# + chunks will be removed to avoid dead links. + + If #remove_unref# is #TRUE#, the function will also remove every + file, which will become unreferenced after the removal of this file. */ + void remove_file(const GUTF8String &id, bool remove_unref=true); + /** Makes page number #page_num# to be #new_page_num#. If #new_page_num# + is negative or too big, the function will move page #page_num# to + the end of the document. */ + void move_page(int page_num, int new_page_num); + /** Shifts all pags from the #page_list# according to the #shift#. + The #shift# can be positive (shift toward the end of the document) + or negative (shift toward the beginning of the document). + + It is OK to make #shift# too big in value. Pages will just be + moved to the end (or to the beginning, depending on the #shift# + sign) of the document. */ + void move_pages(const GList<int> & page_list, int shift); + + /** Changes the name of the file with ID #id# to #name#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_file_name(const GUTF8String &id, const GUTF8String &name); + /** Changes the name of the page #page_num# to #name#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_page_name(int page_num, const GUTF8String &name); + /** Changes the title of the file with ID #id# to #title#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_file_title(const GUTF8String &id, const GUTF8String &title); + /** Changes the title of the page #page_num# to #title#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_page_title(int page_num, const GUTF8String &title); + + /** @name Thumbnails */ + //@{ + /** Returns the number of thumbnails stored inside this document. + + It may be #ZERO#, which means, that there are no thumbnails at all. + + It may be equal to the number of pages, which is what should + normally be. + + Finally, it may be greater than #ZERO# and less than the number + of pages, in which case thumbnails should be regenerated before + the document can be saved. */ + int get_thumbnails_num(void) const; + + /** Returns the size of the first encountered thumbnail image. Since + thumbnails can currently be generated by \Ref{generate_thumbnails}() + only, all thumbnail images should be of the same size. Thus, + the number returned is actually the size of {\em all} + document thumbnails. + + The function will return #-1# if there are no thumbnails. */ + int get_thumbnails_size(void) const; + + /** Removes all thumbnails from the document */ + void remove_thumbnails(void); + + /** Generates thumbnails for the specified page, if and only if + it does not have a thumbnail yet. If you want to regenerate + thumbnails for all pages, call \Ref{remove_thumbnails}() prior + to calling this function. + + @param thumb_size The size of the thumbnails in pixels. DjVu viewer + is able to rescale the thumbnail images if necessary, so this + parameter affects thumbnails quality only. 128 is a good number. + @param page_num The page number to genate the thumbnail for. */ + int generate_thumbnails(int thumb_size, int page_num); + + /** Generates thumbnails for those pages, which do not have them yet. + If you want to regenerate thumbnails for all pages, call + \Ref{remove_thumbnails}() prior to calling this function. + + @param thumb_size The size of the thumbnails in pixels. DjVu viewer + is able to rescale the thumbnail images if necessary, so this + parameter affects thumbnails quality only. 128 is a good number. + @param cb The callback, which will be called after thumbnail image + for the next page has been generated. Regardless of if + the document already has thumbnail images for some of its + pages, the callback will be called #pages_num# times, where + #pages_num# is the total number of pages in the document. + The callback should return #FALSE# if thumbnails generating + should proceed. #TRUE# will stop it. */ + void generate_thumbnails(int thumb_size, + bool (* cb)(int page_num, void *)=0, + void * cl_data=0); + //@} + /** Use this function to simplify annotations in the document. + The "simplified" format is when annotations are only allowed + either in top-level page files or in a special file with + #SHARED_ANNO# flag on. This file is supposed to be included into + every page. */ + void simplify_anno(void (* progress_cb)(float progress, void *)=0, + void * cl_data=0); + + /** Will create a file that will be included into every page and + marked with the #SHARED_ANNO# flag. This file can be used + to store global annotations (annotations applicable to every page). + + {\bf Note:} There may be only one #SHARED_ANNO# file in any + DjVu multipage document. */ + void create_shared_anno_file(void (* progress_cb)(float progress, void *)=0, + void * cl_data=0); + + /** Sets bookmark data */ + void set_djvm_nav(GP<DjVmNav> nav); + + /** Returns a pointer to the file with #SHARED_ANNO# flag on. + This file should be used for storing document-wide annotations. + + {\bf Note:} There may be only one #SHARED_ANNO# file in any + DjVu multipage document. */ + GP<DjVuFile> get_shared_anno_file(void); + + GURL get_doc_url(void) const; + + /** Returns TRUE if #class_name# is #"DjVuDocEditor"#, + #"DjVuDocument"# or #"DjVuPort"# */ + virtual bool inherits(const GUTF8String &class_name) const; + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); +protected: + virtual GP<DjVuFile> url_to_file(const GURL & url, bool dont_create) const; + virtual GP<DataPool> get_thumbnail(int page_num, bool dont_decode); + friend class CThumbNails; +public: + class File; +private: + bool initialized; + GURL doc_url; + GP<DataPool> doc_pool; + GURL tmp_doc_url; + int orig_doc_type; + int orig_doc_pages; + + GPMap<GUTF8String, File> files_map; // files_map[id]=GP<File> + GCriticalSection files_lock; + + GPMap<GUTF8String,DataPool> thumb_map; + GCriticalSection thumb_lock; + + void (* refresh_cb)(void *); + void * refresh_cl_data; + + void check(void); + GUTF8String find_unique_id(GUTF8String id); + GP<DataPool> strip_incl_chunks(const GP<DataPool> & pool); + void clean_files_map(void); + bool insert_file_type(const GURL &file_url, + DjVmDir::File::FILE_TYPE page_type, + int & file_pos, + GMap<GUTF8String, GUTF8String> & name2id); + bool insert_file( const GP<DataPool> &pool, + const GURL &file_url, bool is_page, + int & file_pos, + GMap<GUTF8String,GUTF8String> & name2id, + DjVuPort *source=0 ); + bool insert_file( + const GURL &file_url, bool is_page, + int & file_pos, + GMap<GUTF8String,GUTF8String> & name2id, + DjVuPort *source=0 ); + void remove_file(const GUTF8String &id, bool remove_unref, + GMap<GUTF8String, void *> & ref_map); + void generate_ref_map(const GP<DjVuFile> & file, + GMap<GUTF8String, void *> & ref_map, + GMap<GURL, void *> & visit_map); + void move_file(const GUTF8String &id, int & file_pos, + GMap<GUTF8String, void *> & map); + void unfile_thumbnails(void); + void file_thumbnails(void); + void save_file(const GUTF8String &id, const GURL &codebase, + const bool only_modified, GMap<GUTF8String, GUTF8String> & map); + void save_file(const GUTF8String &id, const GURL &codebase, + GMap<GUTF8String, GUTF8String> & map); +}; + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocument.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.cpp new file mode 100644 index 00000000..3b33d943 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.cpp @@ -0,0 +1,1845 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocument.cpp,v 1.13 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuDocument.h" +#include "DjVmDoc.h" +#include "DjVmDir0.h" +#include "DjVmNav.h" +#include "DjVuNavDir.h" +#include "DjVuImage.h" +#include "DjVuFileCache.h" +#include "IFFByteStream.h" +#include "GOS.h" +#include "DataPool.h" +#include "IW44Image.h" +#include "GRect.h" + +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const char octets[4]={0x41,0x54,0x26,0x54}; +const float DjVuDocument::thumb_gamma=(float)2.20; + +void (* DjVuDocument::djvu_import_codec)( + GP<DataPool> &pool, const GURL &url, bool &needs_compression, + bool &needs_rename )=0; + +void (* DjVuDocument::djvu_compress_codec)( + GP<ByteStream> &doc,const GURL &where,bool bundled)=0; + +void +DjVuDocument::set_import_codec( + void (*codec)( + GP<DataPool> &pool, const GURL &url, bool &needs_compression, bool &needs_rename )) +{ + djvu_import_codec=codec; +} + +void +DjVuDocument::set_compress_codec( + void (* codec)( + GP<ByteStream> &doc,const GURL &where,bool bundled)) +{ + djvu_compress_codec=codec; +} + +DjVuDocument::DjVuDocument(void) + : doc_type(UNKNOWN_TYPE), + needs_compression_flag(false), + can_compress_flag(false), + needs_rename_flag(false), + has_url_names(false), + recover_errors(ABORT), + verbose_eof(false), + init_started(false), + cache(0) +{ +} + +GP<DjVuDocument> +DjVuDocument::create( + GP<DataPool> pool, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + DjVuDocument *doc=new DjVuDocument; + GP<DjVuDocument> retval=doc; + doc->init_data_pool=pool; + doc->start_init(GURL(),xport,xcache); + return retval; +} + +GP<DjVuDocument> +DjVuDocument::create( + const GP<ByteStream> &bs, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + return create(DataPool::create(bs),xport,xcache); +} + +GP<DjVuDocument> +DjVuDocument::create_wait( + const GURL &url, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + GP<DjVuDocument> retval=create(url,xport,xcache); + retval->wait_for_complete_init(); + return retval; +} + +void +DjVuDocument::start_init( + const GURL & url, GP<DjVuPort> xport, DjVuFileCache * xcache) +{ + DEBUG_MSG("DjVuDocument::start_init(): initializing class...\n"); + DEBUG_MAKE_INDENT(3); + if (init_started) + G_THROW( ERR_MSG("DjVuDocument.2nd_init") ); + if (!get_count()) + G_THROW( ERR_MSG("DjVuDocument.not_secure") ); + if(url.is_empty()) + { + if (!init_data_pool) + G_THROW( ERR_MSG("DjVuDocument.empty_url") ); + if(init_url.is_empty()) + { + init_url=invent_url("document.djvu"); + } + }else + { + init_url=url; + } + + // Initialize + cache=xcache; + doc_type=UNKNOWN_TYPE; + DjVuPortcaster * pcaster=get_portcaster(); + if (!xport) + xport=simple_port=new DjVuSimplePort(); + pcaster->add_route(this, xport); + pcaster->add_route(this, this); + + if(!url.is_empty()) + { + init_data_pool=pcaster->request_data(this, init_url); + if(init_data_pool) + { + if(!init_url.is_empty() && init_url.is_local_file_url() && djvu_import_codec) + { + djvu_import_codec(init_data_pool,init_url,needs_compression_flag,needs_rename_flag); + } + if(needs_rename_flag) + can_compress_flag=true; + } + if (!init_data_pool) + { + G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+init_url.get_string()); + } + } + // Now we say it is ready + init_started=true; + + init_thread_flags=STARTED; + init_life_saver=this; + init_thr.create(static_init_thread, this); +} + +DjVuDocument::~DjVuDocument(void) +{ + // No more messages, please. We're being destroyed. + get_portcaster()->del_port(this); + + // We want to stop any DjVuFile which has been created by us + // and is still being decoded. We have to stop them manually because + // they keep the "life saver" in the decoding thread and won't stop + // when we clear the last reference to them + { + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<DjVuFile> file=ufiles_list[pos]->file; + file->stop_decode(false); + file->stop(false); // Disable any access to data + } + ufiles_list.empty(); + } + + GPList<DjVuPort> ports=get_portcaster()->prefix_to_ports(get_int_prefix()); + for(GPosition pos=ports;pos;++pos) + { + GP<DjVuPort> port=ports[pos]; + if (port->inherits("DjVuFile")) + { + DjVuFile * file=(DjVuFile *) (DjVuPort *) port; + file->stop_decode(false); + file->stop(false); // Disable any access to data + } + } + DataPool::close_all(); +} + +void +DjVuDocument::stop_init(void) +{ + DEBUG_MSG("DjVuDocument::stop_init(): making sure that the init thread dies.\n"); + DEBUG_MAKE_INDENT(3); + + GMonitorLock lock(&init_thread_flags); + while((init_thread_flags & STARTED) && + !(init_thread_flags & FINISHED)) + { + if (init_data_pool) init_data_pool->stop(true); // blocking operation + + if (ndir_file) ndir_file->stop(false); + + { + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + ufiles_list[pos]->file->stop(false); // Disable any access to data + ufiles_list.empty(); + } + + init_thread_flags.wait(50); + } +} + +void +DjVuDocument::check() const +{ + if (!init_started) + G_THROW( ERR_MSG("DjVuDocument.not_init") ); +} + +void +DjVuDocument::static_init_thread(void * cl_data) +{ + DjVuDocument * th=(DjVuDocument *) cl_data; + GP<DjVuDocument> life_saver=th; + th->init_life_saver=0; + G_TRY { + th->init_thread(); + } G_CATCH(exc) { + th->flags|=DjVuDocument::DOC_INIT_FAILED; + G_TRY { + th->check_unnamed_files(); + if (!exc.cmp_cause(ByteStream::EndOfFile) && th->verbose_eof) + get_portcaster()->notify_error(th, ERR_MSG("DjVuDocument.init_eof") ); + else if (!exc.cmp_cause(DataPool::Stop)) + get_portcaster()->notify_status(th, ERR_MSG("DjVuDocument.stopped") ); + else + get_portcaster()->notify_error(th, exc.get_cause()); + } G_CATCH_ALL {} G_ENDCATCH; + th->init_thread_flags|=FINISHED; + } G_ENDCATCH; +} + +void +DjVuDocument::init_thread(void) + // This function is run in a separate thread. + // The goal is to detect the document type (BUNDLED, OLD_INDEXED, etc.) + // and decode navigation directory. +{ + DEBUG_MSG("DjVuDocument::init_thread(): guessing what we're dealing with\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=get_portcaster(); + + GP<ByteStream> stream=init_data_pool->get_stream(); + + GP<IFFByteStream> giff=IFFByteStream::create(stream); + IFFByteStream &iff=*giff; + GUTF8String chkid; + int size=iff.get_chunk(chkid); + if (!size) + G_THROW( ByteStream::EndOfFile ); + if (size < 0) + G_THROW( ERR_MSG("DjVuDocument.no_file") ); + if (size<8) + { + G_THROW( ERR_MSG("DjVuDocument.not_DjVu") ); + } + if (chkid=="FORM:DJVM") + { + DEBUG_MSG("Got DJVM document here\n"); + DEBUG_MAKE_INDENT(3); + + size=iff.get_chunk(chkid); + if (chkid=="DIRM") + { + djvm_dir=DjVmDir::create(); + djvm_dir->decode(iff.get_bytestream()); + iff.close_chunk(); + if (djvm_dir->is_bundled()) + { + DEBUG_MSG("Got BUNDLED file.\n"); + doc_type=BUNDLED; + } + else + { + DEBUG_MSG("Got INDIRECT file.\n"); + doc_type=INDIRECT; + } + flags|=DOC_TYPE_KNOWN | DOC_DIR_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN | DOC_DIR_KNOWN, 0); + check_unnamed_files(); + + /* Check for NAVM */ + size=iff.get_chunk(chkid); + if (size && chkid=="NAVM") + { + djvm_nav=DjVmNav::create(); + djvm_nav->decode(iff.get_bytestream()); + iff.close_chunk(); + } + } + else if (chkid=="DIR0") + { + DEBUG_MSG("Got OLD_BUNDLED file.\n"); + doc_type=OLD_BUNDLED; + flags|=DOC_TYPE_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0); + check_unnamed_files(); + } + else + G_THROW( ERR_MSG("DjVuDocument.bad_format") ); + + if (doc_type==OLD_BUNDLED) + { + // Read the DjVmDir0 directory. We are unable to tell what + // files are pages and what are included at this point. + // We only know that the first file with DJVU (BM44 or PM44) + // form *is* the first page. The rest will become known + // after we decode DjVuNavDir + djvm_dir0=DjVmDir0::create(); + djvm_dir0->decode(*iff.get_bytestream()); + iff.close_chunk(); + // Get offset to the first DJVU, PM44 or BM44 chunk + int first_page_offset=0; + while(!first_page_offset) + { + int offset; + size=iff.get_chunk(chkid, &offset); + if (size==0) G_THROW( ERR_MSG("DjVuDocument.no_page") ); + if (chkid=="FORM:DJVU" || chkid=="FORM:PM44" || chkid=="FORM:BM44") + { + DEBUG_MSG("Got 1st page offset=" << offset << "\n"); + first_page_offset=offset; + } + iff.close_chunk(); + } + + // Now get the name of this file + int file_num; + for(file_num=0;file_num<djvm_dir0->get_files_num();file_num++) + { + DjVmDir0::FileRec & file=*djvm_dir0->get_file(file_num); + if (file.offset==first_page_offset) + { + first_page_name=file.name; + break; + } + } + if (!first_page_name.length()) + G_THROW( ERR_MSG("DjVuDocument.no_page") ); + flags|=DOC_DIR_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_DIR_KNOWN, 0); + check_unnamed_files(); + } + } + else // chkid!="FORM:DJVM" + { + // DJVU format + DEBUG_MSG("Got DJVU OLD_INDEXED or SINGLE_PAGE document here.\n"); + doc_type=SINGLE_PAGE; + flags|=DOC_TYPE_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0); + check_unnamed_files(); + } + if (doc_type==OLD_BUNDLED || doc_type==SINGLE_PAGE) + { + DEBUG_MSG("Searching for NDIR chunks...\n"); + ndir_file=get_djvu_file(-1); + if (ndir_file) ndir=ndir_file->decode_ndir(); + ndir_file=0; // Otherwise ~DjVuDocument() will stop (=kill) it + if (!ndir) + { + // Seems to be 1-page old-style document. Create dummy NDIR + if (doc_type==OLD_BUNDLED) + { + ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url)); + ndir->insert_page(-1, first_page_name); + } + else + { + ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url.base())); + ndir->insert_page(-1, init_url.fname()); + } + } + else + { + if (doc_type==SINGLE_PAGE) + doc_type=OLD_INDEXED; + } + flags|=DOC_NDIR_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_NDIR_KNOWN, 0); + check_unnamed_files(); + } + + flags|=DOC_INIT_OK; + pcaster->notify_doc_flags_changed(this, DOC_INIT_OK, 0); + check_unnamed_files(); + init_thread_flags|=FINISHED; + DEBUG_MSG("DOCUMENT IS FULLY INITIALIZED now: doc_type='" << + (doc_type==BUNDLED ? "BUNDLED" : + doc_type==OLD_BUNDLED ? "OLD_BUNDLED" : + doc_type==INDIRECT ? "INDIRECT" : + doc_type==OLD_INDEXED ? "OLD_INDEXED" : + doc_type==SINGLE_PAGE ? "SINGLE_PAGE" : + "UNKNOWN") << "'\n"); +} + +bool +DjVuDocument::wait_for_complete_init(void) +{ + flags.enter(); + while(!(flags & DOC_INIT_FAILED) && + !(flags & DOC_INIT_OK)) flags.wait(); + flags.leave(); + init_thread_flags.enter(); + while (!(init_thread_flags & FINISHED)) + init_thread_flags.wait(); + init_thread_flags.leave(); + return (flags & (DOC_INIT_OK | DOC_INIT_FAILED))!=0; +} + +int +DjVuDocument::wait_get_pages_num(void) const +{ + GSafeFlags &f=const_cast<GSafeFlags &>(flags); + f.enter(); + while(!(f & DOC_TYPE_KNOWN) && + !(f & DOC_INIT_FAILED) && + !(f & DOC_INIT_OK)) f.wait(); + f.leave(); + return get_pages_num(); +} + +GUTF8String +DjVuDocument::get_int_prefix(void) const +{ + // These NAMEs are used to enable DjVuFile sharing inside the same + // DjVuDocument using DjVuPortcaster. Since URLs are unique to the + // document, other DjVuDocuments cannot retrieve files until they're + // assigned some permanent name. After '?' there should be the real + // file's URL. Please note, that output of this function is used only + // as name for DjVuPortcaster. Not as a URL. + GUTF8String retval; + return retval.format("document_%p%d?", this, hash(init_url)); +} + +void +DjVuDocument::set_file_aliases(const DjVuFile * file) +{ + DEBUG_MSG("DjVuDocument::set_file_aliases(): setting global aliases for file '" + << file->get_url() << "'\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + + GMonitorLock lock(&((DjVuFile *) file)->get_safe_flags()); + pcaster->clear_aliases(file); + if (file->is_decode_ok() && cache) + { + // If file is successfully decoded and caching is enabled, + // assign a global alias to this file, so that any other + // DjVuDocument will be able to use it. + + pcaster->add_alias(file, file->get_url().get_string()); + if (flags & (DOC_NDIR_KNOWN | DOC_DIR_KNOWN)) + { + int page_num=url_to_page(file->get_url()); + if (page_num>=0) + { + if (page_num==0) pcaster->add_alias(file, init_url.get_string()+"#-1"); + pcaster->add_alias(file, init_url.get_string()+"#"+GUTF8String(page_num)); + } + } + // The following line MUST stay here. For OLD_INDEXED documents + // a page may finish decoding before DIR or NDIR becomes known + // (multithreading, remember), so the code above would not execute + pcaster->add_alias(file, file->get_url().get_string()+"#-1"); + } else pcaster->add_alias(file, get_int_prefix()+file->get_url()); +} + +void +DjVuDocument::check_unnamed_files(void) +{ + DEBUG_MSG("DjVuDocument::check_unnamed_files(): Seeing if we can fix some...\n"); + DEBUG_MAKE_INDENT(3); + + if (flags & DOC_INIT_FAILED) + { + // Init failed. All unnamed files should be terminated + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<DjVuFile> file=ufiles_list[pos]->file; + file->stop_decode(true); + file->stop(false); // Disable any access to data + } + ufiles_list.empty(); + return; + } + + if ((flags & DOC_TYPE_KNOWN)==0) + return; + + // See the list of unnamed files (created when there was insufficient + // information about DjVuDocument structure) and try to fix those, + // which can be fixed at this time + while(true) + { + DjVuPortcaster * pcaster=get_portcaster(); + + GP<UnnamedFile> ufile; + GURL new_url; + GPosition pos ; + GCriticalSectionLock lock(&ufiles_lock); + for(pos=ufiles_list;pos;) + { + G_TRY + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->id_type==UnnamedFile::ID) + new_url=id_to_url(f->id); + else + new_url=page_to_url(f->page_num); + if (!new_url.is_empty()) + { + ufile=f; + // Don't take it off the list. We want to be + // able to stop the init from ~DjVuDocument(); + // + // ufiles_list.del(pos); + break; + } else if (is_init_complete()) + { + // No empty URLs are allowed at this point. + // We now know all information about the document + // and can determine if a page is inside it or not + f->data_pool->set_eof(); + GUTF8String msg; + if (f->id_type==UnnamedFile::ID) + msg= ERR_MSG("DjVuDocument.miss_page_name") "\t"+f->id; + else + msg= ERR_MSG("DjVuDocument.miss_page_num") "\t"+GUTF8String(f->page_num); + G_THROW(msg); + } + ++pos; + } + G_CATCH(exc) + { + pcaster->notify_error(this, exc.get_cause()); + GP<DataPool> pool=ufiles_list[pos]->data_pool; + if (pool) + pool->stop(); + GPosition this_pos=pos; + ++pos; + ufiles_list.del(this_pos); + } + G_ENDCATCH; + } + + if (ufile && !new_url.is_empty()) + { + DEBUG_MSG("Fixing file: '" << ufile->url << "'=>'" << new_url << "'\n"); + // Now, once we know its real URL we can request a real DataPool and + // can connect the DataPool owned by DjVuFile to that real one + // Note, that now request_data() will not play fool because + // we have enough information + + G_TRY + { + if (ufile->data_pool) + { + GP<DataPool> new_pool=pcaster->request_data(ufile->file, new_url); + if(!new_pool) + G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+new_url.get_string()); + ufile->data_pool->connect(new_pool); + } + ufile->file->set_name(new_url.fname()); + ufile->file->move(new_url.base()); + set_file_aliases(ufile->file); + } + G_CATCH(exc) + { + pcaster->notify_error(this, exc.get_cause()); + } + G_ENDCATCH; + } + else + break; + + // Remove the 'ufile' from the list + for(pos=ufiles_list;pos;++pos) + if (ufiles_list[pos]==ufile) + { + ufiles_list.del(pos); + break; + } + } // while(1) +} + +int +DjVuDocument::get_pages_num(void) const +{ + check(); + if (flags & DOC_TYPE_KNOWN) + if (doc_type==BUNDLED || doc_type==INDIRECT) + return djvm_dir->get_pages_num(); + else if (flags & DOC_NDIR_KNOWN) + return ndir->get_pages_num(); + return 1; +} + +GURL +DjVuDocument::page_to_url(int page_num) const +{ + check(); + DEBUG_MSG("DjVuDocument::page_to_url(): page_num=" << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + GURL url; + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case SINGLE_PAGE: + case OLD_INDEXED: + { + if (page_num<0) url=init_url; + else if (flags & DOC_NDIR_KNOWN) url=ndir->page_to_url(page_num); + break; + } + case OLD_BUNDLED: + { + if (page_num<0) page_num=0; + if (page_num==0 && (flags & DOC_DIR_KNOWN)) + url=GURL::UTF8(first_page_name,init_url); + else if (flags & DOC_NDIR_KNOWN) + url=ndir->page_to_url(page_num); + break; + } + case BUNDLED: + { + if (page_num<0) + page_num=0; + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num); + if (!file) G_THROW( ERR_MSG("DjVuDocument.big_num") ); + url=GURL::UTF8(file->get_load_name(),init_url); + } + break; + } + case INDIRECT: + { + if (page_num<0) page_num=0; + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num); + if (!file) + G_THROW( ERR_MSG("DjVuDocument.big_num") ); + url=GURL::UTF8(file->get_load_name(),init_url.base()); + } + break; + } + default: + G_THROW( ERR_MSG("DjVuDocument.unk_type") ); + } + return url; +} + +int +DjVuDocument::url_to_page(const GURL & url) const +{ + check(); + DEBUG_MSG("DjVuDocument::url_to_page(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + int page_num=-1; + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case SINGLE_PAGE: + case OLD_BUNDLED: + case OLD_INDEXED: + { + if (flags & DOC_NDIR_KNOWN) page_num=ndir->url_to_page(url); + break; + } + case BUNDLED: + { + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file; + if (url.base()==init_url) + file=djvm_dir->id_to_file(url.fname()); + if (file) + page_num=file->get_page_num(); + } + break; + } + case INDIRECT: + { + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file; + if (url.base()==init_url.base()) + file=djvm_dir->id_to_file(url.fname()); + if (file) + page_num=file->get_page_num(); + } + break; + } + default: + G_THROW( ERR_MSG("DjVuDocument.unk_type") ); + } + return page_num; +} + +GURL +DjVuDocument::id_to_url(const GUTF8String & id) const +{ + check(); + DEBUG_MSG("DjVuDocument::id_to_url(): translating ID='" << id << "' to URL\n"); + DEBUG_MAKE_INDENT(3); + + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case BUNDLED: + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->id_to_file(id); + if (!file) + { + file=djvm_dir->name_to_file(id); + if (!file) + file=djvm_dir->title_to_file(id); + } + if (file) + return GURL::UTF8(file->get_load_name(),init_url); + } + break; + case INDIRECT: + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->id_to_file(id); + if (!file) + { + file=djvm_dir->name_to_file(id); + if (!file) + file=djvm_dir->title_to_file(id); + } + if (file) + return GURL::UTF8(file->get_load_name(),init_url.base()); + } + break; + case OLD_BUNDLED: + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir0::FileRec> frec=djvm_dir0->get_file(id); + if (frec) + return GURL::UTF8(id,init_url); + } + break; + case OLD_INDEXED: + case SINGLE_PAGE: + return GURL::UTF8(id,init_url.base()); + break; + } + return GURL(); +} + +GURL +DjVuDocument::id_to_url(const DjVuPort * source, const GUTF8String &id) +{ + return id_to_url(id); +} + +GP<DjVuFile> +DjVuDocument::url_to_file(const GURL & url, bool dont_create) const + // This function is private and is called from two places: + // id_to_file() and get_djvu_file() ONLY when the structure is known +{ + check(); + DEBUG_MSG("DjVuDocument::url_to_file(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Try DjVuPortcaster to find existing files. + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + GP<DjVuPort> port; + + if (cache) + { + // First - fully decoded files + port=pcaster->alias_to_port(url.get_string()); + if (port && port->inherits("DjVuFile")) + { + DEBUG_MSG("found fully decoded file using DjVuPortcaster\n"); + return (DjVuFile *) (DjVuPort *) port; + } + } + + // Second - internal files + port=pcaster->alias_to_port(get_int_prefix()+url); + if (port && port->inherits("DjVuFile")) + { + DEBUG_MSG("found internal file using DjVuPortcaster\n"); + return (DjVuFile *) (DjVuPort *) port; + } + + GP<DjVuFile> file; + + if (!dont_create) + { + DEBUG_MSG("creating a new file\n"); + file=DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof); + const_cast<DjVuDocument *>(this)->set_file_aliases(file); + } + + return file; +} + +GP<DjVuFile> +DjVuDocument::get_djvu_file(int page_num, bool dont_create) const +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvu_file(): request for page " << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + + GURL url; + { + // I'm locking the flags because depending on what page_to_url() + // returns me, I'll be creating DjVuFile in different ways. + // And I don't want the situation to change between the moment I call + // id_to_url() and I actually create DjVuFile + GMonitorLock lock(&(const_cast<DjVuDocument *>(this)->flags)); + url=page_to_url(page_num); + if (url.is_empty()) + { + // If init is complete and url is empty, we know for sure, that + // smth is wrong with the page_num. So we can return ZERO. + // Otherwise we create a temporary file and wait for init to finish + if (is_init_complete()) return 0; + + DEBUG_MSG("Structure is not known => check <doc_url>#<page_num> alias...\n"); + GP<DjVuPort> port; + if (cache) + port=pcaster->alias_to_port(init_url.get_string()+"#"+GUTF8String(page_num)); + if (!port || !port->inherits("DjVuFile")) + { + DEBUG_MSG("failed => invent dummy URL and proceed\n"); + + // Invent some dummy temporary URL. I don't care what it will + // be. I'll remember the page_num and will generate the correct URL + // after I learn what the document is + GUTF8String name("page"); + name+=GUTF8String(page_num); + name+=".djvu"; + url=invent_url(name); + + GCriticalSectionLock(&(const_cast<DjVuDocument *>(this)->ufiles_lock)); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->url==url) return f->file; + } + GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::PAGE_NUM, 0, + page_num, url, 0); + + // We're adding the record to the list before creating the DjVuFile + // because DjVuFile::init() will call request_data(), and the + // latter should be able to find the record. + // + // We also want to keep ufiles_lock to make sure that when + // request_data() is called, the record is still there + const_cast<DjVuDocument *>(this)->ufiles_list.append(ufile); + + GP<DjVuFile> file= + DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof); + ufile->file=file; + return file; + } else url=((DjVuFile *) (DjVuPort *) port)->get_url(); + } + } + + GP<DjVuFile> file=url_to_file(url, dont_create); + if (file) + pcaster->add_route(file, const_cast<DjVuDocument *>(this)); + return file; +} + +GURL +DjVuDocument::invent_url(const GUTF8String &name) const +{ + GUTF8String buffer; + buffer.format("djvufileurl://%p/%s", this, (const char *)name); + return GURL::UTF8(buffer); +} + +GP<DjVuFile> +DjVuDocument::get_djvu_file(const GUTF8String& id, bool dont_create) +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvu_file(): ID='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + if (!id.length()) + return get_djvu_file(-1); + +// Integers are not supported, only ID's +// if (id.is_int()) +// return get_djvu_file(id.toInt(),dont_create); + + GURL url; + // I'm locking the flags because depending on what id_to_url() + // returns me, I'll be creating DjVuFile in different ways. + // And I don't want the situation to change between the moment I call + // id_to_url() and I actually create DjVuFile + { + GMonitorLock lock(&flags); + url=id_to_url(id); + if(url.is_empty() && !id.is_int()) + { + // If init is complete, we know for sure, that there is no such + // file with ID 'id' in the document. Otherwise we have to + // create a temporary file and wait for the init to finish + if (is_init_complete()) + return 0; + // Invent some dummy temporary URL. I don't care what it will + // be. I'll remember the ID and will generate the correct URL + // after I learn what the document is + url=invent_url(id); + DEBUG_MSG("Invented url='" << url << "'\n"); + + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->url==url) + return f->file; + } + GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::ID, id, 0, url, 0); + + // We're adding the record to the list before creating the DjVuFile + // because DjVuFile::init() will call request_data(), and the + // latter should be able to find the record. + // + // We also want to keep ufiles_lock to make sure that when + // request_data() is called, the record is still there + ufiles_list.append(ufile); + + GP<DjVuFile> file=DjVuFile::create(url,this,recover_errors,verbose_eof); + ufile->file=file; + return file; + } + } + + return get_djvu_file(url,dont_create); +} + +GP<DjVuFile> +DjVuDocument::get_djvu_file(const GURL& url, bool dont_create) +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvu_file(): URL='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (url.is_empty()) + return 0; + + const GP<DjVuFile> file(url_to_file(url, dont_create)); + + if (file) + get_portcaster()->add_route(file, this); + + return file; +} + +GP<DjVuImage> +DjVuDocument::get_page(int page_num, bool sync, DjVuPort * port) const +{ + check(); + DEBUG_MSG("DjVuDocument::get_page(): request for page " << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVuImage> dimg; + const GP<DjVuFile> file(get_djvu_file(page_num)); + if (file) + { + dimg=DjVuImage::create(file); + if (port) + DjVuPort::get_portcaster()->add_route(dimg, port); + + file->resume_decode(); + if (dimg && sync) + dimg->wait_for_complete_decode(); + } + return dimg; +} + +GP<DjVuImage> +DjVuDocument::get_page(const GUTF8String &id, bool sync, DjVuPort * port) +{ + check(); + DEBUG_MSG("DjVuDocument::get_page(): ID='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVuImage> dimg; + const GP<DjVuFile> file(get_djvu_file(id)); + if(file) + { + dimg=DjVuImage::create(file); + if (port) + DjVuPort::get_portcaster()->add_route(dimg, port); + + file->resume_decode(); + if (dimg && sync) + dimg->wait_for_complete_decode(); + } + return dimg; +} + +void +DjVuDocument::process_threqs(void) + // Will look thru threqs_list and try to fulfil every request +{ + GCriticalSectionLock lock(&threqs_lock); + for(GPosition pos=threqs_list;pos;) + { + GP<ThumbReq> req=threqs_list[pos]; + bool remove=false; + if (req->thumb_file) + { + G_TRY { + // There is supposed to be a file with thumbnails + if (req->thumb_file->is_data_present()) + { + // Cool, we can extract the thumbnail now + GP<ByteStream> str=req->thumb_file->get_init_data_pool()->get_stream(); + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (!iff.get_chunk(chkid) || chkid!="FORM:THUM") + G_THROW( ERR_MSG("DjVuDocument.bad_thumb") ); + + for(int i=0;i<req->thumb_chunk;i++) + { + if (!iff.get_chunk(chkid)) + G_THROW( ERR_MSG("DjVuDocument.bad_thumb") ); + iff.close_chunk(); + } + if (!iff.get_chunk(chkid) || chkid!="TH44") + G_THROW( ERR_MSG("DjVuDocument.bad_thumb") ); + + // Copy the data + char buffer[1024]; + int length; + while((length=iff.read(buffer, 1024))) + req->data_pool->add_data(buffer, length); + req->data_pool->set_eof(); + + // Also add this file to cache so that we won't have + // to download it next time + add_to_cache(req->thumb_file); + + req->thumb_file=0; + req->image_file=0; + remove=true; + } + } G_CATCH(exc) { + GUTF8String msg= ERR_MSG("DjVuDocument.cant_extract") "\n"; + msg+=exc.get_cause(); + get_portcaster()->notify_error(this, msg); + // Switch this request to the "decoding" mode + req->image_file=get_djvu_file(req->page_num); + req->thumb_file=0; + req->data_pool->set_eof(); + remove=true; + } G_ENDCATCH; + } // if (req->thumb_file) + + if (req->image_file) + { + G_TRY { + // Decode the file if necessary. Or just used predecoded image. + GSafeFlags & file_flags=req->image_file->get_safe_flags(); + { + GMonitorLock lock(&file_flags); + if (!req->image_file->is_decoding()) + { + if (req->image_file->is_decode_ok()) + { + // We can generate it now + const GP<DjVuImage> dimg(DjVuImage::create(req->image_file)); + + dimg->wait_for_complete_decode(); + + int width = 160; + int height = 160; + + if( dimg->get_width() ) + width = dimg->get_width(); + if( dimg->get_height() ) + height = dimg->get_height(); + + GRect rect(0, 0, 160, height*160/width); + GP<GPixmap> pm=dimg->get_pixmap(rect, rect, thumb_gamma); + if (!pm) + { + GP<GBitmap> bm=dimg->get_bitmap(rect, rect, sizeof(int)); + if(bm) + pm=GPixmap::create(*bm); + else + pm = GPixmap::create(rect.height(), rect.width(), + &GPixel::WHITE); + } + + // Store and compress the pixmap + GP<IW44Image> iwpix=IW44Image::create_encode(*pm); + GP<ByteStream> gstr=ByteStream::create(); + IWEncoderParms parms; + parms.slices=97; + parms.bytes=0; + parms.decibels=0; + iwpix->encode_chunk(gstr, parms); + TArray<char> data=gstr->get_data(); + + req->data_pool->add_data((const char *) data, data.size()); + req->data_pool->set_eof(); + + req->thumb_file=0; + req->image_file=0; + remove=true; + } else if (req->image_file->is_decode_failed()) + { + // Unfortunately we cannot decode it + req->thumb_file=0; + req->image_file=0; + req->data_pool->set_eof(); + remove=true; + } else + { + req->image_file->start_decode(); + } + } + } + } G_CATCH(exc) { + GUTF8String msg="Failed to decode thumbnails:\n"; + msg+=exc.get_cause(); + get_portcaster()->notify_error(this, msg); + + // Get rid of this request + req->image_file=0; + req->thumb_file=0; + req->data_pool->set_eof(); + remove=true; + } G_ENDCATCH; + } + + if (remove) + { + GPosition this_pos=pos; + ++pos; + threqs_list.del(this_pos); + } else ++pos; + } +} + +GP<DjVuDocument::ThumbReq> +DjVuDocument::add_thumb_req(const GP<ThumbReq> & thumb_req) + // Will look through the list of pending requests for thumbnails + // and try to add the specified request. If a duplicate is found, + // it will be returned and the list will not be modified +{ + GCriticalSectionLock lock(&threqs_lock); + for(GPosition pos=threqs_list;pos;++pos) + { + GP<ThumbReq> req=threqs_list[pos]; + if (req->page_num==thumb_req->page_num) + return req; + } + threqs_list.append(thumb_req); + return thumb_req; +} + +GList<GUTF8String> +DjVuDocument::get_id_list(void) +{ + GList<GUTF8String> ids; + if (is_init_complete()) + { + if(djvm_dir) + { + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + ids.append(files_list[pos]->get_load_name()); + } + }else + { + const int page_num=get_pages_num(); + for(int page=0;page<page_num;page++) + { + ids.append(page_to_url(page).fname()); + } + } + } + return ids; +} + +void +DjVuDocument::map_ids(GMap<GUTF8String,void *> &map) +{ + GList<GUTF8String> ids=get_id_list(); + for(GPosition pos=ids;pos;++pos) + { + map[ids[pos]]=0; + } +} + +GP<DataPool> +DjVuDocument::get_thumbnail(int page_num, bool dont_decode) +{ + DEBUG_MSG("DjVuDocument::get_thumbnail(): page_num=" << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + if (!is_init_complete()) return 0; + + { + // See if we already have request for this thumbnail pending + GCriticalSectionLock lock(&threqs_lock); + for(GPosition pos=threqs_list;pos;++pos) + { + GP<ThumbReq> req=threqs_list[pos]; + if (req->page_num==page_num) + return req->data_pool; // That's it. Just return it. + } + } + + // No pending request for this page... Create one + GP<ThumbReq> thumb_req=new ThumbReq(page_num, DataPool::create()); + + // First try to find predecoded thumbnail + if (get_doc_type()==INDIRECT || get_doc_type()==BUNDLED) + { + // Predecoded thumbnails exist for new formats only + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + GP<DjVmDir::File> thumb_file; + int thumb_start=0; + int page_cnt=-1; + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> f=files_list[pos]; + if (f->is_thumbnails()) + { + thumb_file=f; + thumb_start=page_cnt+1; + } else if (f->is_page()) + { + page_cnt++; + } + if (page_cnt==page_num) break; + } + if (thumb_file) + { + // That's the file with the desired thumbnail image + thumb_req->thumb_file=get_djvu_file(thumb_file->get_load_name()); + thumb_req->thumb_chunk=page_num-thumb_start; + thumb_req=add_thumb_req(thumb_req); + process_threqs(); + return thumb_req->data_pool; + } + } + + // Apparently we're out of luck and need to decode the requested + // page (unless it's already done and if it's allowed) and render + // it into the thumbnail. If dont_decode is true, do not attempt + // to create this file (because this will result in a request for data) + GP<DjVuFile> file=get_djvu_file(page_num, dont_decode); + if (file) + { + thumb_req->image_file=file; + + // I'm locking the flags here to make sure, that DjVuFile will not + // change its state in between of the checks. + GSafeFlags & file_flags=file->get_safe_flags(); + { + GMonitorLock lock(&file_flags); + if (thumb_req->image_file->is_decode_ok() || !dont_decode) + { + // Just add it to the list and call process_threqs(). It + // will start decoding if necessary + thumb_req=add_thumb_req(thumb_req); + process_threqs(); + } else + { + // Nothing can be done return ZERO + thumb_req=0; + } + } + } else thumb_req=0; + + if (thumb_req) return thumb_req->data_pool; + else return 0; +} + +static void +add_to_cache(const GP<DjVuFile> & f, GMap<GURL, void *> & map, + DjVuFileCache * cache) +{ + GURL url=f->get_url(); + DEBUG_MSG("DjVuDocument::add_to_cache(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!map.contains(url)) + { + map[url]=0; + cache->add_file(f); + + GPList<DjVuFile> list; + for(GPosition pos=list;pos;++pos) + add_to_cache(list[pos], map, cache); + } +} + +void +DjVuDocument::add_to_cache(const GP<DjVuFile> & f) +{ + if (cache) + { + GMap<GURL, void *> map; + ::add_to_cache(f, map, cache); + } +} + +void +DjVuDocument::notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask) +{ + // Don't check here if the document is initialized or not. + // This function may be called when it's not. + // check(); + if (set_mask & DjVuFile::DECODE_OK) + { + set_file_aliases(source); + if (cache) add_to_cache((DjVuFile *) source); + if(!needs_compression_flag) + { + if(source->needs_compression()) + { + can_compress_flag=true; + needs_compression_flag=true; + }else if(source->can_compress()) + { + can_compress_flag=true; + } + } + process_threqs(); + } + + if (set_mask & DjVuFile::DATA_PRESENT) + process_threqs(); // May be we can extract thumbnails now +} + +GP<DjVuFile> +DjVuDocument::id_to_file(const DjVuPort * source, const GUTF8String &id) +{ + return (DjVuFile *) get_djvu_file(id); +} + +GP<DataPool> +DjVuDocument::request_data(const DjVuPort * source, const GURL & url) +{ + DEBUG_MSG("DjVuDocument::request_data(): seeing if we can do it\n"); + DEBUG_MAKE_INDENT(3); + + if (url==init_url) + return init_data_pool; + + check(); // Don't put it before 'init_data_pool' + + { + // See if there is a file in the "UnnamedFiles" list. + // If it's there, then create an empty DataPool and store its + // pointer in the list. The "init thread" will eventually + // do smth with it. + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->url==url) + { + DEBUG_MSG("Found tmp unnamed DjVuFile. Return empty DataPool\n"); + // Remember the DataPool. We will connect it to the + // actual data after the document structure becomes known + f->data_pool=DataPool::create(); + return f->data_pool; + } + } + } + + // Well, the url is not in the "UnnamedFiles" list, but it doesn't + // mean, that it's not "artificial". Stay alert! + GP<DataPool> data_pool; + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case OLD_BUNDLED: + { + if (flags & DOC_DIR_KNOWN) + { + DEBUG_MSG("The document is in OLD_BUNDLED format\n"); + if (url.base()!=init_url) + G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t"+url.get_string()); + + GP<DjVmDir0::FileRec> file=djvm_dir0->get_file(url.fname()); + if (!file) + { + G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname()); + } + data_pool=DataPool::create(init_data_pool, file->offset, file->size); + } + break; + } + case BUNDLED: + { + if (flags & DOC_DIR_KNOWN) + { + DEBUG_MSG("The document is in new BUNDLED format\n"); + if (url.base()!=init_url) + { + G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t" + +url.get_string()); + } + + GP<DjVmDir::File> file=djvm_dir->id_to_file(url.fname()); + if (!file) + { + G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname()); + } + data_pool=DataPool::create(init_data_pool, file->offset, file->size); + } + break; + } + case SINGLE_PAGE: + case OLD_INDEXED: + case INDIRECT: + { + DEBUG_MSG("The document is in SINGLE_PAGE or OLD_INDEXED or INDIRECT format\n"); + if (flags & DOC_DIR_KNOWN) + if (doc_type==INDIRECT && !djvm_dir->id_to_file(url.fname())) + G_THROW( ERR_MSG("DjVuDocument.URL_outside2") "\t"+url.get_string()); + + if (url.is_local_file_url()) + { +// GUTF8String fname=GOS::url_to_filename(url); +// if (GOS::basename(fname)=="-") fname="-"; + DEBUG_MSG("url=" << url << "\n"); + + data_pool=DataPool::create(url); + } + } + } + return data_pool; +} + + +static void +add_file_to_djvm(const GP<DjVuFile> & file, bool page, + DjVmDoc & doc, GMap<GURL, void *> & map) + // This function is used only for obsolete formats. + // For new formats there is no need to process files recursively. + // All information is already available from the DJVM chunk +{ + GURL url=file->get_url(); + + if (!map.contains(url)) + { + map[url]=0; + + if (file->get_chunks_number()>0 && !file->contains_chunk("NDIR")) + { + // Get the data and unlink any file containing NDIR chunk. + // Yes. We're lazy. We don't check if those files contain + // anything else. + GPosition pos; + GPList<DjVuFile> files_list=file->get_included_files(false); + GP<DataPool> data=file->get_djvu_data(false); + for(pos=files_list;pos;++pos) + { + GP<DjVuFile> f=files_list[pos]; + if (f->contains_chunk("NDIR")) + data=DjVuFile::unlink_file(data, f->get_url().fname()); + } + + // Finally add it to the document + GUTF8String name=file->get_url().fname(); + GP<DjVmDir::File> file_rec=DjVmDir::File::create( + name, name, name, + page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE ); + doc.insert_file(file_rec, data, -1); + + // And repeat for all included files + for(pos=files_list;pos;++pos) + add_file_to_djvm(files_list[pos], false, doc, map); + } + } +} + +static void +add_file_to_djvm(const GP<DjVuFile> & file, bool page, + DjVmDoc & doc, GMap<GURL, void *> & map, + bool &needs_compression_flag, bool &can_compress_flag ) +{ + if(!needs_compression_flag) + { + if(file->needs_compression()) + { + can_compress_flag=true; + needs_compression_flag=true; + }else if(file->can_compress()) + { + can_compress_flag=true; + } + } + add_file_to_djvm(file,page,doc,map); +} + +static void +local_get_url_names(DjVuFile * f,const GMap<GURL, void *> & map,GMap<GURL,void *> &tmpmap) +{ + GURL url=f->get_url(); + if (!map.contains(url) && !tmpmap.contains(url)) + { + tmpmap[url]=0; + f->process_incl_chunks(); + GPList<DjVuFile> files_list=f->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + local_get_url_names(files_list[pos], map, tmpmap); + } +} + +static void +local_get_url_names(DjVuFile * f, GMap<GURL, void *> & map) +{ + GMap<GURL,void *> tmpmap; + local_get_url_names(f,map,tmpmap); + for(GPosition pos=tmpmap;pos;++pos) + map[tmpmap.key(pos)]=0; +} + +GList<GURL> +DjVuDocument::get_url_names(void) +{ + check(); + + GCriticalSectionLock lock(&url_names_lock); + if(has_url_names) + return url_names; + + GMap<GURL, void *> map; + int i; + if (doc_type==BUNDLED || doc_type==INDIRECT) + { + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GURL url=id_to_url(files_list[pos]->get_load_name()); + map[url]=0; + } + }else + { + int pages_num=get_pages_num(); + for(i=0;i<pages_num;i++) + { + G_TRY + { + local_get_url_names(get_djvu_file(i), map); + } + G_CATCH(ex) + { + // Why is this try/catch block here? + G_TRY { + get_portcaster()->notify_error(this, ex.get_cause()); + GUTF8String emsg = ERR_MSG("DjVuDocument.exclude_page") "\t" + (i+1); + get_portcaster()->notify_error(this, emsg); + } + G_CATCH_ALL + { + G_RETHROW; + } + G_ENDCATCH; + } + G_ENDCATCH; + } + } + for(GPosition j=map;j;++j) + { + if (map.key(j).is_local_file_url()) + { + url_names.append(map.key(j)); + } + } + has_url_names=true; + return url_names; +} + +GP<DjVmDoc> +DjVuDocument::get_djvm_doc() + // This function may block for data +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvm_doc(): creating the DjVmDoc\n"); + DEBUG_MAKE_INDENT(3); + + if (!is_init_complete()) + G_THROW( ERR_MSG("DjVuDocument.init_not_done") ); + + GP<DjVmDoc> doc=DjVmDoc::create(); + + if (doc_type==BUNDLED || doc_type==INDIRECT) + { + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> f=new DjVmDir::File(*files_list[pos]); + GP<DjVuFile> file=url_to_file(id_to_url(f->get_load_name())); + GP<DataPool> data; + if (file->is_modified()) + data=file->get_djvu_data(false); + else + data=file->get_init_data_pool(); + doc->insert_file(f, data); + } + if (djvm_nav) + doc->set_djvm_nav(djvm_nav); + } + else if (doc_type==SINGLE_PAGE) + { + DEBUG_MSG("Creating: djvm for a single page document.\n"); + GMap<GURL, void *> map_add; + GP<DjVuFile> file=get_djvu_file(0); + add_file_to_djvm(file, true, *doc, map_add, + needs_compression_flag,can_compress_flag); + } + else + { + DEBUG_MSG("Converting: the document is in an old format.\n"); + GMap<GURL, void *> map_add; + if(recover_errors == ABORT) + { + for(int page_num=0;page_num<ndir->get_pages_num();page_num++) + { + GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num)); + add_file_to_djvm(file, true, *doc, map_add, + needs_compression_flag,can_compress_flag); + } + } + else + { + for(int page_num=0;page_num<ndir->get_pages_num();page_num++) + { + G_TRY + { + GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num)); + add_file_to_djvm(file, true, *doc, map_add, + needs_compression_flag,can_compress_flag); + } + G_CATCH(ex) + { + G_TRY { + get_portcaster()->notify_error(this, ex.get_cause()); + GUTF8String emsg = ERR_MSG("DjVuDocument.skip_page") "\t" + + (page_num+1); + get_portcaster()->notify_error(this, emsg); + } + G_CATCH_ALL + { + G_RETHROW; + } + G_ENDCATCH; + } + G_ENDCATCH; + } + } + } + return doc; +} + +void +DjVuDocument::write( const GP<ByteStream> &gstr, + const GMap<GUTF8String,void *> &reserved) +{ + DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n"); + DEBUG_MAKE_INDENT(3); + get_djvm_doc()->write(gstr,reserved); +} + +void +DjVuDocument::write(const GP<ByteStream> &gstr, bool force_djvm) +{ + DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVmDoc> doc=get_djvm_doc(); + GP<DjVmDir> dir=doc->get_djvm_dir(); + if (force_djvm || dir->get_files_num()>1) + { + doc->write(gstr); + }else + { + GPList<DjVmDir::File> files_list=dir->resolve_duplicates(false); + GP<DataPool> pool=doc->get_data(files_list[files_list]->get_load_name()); + GP<ByteStream> pool_str=pool->get_stream(); + ByteStream &str=*gstr; + str.writall(octets,4); + str.copy(*pool_str); + } +} + +void +DjVuDocument::expand(const GURL &codebase, const GUTF8String &idx_name) +{ + DEBUG_MSG("DjVuDocument::expand(): codebase='" << codebase << "'\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVmDoc> doc=get_djvm_doc(); + doc->expand(codebase, idx_name); +} + +void +DjVuDocument::save_as(const GURL &where, const bool bundled) +{ + DEBUG_MSG("DjVuDocument::save_as(): where='" << where << + "', bundled=" << bundled << "\n"); + DEBUG_MAKE_INDENT(3); + + if (needs_compression()) + { + if(!djvu_compress_codec) + { + G_THROW( ERR_MSG("DjVuDocument.comp_codec") ); + } + GP<ByteStream> gmbs=ByteStream::create(); + write(gmbs); + ByteStream &mbs=*gmbs; + mbs.flush(); + mbs.seek(0,SEEK_SET); + (*djvu_compress_codec)(gmbs,where,bundled); + }else if (bundled) + { + DataPool::load_file(where); + write(ByteStream::create(where, "wb")); + } else + { + expand(where.base(), where.fname()); + } +} + +static const char prolog[]="<?xml version=\"1.0\" ?>\n<!DOCTYPE DjVuXML PUBLIC \"-//W3C//DTD DjVuXML 1.1//EN\" \"pubtext/DjVuXML-s.dtd\">\n<DjVuXML>\n<HEAD>"; +static const char start_xml[]="</HEAD>\n<BODY>\n"; +static const char end_xml[]="</BODY>\n</DjVuXML>\n"; + +void +DjVuDocument::writeDjVuXML(const GP<ByteStream> &gstr_out,int flags) const +{ + ByteStream &str_out=*gstr_out; + str_out.writestring( + prolog+get_init_url().get_string().toEscaped()+start_xml); + const int pages=wait_get_pages_num(); + for(int page_num=0;page_num<pages;++page_num) + { + const GP<DjVuImage> dimg(get_page(page_num,true)); + if(!dimg) + { + G_THROW( ERR_MSG("DjVuToText.decode_failed") ); + } + dimg->writeXML(str_out,get_init_url(),flags); + } + str_out.writestring(GUTF8String(end_xml)); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocument.h b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.h new file mode 100644 index 00000000..418d0814 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.h @@ -0,0 +1,1071 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocument.h,v 1.10 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUDOCUMENT_H +#define _DJVUDOCUMENT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuPort.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DjVmDoc; +class DjVmDir; +class DjVmDir0; +class DjVmNav; +class DjVuImage; +class DjVuFile; +class DjVuFileCache; +class DjVuNavDir; +class ByteStream; + +/** @name DjVuDocument.h + Files #"DjVuDocument.h"# and #"DjVuDocument.cpp"# contain implementation + of the \Ref{DjVuDocument} class - the ideal tool for opening, decoding + and saving DjVu single page and multi page documents. + + @memo DjVu document class. + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DjVuDocument.h,v 1.10 2005/05/25 20:24:52 leonb Exp $# +*/ + +//@{ + +/** #DjVuDocument# provides convenient interface for opening, decoding + and saving back DjVu documents in single page and multi page formats. + + {\bf Input formats} + It can read multi page DjVu documents in either of the 4 formats: 2 + obsolete ({\em old bundled} and {\em old indexed}) and two new + ({\em new bundled} and {\em new indirect}). + + {\bf Output formats} + To encourage users to switch to the new formats, the #DjVuDocument# can + save documents back only in the new formats: {\em bundled} and + {\em indirect}. + + {\bf Conversion.} Since #DjVuDocument# can open DjVu documents in + an obsolete format and save it in any of the two new formats + ({\em new bundled} and {\em new indirect}), this class can be used for + conversion from obsolete formats to the new ones. Although it can also + do conversion between the new two formats, it's not the best way to + do it. Please refer to \Ref{DjVmDoc} for details. + + {\bf Decoding.} #DjVuDocument# provides convenient interface for obtaining + \Ref{DjVuImage} corresponding to any page of the document. It uses + \Ref{DjVuFileCache} to do caching thus avoiding unnecessary multiple decoding of + the same page. The real decoding though is accomplished by \Ref{DjVuFile}. + + {\bf Messenging.} Being derived from \Ref{DjVuPort}, #DjVuDocument# + takes an active part in exchanging messages (requests and notifications) + between different parties involved in decoding. It reports (relays) + errors, progress information and even handles some requests for data (when + these requests deal with local files). + + Typical usage of #DjVuDocument# class in a threadless command line + program would be the following: + \begin{verbatim} + static const char file_name[]="/tmp/document.djvu"; + GP<DjVuDocument> doc=DjVuDocument::create_wait(file_name); + const int pages=doc->get_pages_num(); + for(int page=0;page<pages;page++) + { + GP<DjVuImage> dimg=doc->get_page(page); + // Do something + }; + \end{verbatim} + + {\bf Comments for the code above} + \begin{enumerate} + \item Since the document is assumed to be stored on the hard drive, + we don't have to cope with \Ref{DjVuPort}s and can pass + #ZERO# pointer to the \Ref{init}() function. #DjVuDocument# + can access local data itself. In the case of a plugin though, + one would have to implement his own \Ref{DjVuPort}, which + would handle requests for data arising when the document + is being decoded. + \item In a threaded program instead of calling the \Ref{init}() + function one can call \Ref{start_init}() and \Ref{stop_init}() + to initiate and interrupt initialization carried out in + another thread. This possibility of initializing the document + in another thread has been added specially for the plugin + because the initialization itself requires data, which is + not immediately available in the plugin. Thus, to prevent the + main thread from blocking, we perform initialization in a + separate thread. To check if the class is completely and + successfully initialized, use \Ref{is_init_ok}(). To see if + there was an error, use \Ref{is_init_failed}(). To + know when initialization is over (whether successfully or not), + use \Ref{is_init_complete}(). To wait for this to happen use + \Ref{wait_for_complete_init}(). Once again, all these things are + not required for single-threaded program. + + Another difference between single-threaded and multi-threaded + environments is that in a single-threaded program, the image is + fully decoded before it's returned. In a multi-threaded + application decoding starts in a separate thread, and the pointer + to the \Ref{DjVuImage} being decoded is returned immediately. + This has been done to enable progressive redisplay + in the DjVu plugin. Use communication mechanism provided by + \Ref{DjVuPort} and \Ref{DjVuPortcaster} to learn about progress + of decoding. Or try #dimg->wait_for_complete_decode()# to wait + until the decoding ends. + \item See Also: \Ref{DjVuFile}, \Ref{DjVuImage}, \Ref{GOS}. + \end{enumerate} + + {\bf Initialization} + As mentioned above, the #DjVuDocument# can go through several stages + of initialization. The functionality is gradually added while it passes + one stage after another: + \begin{enumerate} + \item First of all, immediately after the object is created \Ref{init}() + or \Ref{start_init}() functions must be called. {\bf Nothing} + will work until this is done. \Ref{init}() function will not + return until the initialization is complete. You need to make + sure, that enough data is available. {\bf Do not call \Ref{init}() + in the plugin}. \Ref{start_init}() will start initialization + in another thread. Use \Ref{stop_init}() to interrupt it. + Use \Ref{is_init_complete}() to check the initialization progress. + Use \Ref{wait_for_complete_init}() to wait for init to finish. + \item The first thing the initializing code learns about the document + is its type (#BUNDLED#, #INDIRECT#, #OLD_BUNDLED# or #OLD_INDEXED#). + As soon as it happens, document flags are changed and + #notify_doc_flags_changed()# request is sent through the + communication mechanism provided by \Ref{DjVuPortcaster}. + \item After the document type becomes known, the initializing code + proceeds with learning the document structure. Gradually the + flags are updated with values: + \begin{itemize} + \item #DOC_DIR_KNOWN#: Contents of the document became known. + This is meaningful for #BUNDLED#, #OLD_BUNDLED# and + #INDIRECT# documents only. + \item #DOC_NDIR_KNOWN#: Contents of the document navigation + directory became known. This is meaningful for old-style + documents (#OLD_BUNDLED# and #OLD_INDEXED#) only + \item #DOC_INIT_OK# or #DOC_INIT_FAILED#: + The initializating code finished. + \end{itemize} + \end{enumerate} */ + +class DjVuDocument : public DjVuPort +{ +public: + /** Flags describing the document initialization state. + \begin{itemize} + \item #DOC_TYPE_KNOWN#: The type of the document has been learnt. + \item #DOC_DIR_KNOWN#: Contents of the document became known. + This is meaningful for #BUNDLED#, #OLD_BUNDLED# and + #INDIRECT# documents only. + \item #DOC_NDIR_KNOWN#: Contents of the document navigation + directory became known. This is meaningful for old-style + documents (#OLD_BUNDLED# and #OLD_INDEXED#) only + \item #DOC_INIT_OK#: The initialization has completed successfully. + \item #DOC_INIT_FAILED#: The initialization failed. + \end{itemize} */ + enum DOC_FLAGS { DOC_TYPE_KNOWN=1, DOC_DIR_KNOWN=2, + DOC_NDIR_KNOWN=4, DOC_INIT_OK=8, + DOC_INIT_FAILED=16 }; + /** Specifies the format of #DjVuDocument#. There are currently 4 DjVu + multipage formats recognized by the library. Two of them are obsolete + and should not be used. + \begin{enumerate} + \item #OLD_BUNDLED# - Obsolete bundled format + \item #OLD_INDEXED# - Obsolete multipage format where every page + is stored in a separate file and "includes" (by means + of an #INCL# chunk) the file with the document directory. + \item #SINGLE_PAGE# - Single page document. Basically a file + with either #FORM:DJVU# or #FORM:IW44# and no multipage + information. For example, #OLD_INDEXED# documents with + document directory do not qualify even if they contain only + one page. + \item #BUNDLED# - Currently supported bundled format + \item #INDIRECT# - Currently supported "expanded" format, where + every page and component is stored in a separate file. There + is also a {\em top-level} file with the document directory. + \end{enumerate} */ + enum DOC_TYPE { OLD_BUNDLED=1, OLD_INDEXED, BUNDLED, INDIRECT, + SINGLE_PAGE, UNKNOWN_TYPE }; + enum THREAD_FLAGS { STARTED=1, FINISHED=2 }; + +protected: + /** Default creator. Please call functions \Ref{init}() or + \Ref{start_init}() before you start working with the #DjVuDocument#. + */ + DjVuDocument(void); +public: + + /// Virtual Destructor + virtual ~DjVuDocument(void); + + /** Initializes the #DjVuDocument# object using an existing document. + This function should be called once after creating the object. + The #url# should point to the real data, and the creator of the + document should be ready to return this data to the document + if it's not stored locally (in which case #DjVuDocument# can + access it itself). + + {\bf Initializing thread} + In a single-threaded application, the #start_init()# function performs + the complete initialization of the #DjVuDocument# before it returns. + In a multi-threaded application, though, it initializes some internal + variables, requests data for the document and starts a new + {\em initializing} thread, which is responsible for determining the + document type and structure and completing the initialization + process. This additional complication is justified in the case of + the DjVu plugin because performing initialization requires data and + in the plugin the data can be supplied by the main thread only. + Thus, if the initialization was completed by the main thread, the + plugin would run out of data and block. + + {\bf Stages of initialization} + Immediately after the #start_init()# function terminates, the + #DjVuDocument# object is ready for use. Its functionality will + not be complete (until the initializing thread finishes), but + the object is still very useful. Such functions as \Ref{get_page}() + or \Ref{get_djvu_file}() or \Ref{id_to_url}() may be called + before the initializing thread completes. This allows the DjVu + plugin start decoding as soon as possible without waiting for + all data to arrive. + + To query the current stage of initialization you can use + \Ref{get_doc_flags}() function or listen to the + #notify_doc_flags_changed()# notifications distributed with the help + of \Ref{DjVuPortcaster}. To wait for the initialization to + complete use \Ref{wait_for_complete_init}(). To stop initialization + call \Ref{stop_init}(). + + {\bf Querying data} + The query for data is done using the communication mechanism + provided by \Ref{DjVuPort} and \Ref{DjVuPortcaster}. If #port# + is not #ZERO#, then the request for data will be forwarded to it. + If it {\bf is} #ZERO# then #DjVuDocument# will create an internal + instance of \Ref{DjVuSimplePort} and will use it to access local + files and report errors to #stderr#. In short, if the document + file is stored on the local hard disk, and you're OK about reporting + errors to #stderr#, you may pass #ZERO# pointer to \Ref{DjVuPort} + as #DjVuDocument# can take care of this situation by itself. + + {\bf The URL} + Depending on the document type the #url# should point to: + \begin{itemize} + \item {\bf Old bundled} and {\bf New bundled} formats: to the + document itself. + \item {\bf Old indexed} format: to any page of the document. + \item {\bf New indirect} format: to the top-level file of the + document. If (like in the {\em old indexed} format) you + point the #url# to a page, the page {\em will} be decoded, + but it will {\em not} be recognized to be part of the + document. + \end{itemize} + + @param url The URL pointing to the document. If the document is + in a {\em bundled} format then the URL should point to it. + If the document is in the {\em old indexed} format then + URL may point to any page of this document. For {\em new + indirect} format the URL should point to the top-level + file of the document. + @param port If not #ZERO#, all requests and notifications will + be sent to it. Otherwise #DjVuDocument# will create an internal + instance of \Ref{DjVuSimplePort} for these purposes. + It's OK to make it #ZERO# if you're writing a command line + tool, which should work with files on the hard disk only + because #DjVuDocument# can access such files itself. + @param cache It's used to cache decoded \Ref{DjVuFile}s and + is actually useful in the plugin only. */ + void start_init(const GURL & url, GP<DjVuPort> port=0, + DjVuFileCache * cache=0); + + /** This creates a DjVuDocument without initializing it. */ + static GP<DjVuDocument> create_noinit(void) {return new DjVuDocument;} + + /** Create a version of DjVuDocument which has finished initializing. */ + static GP<DjVuDocument> create_wait( + const GURL &url, GP<DjVuPort> xport=0, DjVuFileCache * const xcache=0); + + /** Create a version of DjVuDocument which has begun initializing. */ + static GP<DjVuDocument> create( + const GURL &url, GP<DjVuPort> xport=0, DjVuFileCache * const xcache=0); + + /** Create a version of DjVuDocument which has begun initializing. */ + static GP<DjVuDocument> create( + GP<DataPool> pool, GP<DjVuPort> xport=0, DjVuFileCache * const xcache=0); + + /** Create a version of DjVuDocument which has begun initializing. */ + static GP<DjVuDocument> create( + const GP<ByteStream> &bs, GP<DjVuPort> xport=0, + DjVuFileCache * const xcache=0); + + /** Call this function when you don't need the #DjVuDocument# any more. + In a multi-threaded environment it will stop initialization + thread, if it is currently running. {\bf You will not be able + to start the initialization again. Thus, after calling this + function the document should not be used any more}. */ + void stop_init(void); + + /** Initializes the document. + + Contrary to \Ref{start_init}(), which just starts the initialization + thread in a multi-threaded environment, this function does not + return until the initialization completes (either successfully or + not). Basically, it calls \Ref{start_init}() and then + \Ref{wait_for_complete_init}(). + */ + void init(const GURL & url, GP<DjVuPort> port=0, + DjVuFileCache * cache=0); + + /** Returns #TRUE# if the initialization thread finished (does not + matter successfully or not). As soon as it happens, the document + becomes completely initialized and its every function should work + properly. Please refer to the description of \Ref{init}() function + and of the #DjVuDocument# class to learn about the initializing + stages. + + To wait for the initialization to complete use + \Ref{wait_for_complete_init}() function. + + To query the initialization stage use \Ref{get_flags}() function. + + To learn whether initialization was successful or not, + use \Ref{is_init_ok}() and \Ref{is_init_failed}(). + + {\bf Note:} In a single threaded application the initialization + completes before the \Ref{init}() function returns. */ + bool is_init_complete(void) const; + + /** Returns #TRUE# is the initialization thread finished successfully. + + See \Ref{is_init_complete}() and \Ref{wait_for_complete_init}() + for more details. */ + bool is_init_ok(void) const; + /** Forces compression with the next save_as function. */ + void set_needs_compression(void); + /** Returns #TRUE# if there are uncompressed pages in this document. */ + bool needs_compression(void) const; + /** Returns #TRUE# if this file must be renamed before saving. */ + bool needs_rename(void) const; + /** Returns #TRUE# if this file must be renamed before saving. */ + bool can_compress(void) const; + + /** Returns #TRUE# is the initialization thread failed. + + See \Ref{is_init_complete}() and \Ref{wait_for_complete_init}() + for more details. */ + bool is_init_failed(void) const; + + /** If the document has already learnt its type, the function will + returns it: #DjVuDocument::OLD_BUNDLED# or + #DjVuDocument::OLD_INDEXED# or #DjVuDocument::SINGLE_PAGE# or + #DjVuDocument:BUNDLED# or #DjVuDocument::INDIRECT#. The first + two formats are obsolete. Otherwise (if the type is unknown yet), + #UNKNOWN_TYPE# will be returned. + + {\bf Note:} To check the stage of the document initialization + use \Ref{get_flags}() or \Ref{is_init_complete}() functions. To + wait for the initialization to complete use \Ref{wait_for_complete_init}(). + For single threaded applications the initialization completes + before the \Ref{init}() function returns. */ + int get_doc_type(void) const; + + /** Returns the document flags. The flags describe the degree in which + the #DjVuDocument# object is initialized. Every time the flags + are changed, a #notify_doc_flags_changed()# notification is + distributed using the \Ref{DjVuPortcaster} communication + mechanism. + + {\bf Note:} To wait for the initialization to complete use + \Ref{wait_for_complete_init}(). For single threaded applications + the initialization completes before the \Ref{init}() function + returns. */ + long get_doc_flags(void) const; + + /** Returns #TRUE# if the document is in bundled format (either in + #DjVuDocument::OLD_BUNDLED# or #DjVuDocument::BUNDLED# formats). */ + bool is_bundled(void) const; + + /// Returns the URL passed to the \Ref{init}() function + GURL get_init_url(void) const; + + /// Returns a listing of id's used by this document. + GList<GUTF8String> get_id_list(void); + + /// Fill the id's into a GMap. + void map_ids( GMap<GUTF8String,void *> &map); + + /** Returns data corresponding to the URL passed to the \Ref{init}() + function. + + {\bf Note:} The pointer returned is guaranteed to be non-#ZERO# + only after the #DjVuDocument# learns its type (passes through + the first stage of initialization process). Please refer to + \Ref{init}() for details. */ + GP<DataPool> get_init_data_pool(void) const; + + /** @name Accessing pages */ + //@{ + /** Returns the number of pages in the document. If there is still + insufficient information about the document structure (initialization + has not finished yet), #1# will be returned. Please refer to + \Ref{init}() for details. */ + int get_pages_num(void) const; + + /** Translates the page number to the full URL of the page. This URL + is "artificial" for the {\em bundled} formats and is obtained + by appending the page name to the document's URL honoring possible + #;# and #?# in it. Negative page number has a special meaning for + #OLD_INDEXED# documents: it points to the URL, which the + #DjVuDocument# has been initialized with. For other formats this + is the same as page #0#. + + The function tries it best to map the page number to the URL. + Although, if the document structure has not been fully discovered + yet, an empty URL will be returned. Use \Ref{wait_for_complete_init}() + to wait until the document initialization completes. Refer to + \Ref{init}() for details. + + Depending on the document format, the function assumes, that there + is enough information to complete the request when: + \begin{itemize} + \item #OLD_INDEXED#: If #page_num<0#, #DOC_TYPE_KNOWN# flag must + be set. Otherwise #DOC_NDIR_KNOWN# must be set. + \item #OLD_BUNDLED#: If #page_num=0#, #DOC_DIR_KNOWN# flag must + be set. Otherwise #DOC_NDIR_KNOWN# flag must be set. + \item #INDIRECT# and #BUNDLED#: #DOC_DIR_KNOWN# flag must be set. + \end{itemize} */ + GURL page_to_url(int page_num) const; + /// Tranlate the page number to id... + GUTF8String page_to_id(int page_num) const + { return url_to_id(page_to_url(page_num)); } + /** Translates the page URL back to page number. Returns #-1# if the + page is not in the document or the document's structure + has not been learnt yet. + + Depending on the document format, the function starts working + properly as soon as: + \begin{itemize} + \item #OLD_INDEXED# and #OLD_BUNDLED# and #SINGLE_PAGE#: + #DOC_NDIR_KNOWN# is set + \item #INDIRECT# and #BUNDLED#: #DOC_DIR_KNOWN# is set. + \end{itemize} */ + int url_to_page(const GURL & url) const; + /// Map the specified url to it's id. + GUTF8String url_to_id(const GURL &url) const + { return url.fname(); } + + /** Translates the textual ID to the complete URL if possible. + + Depending on the document format the translation is done in the + following way: + \begin{itemize} + \item For #BUNDLED# and #INDIRECT# documents the function + scans the \Ref{DjVmDir} (the document directory) and + matches the ID against: + \begin{enumerate} + \item File ID from the \Ref{DjVmDir} + \item File name from the \Ref{DjVmDir} + \item File title from the \Ref{DjVmDir} + \end{enumerate} + Then for #BUNDLED# document the URL is obtained by + appending the #name# of the found file to the document's + URL. + + For #INDIRECT# documents the URL is obtained by + appending the #name# of the found file to the URL of + the directory containing the document. + \item For #OLD_BUNDLED# documents the function compares the ID + with internal name of every file inside the bundle and + composes an artificial URL by appending the file name to + the document's URL. + \item For #OLD_INDEXED# or #SINGLE_PAGE# documents the function + composes the URL by appending the ID to the URL of the + directory containing the document. + \end{itemize} + + If information obtained by the initialization thread is not + sufficient yet, the #id_to_url()# may return an empty URL. + Depending on the document type, the information is sufficient when + \begin{itemize} + \item #BUNDLED# and #INDIRECT#: #DOC_DIR_KNOWN# flag is set. + \item #OLD_BUNDLED# and #OLD_INDEXED# and #SINGLE_PAGE#: + #DOC_TYPE_KNOWN# flag is set. + \end{itemize} */ + GURL id_to_url(const GUTF8String &id) const; + /// Find out which page this id is... + int id_to_page(const GUTF8String &id) const + { return url_to_page(id_to_url(id)); } + + /** Returns \Ref{GP} pointer to \Ref{DjVuImage} corresponding to page + #page_num#. If caching is enabled, and there is a {\em fully decoded} + \Ref{DjVuFile} in the cache, the image will be reused and will + be returned fully decoded. Otherwise, if multi-threaded behavior + is allowed, and #sync# is set to #FALSE#, the decoding will be + started in a separate thread, which enables to do progressive + redisplay. Thus, in this case the image returned may be partially + decoded. + + Negative #page_num# has a special meaning for the {\em old indexed} + multipage documents: the #DjVuDocument# will start decoding of the + URL with which it has been initialized. For other formats page + #-1# is the same as page #0#. + + #DjVuDocument# can also connect the created page to the specified + #port# {\em before starting decoding}. This option will allow + the future owner of \Ref{DjVuImage} to receive all messages and + requests generated during its decoding. + + If this function is called before the document's structure becomes + known (the initialization process completes), the \Ref{DjVuFile}, + which the returned image will be attached to, will be assigned a + temporary artificial URL, which will be corrected as soon as enough + information becomes available. The trick prevents the main thread + from blocking and in some cases helps to start decoding earlier. + The URL is corrected and decoding will start as soon as + #DjVuDocument# passes some given stages of initialization and + \Ref{page_to_url}(), \Ref{id_to_url}() functions start working + properly. Please look through their description for details. + + {\bf Note:} To wait for the initialization to complete use + \Ref{wait_for_complete_init}(). For single threaded applications + the initialization completes before the \Ref{init}() function + returns. + + @param page_num Number of the page to be decoded + @param sync When set to #TRUE# the function will not return + until the page is completely decoded. Otherwise, + in a multi-threaded program, this function will + start decoding in a new thread and will return + a partially decoded image. Refer to + \Ref{DjVuImage::wait_for_complete_decode}() and + \Ref{DjVuFile::is_decode_ok}(). + @param port A pointer to \Ref{DjVuPort}, that the created image + will be connected to. */ + GP<DjVuImage> get_page(int page_num, bool sync=true, DjVuPort * port=0) const; + GP<DjVuImage> get_page(int page_num, bool sync=true, DjVuPort * port=0) + { return const_cast<const DjVuDocument *>(this)->get_page(page_num,sync,port); } + + /** Returns \Ref{GP} pointer to \Ref{DjVuImage} corresponding to the + specified ID. This function behaves exactly as the #get_page()# + function above. The only thing worth mentioning here is how the #ID# + parameter is treated. + + First of all the function checks, if the ID contains a number. + If so, it just calls the #get_page()# function above. If ID is + #ZERO# or just empty, page number #-1# is assumed. Otherwise + the ID is translated to the URL using \Ref{id_to_url}(). */ + GP<DjVuImage> get_page(const GUTF8String &id, bool sync=true, DjVuPort * port=0); + + /** Returns \Ref{DjVuFile} corresponding to the specified page. + Normally it translates the page number to the URL using + \Ref{page_to_url}() and then creates \Ref{DjVuFile} initializing + it with data from the URL. + + The behavior becomes different, though in the case when the + document structure is unknown at the moment this function is called. + In this situations it invents a temporary URL, creates a + \Ref{DjVuFile}, initializes it with this URL and returns + immediately. The caller may start decoding the file right away + (if necessary). The decoding will block but will automatically + continue as soon as enough information is collected about the + document. This trick should be quite transparent to the user and + helps to prevent the main thread from blocking. The decoding will + unblock and this function will stop using this "trick" as soon + as #DjVuDocument# passes some given stages of initialization and + \Ref{page_to_url}(), \Ref{id_to_url}() functions start working + properly. + + If #dont_create# is #FALSE# the function will return the file + only if it already exists. + + {\bf Note:} To wait for the initialization to complete use + \Ref{wait_for_complete_init}(). For single threaded applications + the initialization completes before the \Ref{init}() function + returns. */ + GP<DjVuFile> get_djvu_file(int page_num, bool dont_create=false) const; + GP<DjVuFile> get_djvu_file(int page_num, bool dont_create=false) + { return const_cast<const DjVuDocument *>(this)->get_djvu_file(page_num,dont_create); } + + + /** Returns \Ref{DjVuFile} corresponding to the specified ID. + This function behaves exactly as the #get_djvu_file()# function + above. The only thing worth mentioning here is how the #ID# + parameter is treated. + + First off, \Ref{id_to_url}() is called. If not successfull, + the function checks, if the ID contains a number. + If so, it just calls the #get_djvu_file()# function above. If ID is + #ZERO# or just empty, page number #-1# is assumed. + + If #dont_create# is #FALSE# the function will return the file + only if it already exists. */ + GP<DjVuFile> get_djvu_file(const GUTF8String &id, bool dont_create=false); + GP<DjVuFile> get_djvu_file(const GURL &url, bool dont_create=false); + /** Returns a \Ref{DataPool} containing one chunk #TH44# with + the encoded thumbnail for the specified page. The function + first looks for thumbnails enclosed into the document and if + it fails to find one, it decodes the required page and creates + the thumbnail on the fly (unless #dont_decode# is true). + + {\bf Note:} It may happen that the returned \Ref{DataPool} will + not contain all the data you need. In this case you will need + to install a trigger into the \Ref{DataPool} to learn when the + data actually arrives. */ + virtual GP<DataPool> get_thumbnail(int page_num, bool dont_decode); + /* Will return gamma correction, which was used when creating + thumbnail images. If you need other gamma correction, you will + need to correct the thumbnails again. */ + float get_thumbnails_gamma(void) const; + //@} + + /** Waits until the document initialization process finishes. + It can finish either successfully or not. Use \Ref{is_init_ok}() + and \Ref{is_init_failed}() to learn the result code. + + As described in \Ref{start_init}(), for multi-threaded applications the + initialization is carried out in parallel with the main thread. + This function blocks the calling thread until the initializing + thread reads enough data, receives information about the document + format and exits. This function returns #true# if the + initialization is successful. You can use \Ref{get_flags}() or + \Ref{is_init_complete}() to check more precisely the degree of + initialization. Use \Ref{stop_init}() to interrupt initialization. */ + bool wait_for_complete_init(void); + + /** Wait until we known the number of pages and return. */ + int wait_get_pages_num(void) const; + + /// Returns cache being used. + DjVuFileCache * get_cache(void) const; + + /** @name Saving document to disk */ + //@{ + /** Returns pointer to the \Ref{DjVmDoc} class, which can save the + document contents on the hard disk in one of the two new formats: + {\em bundled} and {\em indirect}. You may also want to look + at \Ref{write}() and \Ref{expand}() if you are interested in + how to save the document. + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. */ + GP<DjVmDoc> get_djvm_doc(void); + /** Saves the document in the {\em new bundled} format. All the data + is "bundled" into one file and this file is written into the + passed stream. + + If #force_djvm# is #TRUE# then even one page documents will be + saved in the #DJVM BUNDLED# format (inside a #FORM:DJVM#); + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. */ + virtual void write(const GP<ByteStream> &str, bool force_djvm=false); + /** Always save as bundled, renaming any files conflicting with the + the names in the supplied GMap. */ + virtual void write(const GP<ByteStream> &str, + const GMap<GUTF8String,void *> &reserved); + /** Saves the document in the {\em new indirect} format when every + page and component are stored in separate files. This format + is ideal for web publishing because it allows direct access to + any page and component. In addition to it, a top-level file + containing the list of all components will be created. To view + the document later in the plugin or in the viewer one should + load the top-level file. + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. + + @param codebase - Name of the directory which the document should + be expanded into. + @param idx_name - Name of the top-level file containing the document + directory (basically, list of all files composing the document). + */ + void expand(const GURL &codebase, const GUTF8String &idx_name); + /** This function can be used instead of \Ref{write}() and \Ref{expand}(). + It allows to save the document either in the new #BUNDLED# format + or in the new #INDIRECT# format depending on the value of parameter + #bundled#. + + Depending on the document's type, the meaning of #where# is: + \begin{itemize} + \item For #BUNDLED# documents this is the name of the file + \item For #INDIRECT# documents this is the name of top-level + index file. All document files will be saved into the + save directory where the index file will resize. */ + virtual void save_as(const GURL &where, const bool bundled=0); + //@} + /** Returns pointer to the internal directory of the document, if it + is in one of the new formats: #BUNDLED# or #INDIRECT#. + Otherwise (if the format of the input document is obsolete), + #ZERO# is returned. + + #ZERO# will also be returned if the initializing thread has not + learnt enough information about the document (#DOC_DIR_KNOWN# has + not been set yet). Check \Ref{is_init_complete}() and \Ref{init}() + for details. */ + GP<DjVmDir> get_djvm_dir(void) const; + /** Returns pointer to the document bookmarks. + This applies to #BUNDLED# and #INDIRECT# documents. + + #ZERO# will also be returned if the initializing thread has not + learnt enough information about the document (#DOC_DIR_KNOWN# has + not been set yet). Check \Ref{is_init_complete}() and \Ref{init}() + for details. */ + GP<DjVmNav> get_djvm_nav(void) const; + /** Returns pointer to the internal directory of the document, if it + is in obsolete #OLD_BUNDLED# format. + + #ZERO# will also be returned if the initializing thread has not + learnt enough information about the document (#DOC_DIR_KNOWN# has + not been set yet). Check \Ref{is_init_complete}() and \Ref{init}() + for details. */ + GP<DjVmDir0> get_djvm_dir0(void) const; + /** Returns pointer to {\em navigation directory} of the document. + The navigation directory is a DjVu file containing only one + chunk #NDIR# inside a #FORM:DJVI# with the list of all + document pages. */ + GP<DjVuNavDir> get_nav_dir(void) const; + + /// Create a complete DjVuXML file. + void writeDjVuXML(const GP<ByteStream> &gstr_out,int flags) const; + + /// Returns TRUE if #class_name# is #"DjVuDocument"# or #"DjVuPort"# + virtual bool inherits(const GUTF8String &class_name) const; + + /// Converts the specified id to a URL. + virtual GURL id_to_url(const DjVuPort * source, const GUTF8String &id); + virtual GP<DjVuFile> id_to_file(const DjVuPort * source, const GUTF8String &id); + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + virtual void notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask); + + virtual GList<GURL> get_url_names(void); + virtual void set_recover_errors(ErrorRecoveryAction=ABORT); + virtual void set_verbose_eof(bool=true); + + static void set_compress_codec( + void (*codec)(GP<ByteStream> &, const GURL &where, bool bundled)); + + static void set_import_codec( + void (*codec)(GP<DataPool> &,const GURL &url,bool &, bool &)); + +protected: + static void (*djvu_import_codec) ( + GP<DataPool> &pool, const GURL &url,bool &needs_compression, bool &needs_rename ); + static void (*djvu_compress_codec) ( + GP<ByteStream> &bs, const GURL &where, bool bundled); + virtual GP<DjVuFile> url_to_file(const GURL & url, bool dont_create=false) const; + GURL init_url; + GP<DataPool> init_data_pool; + GP<DjVmDir> djvm_dir; // New-style DjVm directory + GP<DjVmNav> djvm_nav; + int doc_type; + bool needs_compression_flag; + bool can_compress_flag; + bool needs_rename_flag; + + + + bool has_url_names; + GCriticalSection url_names_lock; + GList<GURL> url_names; + ErrorRecoveryAction recover_errors; + bool verbose_eof; +public: + class UnnamedFile; // This really should be protected ... + class ThumbReq; // This really should be protected ... +protected: + bool init_started; + GSafeFlags flags; + GSafeFlags init_thread_flags; + DjVuFileCache * cache; + GP<DjVuSimplePort> simple_port; + + GP<DjVmDir0> djvm_dir0; // Old-style DjVm directory + GP<DjVuNavDir> ndir; // Old-style navigation directory + GUTF8String first_page_name;// For OLD_BUNDLED docs only + + // The following is used in init() and destructor to query NDIR + // DO NOT USE FOR ANYTHING ELSE. THE FILE IS ZEROED IMMEDIATELY + // AFTER IT'S NO LONGER NEEDED. If you don't zero it, ~DjVuDocument() + // will kill it, which is a BAD thing if the file's already in cache. + GP<DjVuFile> ndir_file; + + GPList<UnnamedFile> ufiles_list; + GCriticalSection ufiles_lock; + + GPList<ThumbReq> threqs_list; + GCriticalSection threqs_lock; + + GP<DjVuDocument> init_life_saver; + + static const float thumb_gamma; + + // Reads document contents in another thread trying to determine + // its type and structure + GThread init_thr; + static void static_init_thread(void *); + void init_thread(void); + + void check() const; + + void process_threqs(void); + GP<ThumbReq> add_thumb_req(const GP<ThumbReq> & thumb_req); + + void add_to_cache(const GP<DjVuFile> & f); + void check_unnamed_files(void); + GUTF8String get_int_prefix(void) const; + void set_file_aliases(const DjVuFile * file); + GURL invent_url(const GUTF8String &name) const; +}; + +class DjVuDocument::UnnamedFile : public GPEnabled +{ +public: + enum { ID, PAGE_NUM }; + int id_type; + GUTF8String id; + int page_num; + GURL url; + GP<DjVuFile> file; + GP<DataPool> data_pool; +protected: + UnnamedFile(int xid_type, const GUTF8String &xid, int xpage_num, const GURL & xurl, + const GP<DjVuFile> & xfile) : + id_type(xid_type), id(xid), page_num(xpage_num), url(xurl), file(xfile) {} + friend class DjVuDocument; +}; + +class DjVuDocument::ThumbReq : public GPEnabled +{ +public: + int page_num; + GP<DataPool> data_pool; + + // Either of the next two blocks should present + GP<DjVuFile> image_file; + + int thumb_chunk; + GP<DjVuFile> thumb_file; +protected: + ThumbReq(int xpage_num, const GP<DataPool> & xdata_pool) : + page_num(xpage_num), data_pool(xdata_pool) {} + friend class DjVuDocument; +}; + +inline void +DjVuDocument::init(const GURL &url, GP<DjVuPort> port, DjVuFileCache *cache) +{ + start_init(url,port,cache); + wait_for_complete_init(); +} + +inline GP<DjVuDocument> +DjVuDocument::create( + const GURL &url, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + DjVuDocument *doc=new DjVuDocument; + GP<DjVuDocument> retval=doc; + doc->start_init(url,xport,xcache); + return retval; +} + +inline bool +DjVuDocument::is_init_complete(void) const +{ + return (flags & (DOC_INIT_OK | DOC_INIT_FAILED))!=0; +} + +inline bool +DjVuDocument::is_init_ok(void) const +{ + return (flags & DOC_INIT_OK)!=0; +} + +inline void +DjVuDocument::set_needs_compression(void) +{ + needs_compression_flag=true; +} + +inline bool +DjVuDocument::needs_compression(void) const +{ + return needs_compression_flag; +} + +inline bool +DjVuDocument::needs_rename(void) const +{ + return needs_rename_flag; +} + +inline bool +DjVuDocument::can_compress(void) const +{ + return can_compress_flag; +} + +inline bool +DjVuDocument::is_init_failed(void) const +{ + return (flags & DOC_INIT_FAILED)!=0; +} + +inline int +DjVuDocument::get_doc_type(void) const { return doc_type; } + +inline long +DjVuDocument::get_doc_flags(void) const { return flags; } + +inline bool +DjVuDocument::is_bundled(void) const +{ + return doc_type==BUNDLED || doc_type==OLD_BUNDLED; +} + +inline GURL +DjVuDocument::get_init_url(void) const { return init_url; } + +inline GP<DataPool> +DjVuDocument::get_init_data_pool(void) const { return init_data_pool; } + +inline bool +DjVuDocument::inherits(const GUTF8String &class_name) const +{ + return + (GUTF8String("DjVuDocument") == class_name) || + DjVuPort::inherits(class_name); +// !strcmp("DjVuDocument", class_name) || +// DjVuPort::inherits(class_name); +} + +inline float +DjVuDocument::get_thumbnails_gamma(void) const +{ + return thumb_gamma; +} + +inline DjVuFileCache * +DjVuDocument::get_cache(void) const +{ + return cache; +} + +inline GP<DjVmDir> +DjVuDocument::get_djvm_dir(void) const +{ + if (doc_type==SINGLE_PAGE) + G_THROW( ERR_MSG("DjVuDocument.no_dir") ); + if (doc_type!=BUNDLED && doc_type!=INDIRECT) + G_THROW( ERR_MSG("DjVuDocument.obsolete") ); + return djvm_dir; +} + +inline GP<DjVmNav> +DjVuDocument::get_djvm_nav(void) const +{ + if (doc_type==BUNDLED || doc_type==INDIRECT) + return djvm_nav; + return 0; +} + +inline GP<DjVmDir0> +DjVuDocument::get_djvm_dir0(void) const +{ + if (doc_type!=OLD_BUNDLED) + G_THROW( ERR_MSG("DjVuDocument.old_bundle") ); + return djvm_dir0; +} + +inline GP<DjVuNavDir> +DjVuDocument::get_nav_dir(void) const +{ + return ndir; +} + +inline void +DjVuDocument::set_recover_errors(ErrorRecoveryAction recover) +{ + recover_errors=recover; +} + +inline void +DjVuDocument::set_verbose_eof(bool verbose) +{ + verbose_eof=verbose; +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.cpp new file mode 100644 index 00000000..2d977be1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.cpp @@ -0,0 +1,353 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDumpHelper.cpp,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuDumpHelper.h" +#include "DataPool.h" +#include "DjVmDir.h" +#include "DjVuInfo.h" +#include "IFFByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#ifdef putchar +#undef putchar +#endif + +struct DjVmInfo +{ + GP<DjVmDir> dir; + GPMap<int,DjVmDir::File> map; +}; + +inline static void +putchar(ByteStream & str, char ch) +{ + str.write(&ch, 1); +} + +// ---------- ROUTINES FOR SUMMARIZING CHUNK DATA + +static void +display_djvu_info(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t size, DjVmInfo&, int) +{ + GP<DjVuInfo> ginfo=DjVuInfo::create(); + DjVuInfo &info=*ginfo; + info.decode(*iff.get_bytestream()); + if (size >= 4) + out_str.format( "DjVu %dx%d", info.width, info.height); + if (size >= 5) + out_str.format( ", v%d", info.version); + if (size >= 8) + out_str.format( ", %d dpi", info.dpi); + if (size >= 8) + out_str.format( ", gamma=%3.1f", info.gamma); +} + +static void +display_djbz(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "JB2 shared dictionary"); +} + +static void +display_fgbz(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "JB2 colors data"); +} + +static void +display_sjbz(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "JB2 bilevel data"); +} + +static void +display_smmr(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "G4/MMR stencil data"); +} + +static void +display_iw4(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + GP<ByteStream> gbs = iff.get_bytestream(); + unsigned char serial = gbs->read8(); + unsigned char slices = gbs->read8(); + out_str.format( "IW4 data #%d, %d slices", serial+1, slices); + if (serial == 0) + { + unsigned char major = gbs->read8(); + unsigned char minor = gbs->read8(); + unsigned char xhi = gbs->read8(); + unsigned char xlo = gbs->read8(); + unsigned char yhi = gbs->read8(); + unsigned char ylo = gbs->read8(); + out_str.format( ", v%d.%d (%s), %dx%d", major & 0x7f, minor, + (major & 0x80 ? "b&w" : "color"), (xhi<<8)+xlo, (yhi<<8)+ylo ); + } +} + +static void +display_djvm_dirm(ByteStream & out_str, IFFByteStream & iff, + GUTF8String head, size_t, DjVmInfo& djvminfo, int) +{ + GP<DjVmDir> dir = DjVmDir::create(); + dir->decode(iff.get_bytestream()); + GPList<DjVmDir::File> list = dir->get_files_list(); + if (dir->is_indirect()) + { + out_str.format( "Document directory (indirect, %d files %d pages)", + dir->get_files_num(), dir->get_pages_num()); + for (GPosition p=list; p; ++p) + out_str.format( "\n%s%s -> %s", (const char*)head, + (const char*)list[p]->get_load_name(), (const char*)list[p]->get_save_name() ); + } + else + { + out_str.format( "Document directory (bundled, %d files %d pages)", + dir->get_files_num(), dir->get_pages_num()); + djvminfo.dir = dir; + djvminfo.map.empty(); + for (GPosition p=list; p; ++p) + djvminfo.map[list[p]->offset] = list[p]; + } +} + +static void +display_th44(ByteStream & out_str, IFFByteStream & iff, + GUTF8String, size_t, DjVmInfo & djvminfo, int counter) +{ + int start_page=-1; + if (djvminfo.dir) + { + GPList<DjVmDir::File> files_list=djvminfo.dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> frec=files_list[pos]; + if (iff.tell()>=frec->offset && + iff.tell()<frec->offset+frec->size) + { + while(pos && !files_list[pos]->is_page()) + ++pos; + if (pos) + start_page=files_list[pos]->get_page_num(); + break; + } + } + } + if (start_page>=0) + out_str.format( "Thumbnail icon for page %d", start_page+counter+1); + else + out_str.format( "Thumbnail icon"); +} + +static void +display_incl(ByteStream & out_str, IFFByteStream & iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + GUTF8String name; + char ch; + while(iff.read(&ch, 1) && ch!='\n') + name += ch; + out_str.format( "Indirection chunk --> {%s}", (const char *) name); +} + +static void +display_anno(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "Page annotation"); + GUTF8String id; + iff.short_id(id); + out_str.format( " (hyperlinks, etc.)"); +} + +static void +display_text(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "Hidden text"); + GUTF8String id; + iff.short_id(id); + out_str.format( " (text, etc.)"); +} + +struct displaysubr +{ + const char *id; + void (*subr)(ByteStream &, IFFByteStream &, GUTF8String, + size_t, DjVmInfo&, int counter); +}; + +static displaysubr disproutines[] = +{ + { "DJVU.INFO", display_djvu_info }, + { "DJVU.Smmr", display_smmr }, + { "DJVU.Sjbz", display_sjbz }, + { "DJVU.Djbz", display_djbz }, + { "DJVU.FG44", display_iw4 }, + { "DJVU.BG44", display_iw4 }, + { "DJVU.FGbz", display_fgbz }, + { "DJVI.Sjbz", display_sjbz }, + { "DJVI.Djbz", display_djbz }, + { "DJVI.FGbz", display_fgbz }, + { "DJVI.FG44", display_iw4 }, + { "DJVI.BG44", display_iw4 }, + { "BM44.BM44", display_iw4 }, + { "PM44.PM44", display_iw4 }, + { "DJVM.DIRM", display_djvm_dirm }, + { "THUM.TH44", display_th44 }, + { "INCL", display_incl }, + { "ANTa", display_anno }, + { "ANTz", display_anno }, + { "TXTa", display_text }, + { "TXTz", display_text }, + { 0, 0 }, +}; + +// ---------- ROUTINES FOR DISPLAYING CHUNK STRUCTURE + +static void +display_chunks(ByteStream & out_str, IFFByteStream &iff, + const GUTF8String &head, DjVmInfo djvminfo) +{ + size_t size; + GUTF8String id, fullid; + GUTF8String head2 = head + " "; + GPMap<int,DjVmDir::File> djvmmap; + int rawoffset; + GMap<GUTF8String, int> counters; + + while ((size = iff.get_chunk(id, &rawoffset))) + { + if (!counters.contains(id)) counters[id]=0; + else counters[id]++; + + GUTF8String msg; + msg.format("%s%s [%d] ", (const char *)head, (const char *)id, size); + out_str.format( "%s", (const char *)msg); + // Display DJVM is when adequate + if (djvminfo.dir) + { + GP<DjVmDir::File> rec = djvminfo.map[rawoffset]; + if (rec) + out_str.format( "{%s}", (const char*) rec->get_load_name()); + } + // Test chunk type + iff.full_id(fullid); + for (int i=0; disproutines[i].id; i++) + if (fullid == disproutines[i].id || id == disproutines[i].id) + { + int n = msg.length(); + while (n++ < 14+(int) head.length()) putchar(out_str, ' '); + if (!iff.composite()) out_str.format( " "); + (*disproutines[i].subr)(out_str, iff, head2, + size, djvminfo, counters[id]); + break; + } + // Default display of composite chunk + out_str.format( "\n"); + if (iff.composite()) + display_chunks(out_str, iff, head2, djvminfo); + // Terminate + iff.close_chunk(); + } +} + +GP<ByteStream> +DjVuDumpHelper::dump(const GP<DataPool> & pool) +{ + return dump(pool->get_stream()); +} + +GP<ByteStream> +DjVuDumpHelper::dump(GP<ByteStream> gstr) +{ + GP<ByteStream> out_str=ByteStream::create(); + GUTF8String head=" "; + GP<IFFByteStream> iff=IFFByteStream::create(gstr); + DjVmInfo djvminfo; + display_chunks(*out_str, *iff, head, djvminfo); + return out_str; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.h b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.h new file mode 100644 index 00000000..33be56c3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.h @@ -0,0 +1,126 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDumpHelper.h,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUDUMPHELPER_H +#define _DJVUDUMPHELPER_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVuDupmHelper.h + This file contains code capable of generating information on + DjVu documents without actually decoding them. The code has + been extracted from a command line utility \Ref{djvudump.cpp} + for use in the DjVu plugin. + @memo + DjVu Dump Helper. + @author + L\'eon Bottou <leonb@research.att.com> -- as a separate program.\\ + Andrei Erofeev <eaf@geocities.com> -- as a class. + @version + #$Id: DjVuDumpHelper.h,v 1.9 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DataPool; +class ByteStream; + +/** DjVuDumpHelper. + This class can dump information on any DjVu file without decoding it. + Based upon old \Ref{djvudump.cpp} code. + */ + +class DjVuDumpHelper +{ +public: + /// Default constructor + DjVuDumpHelper(void) {} + /// Destructor + ~DjVuDumpHelper(void) {} + /** Interprets the file passed in the \Ref{DataPool}, and returns + the results in \Ref{ByteStream}. */ + GP<ByteStream> dump(const GP<DataPool> & pool); + /** Interprets the file passed in the \Ref{ByteStream}, and returns + the results in \Ref{ByteStream}. */ + GP<ByteStream> dump(GP<ByteStream> str); +}; + + +//@} + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.cpp new file mode 100644 index 00000000..e7c74b84 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.cpp @@ -0,0 +1,174 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuErrorList.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuErrorList.h" +#include "DjVmDoc.h" +#include "GException.h" +#include "GContainer.h" +#include "GOS.h" +#include "DataPool.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +DjVuErrorList::DjVuErrorList() {} + +GURL +DjVuErrorList::set_stream(GP<ByteStream> xibs) +{ + GUTF8String name; + static unsigned long serial=0; + pool=DataPool::create(xibs); + name.format("data://%08lx/%08lx.djvu", + ++serial,(unsigned long)(size_t)((const ByteStream *)xibs)); + pool_url=GURL::UTF8(name); + return pool_url; +} + +bool +DjVuErrorList::notify_error(const DjVuPort * source, const GUTF8String & msg) +{ + Errors.append(msg); + return 1; +} + +bool +DjVuErrorList::notify_status(const DjVuPort * source, const GUTF8String &msg) +{ + Status.append(msg); + return 1; +} + +GUTF8String +DjVuErrorList::GetError(void) +{ + GUTF8String PrevError; + GPosition pos; + if((pos=Errors)) + { + PrevError=Errors[pos]; + Errors.del(pos); + } + return PrevError; +} + +GUTF8String +DjVuErrorList::GetStatus(void) +{ + GUTF8String PrevStatus; + GPosition pos; + if((pos=Status)) + { + PrevStatus=Status[pos]; + Status.del(pos); + } + return PrevStatus; +} + +GP<DataPool> +DjVuErrorList::request_data(const DjVuPort * source, const GURL & url) +{ + GP<DataPool> retval; + G_TRY + { + if (pool && url.protocol().downcase() == "data") + { + if(url == pool_url) + { + retval=pool; + }else if(url.base() == pool_url) + { + GUTF8String name=url.fname(); + GP<DjVmDoc> doc=DjVmDoc::create(); + GP<ByteStream> bs=pool->get_stream(); + doc->read(*bs); + retval=doc->get_data(name); + } + }else if (url.is_local_file_url()) + { +// GUTF8String fname=GOS::url_to_filename(url); +// if (GOS::basename(fname)=="-") fname="-"; + retval=DataPool::create(url); + } + } + G_CATCH_ALL + { + retval=0; + } G_ENDCATCH; + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.h b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.h new file mode 100644 index 00000000..885e76aa --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.h @@ -0,0 +1,194 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuErrorList.h,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUERRORLIST_H +#define _DJVUERRORLIST_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuPort.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVuErrorList.h + This file implements a very simple class for redirecting port caster + messages that would normally end up on stderr to a double linked list. + + @memo DjVuErrorList class. + @author Bill C Riemers <docbill@sourceforge.net> + @version #$Id: DjVuErrorList.h,v 1.9 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** #DjVuErrorList# provides a convenient way to redirect error messages + from classes derived from DjVuPort to a list that can be accessed at + any time. */ + +class DjVuErrorList : public DjVuSimplePort +{ +protected: + /// The normal port caster constructor. + DjVuErrorList(void); +public: + static GP<DjVuErrorList> create(void) {return new DjVuErrorList();} + + /// This constructor allows the user to specify the ByteStream. + GURL set_stream(GP<ByteStream>); + + /// Append all error messages to the list + virtual bool notify_error(const DjVuPort * source, const GUTF8String & msg); + + /// Append all status messages to the list + virtual bool notify_status(const DjVuPort * source, const GUTF8String & msg); + + /// Add a new class to have its messages redirected here. + inline void connect( const DjVuPort &port); + + /// Get the listing of errors, and clear the list. + inline GList<GUTF8String> GetErrorList(void); + + /// Just clear the list. + inline void ClearError(void); + + /// Get one error message and clear that message from the list. + GUTF8String GetError(void); + + /// Check if there are anymore error messages. + inline bool HasError(void) const; + + /// Get the listing of status messages, and clear the list. + inline GList<GUTF8String> GetStatusList(void); + + /// Just clear the list. + inline void ClearStatus(void); + + /// Get one status message and clear that message from the list. + GUTF8String GetStatus(void); + + /// Check if there are any more status messages. + inline bool HasStatus(void) const; + + /** This gets the data. We can't use the simple port's request + data since we want to allow the user to specify the ByteStream. */ + virtual GP<DataPool> request_data ( + const DjVuPort * source, const GURL & url ); + +private: + GURL pool_url; + GP<DataPool> pool; + GList<GUTF8String> Errors; + GList<GUTF8String> Status; +private: //dummy stuff + static GURL set_stream(ByteStream *); +}; + +inline void +DjVuErrorList::connect( const DjVuPort &port ) +{ get_portcaster()->add_route(&port, this); } + +inline GList<GUTF8String> +DjVuErrorList::GetErrorList(void) +{ + GList<GUTF8String> retval=(const GList<GUTF8String>)Errors; + Errors.empty(); + return retval; +} + +inline void +DjVuErrorList::ClearError(void) +{ Errors.empty(); } + +inline GList<GUTF8String> +DjVuErrorList::GetStatusList(void) +{ + GList<GUTF8String> retval=(const GList<GUTF8String>)Status; + Status.empty(); + return retval; +} + +inline void +DjVuErrorList::ClearStatus(void) +{ Status.empty(); } + +inline bool +DjVuErrorList::HasError(void) const +{ return !Errors.isempty(); } + +inline bool +DjVuErrorList::HasStatus(void) const +{ return !Status.isempty(); } + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFile.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuFile.cpp new file mode 100644 index 00000000..73e3a9c2 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFile.cpp @@ -0,0 +1,2831 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFile.cpp,v 1.11 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuFile.h" +#include "IFFByteStream.h" +#include "GOS.h" +#include "MMRDecoder.h" +#ifdef NEED_JPEG_DECODER +#include "JPEGDecoder.h" +#endif +#include "DjVuAnno.h" +#include "DjVuText.h" +#include "DataPool.h" +#include "JB2Image.h" +#include "IW44Image.h" +#include "DjVuNavDir.h" +#ifndef NEED_DECODER_ONLY +#include "BSByteStream.h" +#endif // NEED_DECODER_ONLY + +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#define STRINGIFY(x) STRINGIFY_(x) +#define STRINGIFY_(x) #x + + +#define REPORT_EOF(x) \ + {G_TRY{G_THROW( ByteStream::EndOfFile );}G_CATCH(ex){report_error(ex,(x));}G_ENDCATCH;} + +static GP<GPixmap> (*djvu_decode_codec)(ByteStream &bs)=0; + +class ProgressByteStream : public ByteStream +{ +public: + ProgressByteStream(const GP<ByteStream> & xstr) : str(xstr), + last_call_pos(0) {} + virtual ~ProgressByteStream() {} + + virtual size_t read(void *buffer, size_t size) + { + int rc=0; + // G_TRY {} CATCH; block here is merely to avoid egcs internal error + G_TRY { + int cur_pos=str->tell(); + if (progress_cb && (last_call_pos/256!=cur_pos/256)) + { + progress_cb(cur_pos, progress_cl_data); + last_call_pos=cur_pos; + } + rc=str->read(buffer, size); + } G_CATCH_ALL { + G_RETHROW; + } G_ENDCATCH; + return rc; + } + virtual size_t write(const void *buffer, size_t size) + { + return str->write(buffer, size); + } + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false) + { + return str->seek(offset, whence); + } + virtual long tell(void ) const { return str->tell(); } + + void set_progress_cb(void (* xprogress_cb)(int, void *), + void * xprogress_cl_data) + { + progress_cb=xprogress_cb; + progress_cl_data=xprogress_cl_data; + } +private: + GP<ByteStream> str; + void * progress_cl_data; + void (* progress_cb)(int pos, void *); + int last_call_pos; + + // Cancel C++ default stuff + ProgressByteStream & operator=(const ProgressByteStream &); +}; + + +DjVuFile::DjVuFile() +: file_size(0), recover_errors(ABORT), verbose_eof(false), chunks_number(-1), +initialized(false) +{ +} + +void +DjVuFile::check() const +{ + if (!initialized) + G_THROW( ERR_MSG("DjVuFile.not_init") ); +} + +GP<DjVuFile> +DjVuFile::create( + const GP<ByteStream> & str, const ErrorRecoveryAction recover_errors, + const bool verbose_eof ) +{ + DjVuFile *file=new DjVuFile(); + GP<DjVuFile> retval=file; + file->set_recover_errors(recover_errors); + file->set_verbose_eof(verbose_eof); + file->init(str); + return retval; +} + +void +DjVuFile::init(const GP<ByteStream> & str) +{ + DEBUG_MSG("DjVuFile::DjVuFile(): ByteStream constructor\n"); + DEBUG_MAKE_INDENT(3); + + if (initialized) + G_THROW( ERR_MSG("DjVuFile.2nd_init") ); + if (!get_count()) + G_THROW( ERR_MSG("DjVuFile.not_secured") ); + + file_size=0; + decode_thread=0; + + // Read the data from the stream + data_pool=DataPool::create(str); + + // Construct some dummy URL + GUTF8String buffer; + buffer.format("djvufile:/%p.djvu", this); + DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)buffer<<"\n"); + url=GURL::UTF8(buffer); + + // Set it here because trigger will call other DjVuFile's functions + initialized=true; + + // Add (basically - call) the trigger + data_pool->add_trigger(-1, static_trigger_cb, this); +} + +GP<DjVuFile> +DjVuFile::create( + const GURL & xurl, GP<DjVuPort> port, + const ErrorRecoveryAction recover_errors, const bool verbose_eof ) +{ + DjVuFile *file=new DjVuFile(); + GP<DjVuFile> retval=file; + file->set_recover_errors(recover_errors); + file->set_verbose_eof(verbose_eof); + file->init(xurl,port); + return retval; +} + +void +DjVuFile::init(const GURL & xurl, GP<DjVuPort> port) +{ + DEBUG_MSG("DjVuFile::init(): url='" << xurl << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (initialized) + G_THROW( ERR_MSG("DjVuFile.2nd_init") ); + if (!get_count()) + G_THROW( ERR_MSG("DjVuFile.not_secured") ); + if (xurl.is_empty()) + G_THROW( ERR_MSG("DjVuFile.empty_URL") ); + + url = xurl; + DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)url<<"\n"); + file_size=0; + decode_thread=0; + + DjVuPortcaster * pcaster=get_portcaster(); + + // We need it 'cause we're waiting for our own termination in stop_decode() + pcaster->add_route(this, this); + if (!port) + port = simple_port = new DjVuSimplePort(); + pcaster->add_route(this, port); + + // Set it here because trigger will call other DjVuFile's functions + initialized=true; + + if (!(data_pool=DataPool::create(pcaster->request_data(this, url)))) + G_THROW( ERR_MSG("DjVuFile.no_data") "\t"+url.get_string()); + data_pool->add_trigger(-1, static_trigger_cb, this); +} + +DjVuFile::~DjVuFile(void) +{ + DEBUG_MSG("DjVuFile::~DjVuFile(): destroying...\n"); + DEBUG_MAKE_INDENT(3); + + // No more messages. They may result in adding this file to a cache + // which will be very-very bad as we're being destroyed + get_portcaster()->del_port(this); + + // Unregister the trigger (we don't want it to be called and attempt + // to access the destroyed object) + if (data_pool) + data_pool->del_trigger(static_trigger_cb, this); + + // We don't have to wait for decoding to finish here. It's already + // finished (we know it because there is a "life saver" in the + // thread function) -- but we need to delete it + delete decode_thread; decode_thread=0; +} + +void +DjVuFile::reset(void) +{ + flags.enter(); + info = 0; + anno = 0; + text = 0; + meta = 0; + bg44 = 0; + fgbc = 0; + fgjb = 0; + fgjd = 0; + fgpm = 0; + dir = 0; + description = ""; + mimetype = ""; + flags=(flags&(ALL_DATA_PRESENT|DECODE_STOPPED|DECODE_FAILED)); + flags.leave(); +} + +unsigned int +DjVuFile::get_memory_usage(void) const +{ + unsigned int size=sizeof(*this); + if (info) size+=info->get_memory_usage(); + if (bg44) size+=bg44->get_memory_usage(); + if (fgjb) size+=fgjb->get_memory_usage(); + if (fgpm) size+=fgpm->get_memory_usage(); + if (fgbc) size+=fgbc->size()*sizeof(int); + if (anno) size+=anno->size(); + if (meta) size+=meta->size(); + if (dir) size+=dir->get_memory_usage(); + return size; +} + +GPList<DjVuFile> +DjVuFile::get_included_files(bool only_created) +{ + check(); + if (!only_created && !are_incl_files_created()) + process_incl_chunks(); + + GCriticalSectionLock lock(&inc_files_lock); + GPList<DjVuFile> list=inc_files_list; // Get a copy when locked + return list; +} + +void +DjVuFile::wait_for_chunk(void) +// Will return after a chunk has been decoded +{ + check(); + DEBUG_MSG("DjVuFile::wait_for_chunk() called\n"); + DEBUG_MAKE_INDENT(3); + chunk_mon.enter(); + chunk_mon.wait(); + chunk_mon.leave(); +} + +bool +DjVuFile::wait_for_finish(bool self) +// if self==TRUE, will block until decoding of this file is over +// if self==FALSE, will block until decoding of a child (direct +// or indirect) is over. +// Will return FALSE if there is nothing to wait for. TRUE otherwise +{ + DEBUG_MSG("DjVuFile::wait_for_finish(): self=" << self <<"\n"); + DEBUG_MAKE_INDENT(3); + + check(); + + if (self) + { + // It's best to check for self termination using flags. The reason + // is that finish_mon is updated in a DjVuPort function, which + // will not be called if the object is being destroyed + GMonitorLock lock(&flags); + if (is_decoding()) + { + while(is_decoding()) flags.wait(); + DEBUG_MSG("got it\n"); + return 1; + } + } else + { + // By locking the monitor, we guarantee that situation doesn't change + // between the moments when we check for pending finish events + // and when we actually run wait(). If we don't lock, the last child + // may terminate in between, and we'll wait forever. + // + // Locking is required by GMonitor interface too, btw. + GMonitorLock lock(&finish_mon); + GP<DjVuFile> file; + { + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + { + GP<DjVuFile> & f=inc_files_list[pos]; + if (f->is_decoding()) + { + file=f; break; + } + } + } + if (file) + { + finish_mon.wait(); + DEBUG_MSG("got it\n"); + return 1; + } + } + DEBUG_MSG("nothing to wait for\n"); + return 0; +} + +void +DjVuFile::notify_chunk_done(const DjVuPort *, const GUTF8String &) +{ + check(); + chunk_mon.enter(); + chunk_mon.broadcast(); + chunk_mon.leave(); +} + +void +DjVuFile::notify_file_flags_changed(const DjVuFile * src, + long set_mask, long clr_mask) +{ + check(); + if (set_mask & (DECODE_OK | DECODE_FAILED | DECODE_STOPPED)) + { + // Signal threads waiting for file termination + finish_mon.enter(); + finish_mon.broadcast(); + finish_mon.leave(); + + // In case a thread is still waiting for a chunk + chunk_mon.enter(); + chunk_mon.broadcast(); + chunk_mon.leave(); + } + + if ((set_mask & ALL_DATA_PRESENT) && src!=this && + are_incl_files_created() && is_data_present()) + { + if (src!=this && are_incl_files_created() && is_data_present()) + { + // Check if all children have data + bool all=true; + { + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + if (!inc_files_list[pos]->is_all_data_present()) + { + all=false; + break; + } + } + if (all) + { + DEBUG_MSG("Just got ALL data for '" << url << "'\n"); + flags|=ALL_DATA_PRESENT; + get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0); + } + } + } +} + +void +DjVuFile::static_decode_func(void * cl_data) +{ + DjVuFile * th=(DjVuFile *) cl_data; + + /* Please do not undo this life saver. If you do then try to resolve the + following conflict first: + 1. Decoding starts and there is only one external reference + to the DjVuFile. + 2. Decoding proceeds and calls DjVuPortcaster::notify_error(), + which creates inside a temporary GP<DjVuFile>. + 3. While notify_error() is running, the only external reference + is lost, but the DjVuFile is still alive (remember the + temporary GP<>?) + 4. The notify_error() returns, the temporary GP<> gets destroyed + and the DjVuFile is attempting to destroy right in the middle + of the decoding thread. This is either a dead block (waiting + for the termination of the decoding from the ~DjVuFile() called + from the decoding thread) or coredump. */ + GP<DjVuFile> life_saver=th; + th->decode_life_saver=0; + G_TRY { + th->decode_func(); + } G_CATCH_ALL { + } G_ENDCATCH; +} + +void +DjVuFile::decode_func(void) +{ + check(); + DEBUG_MSG("DjVuFile::decode_func() called, url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=get_portcaster(); + + G_TRY { + const GP<ByteStream> decode_stream(decode_data_pool->get_stream()); + ProgressByteStream *pstr=new ProgressByteStream(decode_stream); + const GP<ByteStream> gpstr(pstr); + pstr->set_progress_cb(progress_cb, this); + + decode(gpstr); + + // Wait for all child files to finish + while(wait_for_finish(0)) + continue; + + DEBUG_MSG("waiting for children termination\n"); + // Check for termination status + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + { + GP<DjVuFile> & f=inc_files_list[pos]; + if (f->is_decode_failed()) + G_THROW( ERR_MSG("DjVuFile.decode_fail") ); + if (f->is_decode_stopped()) + G_THROW( DataPool::Stop ); + if (!f->is_decode_ok()) + { + DEBUG_MSG("this_url='" << url << "'\n"); + DEBUG_MSG("incl_url='" << f->get_url() << "'\n"); + DEBUG_MSG("decoding=" << f->is_decoding() << "\n"); + DEBUG_MSG("status='" << f->get_flags() << "\n"); + G_THROW( ERR_MSG("DjVuFile.not_finished") ); + } + } + } G_CATCH(exc) { + G_TRY { + if (!exc.cmp_cause(DataPool::Stop)) + { + flags.enter(); + flags=flags & ~DECODING | DECODE_STOPPED; + flags.leave(); + pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.stopped")) + + GUTF8String("\t") + GUTF8String(url)); + pcaster->notify_file_flags_changed(this, DECODE_STOPPED, DECODING); + } else + { + flags.enter(); + flags=flags & ~DECODING | DECODE_FAILED; + flags.leave(); + pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.failed")) + + GUTF8String("\t") + GUTF8String(url)); + pcaster->notify_error(this, exc.get_cause()); + pcaster->notify_file_flags_changed(this, DECODE_FAILED, DECODING); + } + } G_CATCH_ALL + { + DEBUG_MSG("******* Oops. Almost missed an exception\n"); + } G_ENDCATCH; + } G_ENDCATCH; + + decode_data_pool->clear_stream(); + G_TRY { + if (flags.test_and_modify(DECODING, 0, DECODE_OK | INCL_FILES_CREATED, DECODING)) + pcaster->notify_file_flags_changed(this, DECODE_OK | INCL_FILES_CREATED, + DECODING); + } G_CATCH_ALL {} G_ENDCATCH; + DEBUG_MSG("decoding thread for url='" << url << "' ended\n"); +} + +GP<DjVuFile> +DjVuFile::process_incl_chunk(ByteStream & str, int file_num) +{ + check(); + DEBUG_MSG("DjVuFile::process_incl_chunk(): processing INCL chunk...\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=get_portcaster(); + + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=str.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + { + incl_str.setat(incl_str.length()-1, 0); + } + + if (incl_str.length()>0) + { + if (strchr(incl_str, '/')) + G_THROW( ERR_MSG("DjVuFile.malformed") ); + + DEBUG_MSG("incl_str='" << incl_str << "'\n"); + + GURL incl_url=pcaster->id_to_url(this, incl_str); + if (incl_url.is_empty()) // Fallback. Should never be used. + incl_url=GURL::UTF8(incl_str,url.base()); + + // Now see if there is already a file with this *name* created + { + GCriticalSectionLock lock(&inc_files_lock); + GPosition pos; + for(pos=inc_files_list;pos;++pos) + { + if (inc_files_list[pos]->url.fname()==incl_url.fname()) + break; + } + if (pos) + return inc_files_list[pos]; + } + + // No. We have to request a new file + GP<DjVuFile> file; + G_TRY + { + file=pcaster->id_to_file(this, incl_str); + } + G_CATCH(ex) + { + unlink_file(incl_str); + // In order to keep compatibility with the previous + // release of the DjVu plugin, we will not interrupt + // decoding here. We will just report the error. + // NOTE, that it's now the responsibility of the + // decoder to resolve all chunk dependencies, and + // abort decoding if necessary. + + // G_EXTHROW(ex); /* commented out */ + + get_portcaster()->notify_error(this,ex.get_cause()); + return 0; + } + G_ENDCATCH; + if (!file) + { + G_THROW( ERR_MSG("DjVuFile.no_create") "\t"+incl_str); + } + if (recover_errors!=ABORT) + file->set_recover_errors(recover_errors); + if (verbose_eof) + file->set_verbose_eof(verbose_eof); + pcaster->add_route(file, this); + + // We may have been stopped. Make sure the child will be stopped too. + if (flags & STOPPED) + file->stop(false); + if (flags & BLOCKED_STOPPED) + file->stop(true); + + // Lock the list again and check if the file has already been + // added by someone else + { + GCriticalSectionLock lock(&inc_files_lock); + GPosition pos; + for(pos=inc_files_list;pos;++pos) + { + if (inc_files_list[pos]->url.fname()==incl_url.fname()) + break; + } + if (pos) + { + file=inc_files_list[pos]; + } else if (file_num<0 || !(pos=inc_files_list.nth(file_num))) + { + inc_files_list.append(file); + } else + { + inc_files_list.insert_before(pos, file); + } + } + return file; + } + return 0; +} + + +void +DjVuFile::report_error(const GException &ex,bool throw_errors) +{ + data_pool->clear_stream(); + if((!verbose_eof)|| (ex.cmp_cause(ByteStream::EndOfFile))) + { + if(throw_errors) + { + G_EXTHROW(ex); + }else + { + get_portcaster()->notify_error(this,ex.get_cause()); + } + }else + { + GURL url=get_url(); + GUTF8String url_str=url.get_string(); +// if (url.is_local_file_url()) +// url_str=url.filename(); + + GUTF8String msg = GUTF8String( ERR_MSG("DjVuFile.EOF") "\t") + url; + if(throw_errors) + { + G_EXTHROW(ex, msg); + }else + { + get_portcaster()->notify_error(this,msg); + } + } +} + +void +DjVuFile::process_incl_chunks(void) +// This function may block for data +// NOTE: It may be called again when INCL_FILES_CREATED is set. +// It happens in insert_file() when it has modified the data +// and wants to create the actual file +{ + DEBUG_MSG("DjVuFile::process_incl_chunks(void)\n"); + DEBUG_MAKE_INDENT(3); + check(); + + int incl_cnt=0; + + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (iff.get_chunk(chkid)) + { + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (chkid=="INCL") + { + G_TRY + { + process_incl_chunk(*iff.get_bytestream(), incl_cnt++); + } + G_CATCH(ex); + { + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + }else if(chkid=="FAKE") + { + set_needs_compression(true); + set_can_compress(true); + }else if(chkid=="BGjp") + { + set_can_compress(true); + }else if(chkid=="Smmr") + { + set_can_compress(true); + } + iff.seek_close_chunk(); + } + if (chunks_number < 0) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + } + flags|=INCL_FILES_CREATED; + data_pool->clear_stream(); +} + +GP<JB2Dict> +DjVuFile::static_get_fgjd(void *arg) +{ + DjVuFile *file = (DjVuFile*)arg; + return file->get_fgjd(1); +} + +GP<JB2Dict> +DjVuFile::get_fgjd(int block) +{ + check(); + + // Simplest case + if (fgjd) + return fgjd; + // Check wether included files + chunk_mon.enter(); + G_TRY { + for(;;) + { + int active = 0; + GPList<DjVuFile> incs = get_included_files(); + for (GPosition pos=incs.firstpos(); pos; ++pos) + { + GP<DjVuFile> file = incs[pos]; + if (file->is_decoding()) + active = 1; + GP<JB2Dict> fgjd = file->get_fgjd(); + if (fgjd) + { + chunk_mon.leave(); + return fgjd; + } + } + // Exit if non-blocking mode + if (! block) + break; + // Exit if there is no decoding activity + if (! active) + break; + // Wait until a new chunk gets decoded + wait_for_chunk(); + } + } G_CATCH_ALL { + chunk_mon.leave(); + G_RETHROW; + } G_ENDCATCH; + chunk_mon.leave(); + if (is_decode_stopped()) G_THROW( DataPool::Stop ); + return 0; +} + +int +DjVuFile::get_dpi(int w, int h) +{ + int dpi=0, red=1; + if (info) + { + for(red=1; red<=12; red++) + if ((info->width+red-1)/red==w) + if ((info->height+red-1)/red==h) + break; + if (red>12) + G_THROW( ERR_MSG("DjVuFile.corrupt_BG44") ); + dpi=info->dpi; + } + return (dpi ? dpi : 300)/red; +} + +static inline bool +is_info(const GUTF8String &chkid) +{ + return (chkid=="INFO"); +} + +static inline bool +is_annotation(const GUTF8String &chkid) +{ + return (chkid=="ANTa" || + chkid=="ANTz" || + chkid=="FORM:ANNO" ); +} + +static inline bool +is_text(const GUTF8String &chkid) +{ + return (chkid=="TXTa" || chkid=="TXTz"); +} + +static inline bool +is_meta(const GUTF8String &chkid) +{ + return (chkid=="METa" || chkid=="METz"); +} + + +GUTF8String +DjVuFile::decode_chunk( const GUTF8String &id, const GP<ByteStream> &gbs, + bool djvi, bool djvu, bool iw44) +{ + DEBUG_MSG("DjVuFile::decode_chunk()\n"); + ByteStream &bs=*gbs; + check(); + + // If this object is referenced by only one GP<> pointer, this + // pointer should be the "life_saver" created by the decoding thread. + // If it is the only GP<> pointer, then nobody is interested in the + // results of the decoding and we can abort now with #DataPool::Stop# + if (get_count()==1) + G_THROW( DataPool::Stop ); + + GUTF8String desc = ERR_MSG("DjVuFile.unrecog_chunk"); + GUTF8String chkid = id; + DEBUG_MSG("DjVuFile::decode_chunk() : decoding " << id << "\n"); + + // INFO (information chunk for djvu page) + if (is_info(chkid) && (djvu || djvi)) + { + if (info) + G_THROW( ERR_MSG("DjVuFile.corrupt_dupl") ); + if (djvi) + G_THROW( ERR_MSG("DjVuFile.corrupt_INFO") ); + // DjVuInfo::decode no longer throws version exceptions + GP<DjVuInfo> xinfo=DjVuInfo::create(); + xinfo->decode(bs); + info = xinfo; + desc.format( ERR_MSG("DjVuFile.page_info") ); + // Consistency checks (previously in DjVuInfo::decode) + if (info->width<0 || info->height<0) + G_THROW( ERR_MSG("DjVuFile.corrupt_zero") ); + if (info->version >= DJVUVERSION_TOO_NEW) + G_THROW( ERR_MSG("DjVuFile.new_version") "\t" STRINGIFY(DJVUVERSION_TOO_NEW) ); + if(info->compressable) + set_can_compress(true); + } + + // INCL (inclusion chunk) + else if (chkid == "INCL" && (djvi || djvu || iw44)) + { + GP<DjVuFile> file=process_incl_chunk(bs); + if (file) + { + int decode_was_already_started = 1; + { + GMonitorLock lock(&file->flags); + // Start decoding + if(file->resume_decode()) + { + decode_was_already_started = 0; + } + } + // Send file notifications if previously started + if (decode_was_already_started) + { + // May send duplicate notifications... + if (file->is_decode_ok()) + get_portcaster()->notify_file_flags_changed(file, DECODE_OK, 0); + else if (file->is_decode_failed()) + get_portcaster()->notify_file_flags_changed(file, DECODE_FAILED, 0); + } + desc.format( ERR_MSG("DjVuFile.indir_chunk1") "\t" + file->get_url().fname() ); + } else + desc.format( ERR_MSG("DjVuFile.indir_chunk2") ); + } + + // Djbz (JB2 Dictionary) + else if (chkid == "Djbz" && (djvu || djvi)) + { + if (this->fgjd) + G_THROW( ERR_MSG("DjVuFile.dupl_Dxxx") ); + if (this->fgjd) + G_THROW( ERR_MSG("DjVuFile.Dxxx_after_Sxxx") ); + GP<JB2Dict> fgjd = JB2Dict::create(); + fgjd->decode(gbs); + this->fgjd = fgjd; + desc.format( ERR_MSG("DjVuFile.shape_dict") "\t%d", fgjd->get_shape_count() ); + } + + // Sjbz (JB2 encoded mask) + else if (chkid=="Sjbz" && (djvu || djvi)) + { + if (this->fgjb) + G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") ); + GP<JB2Image> fgjb=JB2Image::create(); + // ---- begin hack + if (info && info->version <=18) + fgjb->reproduce_old_bug = true; + // ---- end hack + fgjb->decode(gbs, static_get_fgjd, (void*)this); + this->fgjb = fgjb; + desc.format( ERR_MSG("DjVuFile.fg_mask") "\t%d\t%d\t%d", + fgjb->get_width(), fgjb->get_height(), + get_dpi(fgjb->get_width(), fgjb->get_height())); + } + + // Smmr (MMR-G4 encoded mask) + else if (chkid=="Smmr" && (djvu || djvi)) + { + if (this->fgjb) + G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") ); + set_can_compress(true); + this->fgjb = MMRDecoder::decode(gbs); + desc.format( ERR_MSG("DjVuFile.G4_mask") "\t%d\t%d\t%d", + fgjb->get_width(), fgjb->get_height(), + get_dpi(fgjb->get_width(), fgjb->get_height())); + } + + // BG44 (background wavelets) + else if (chkid == "BG44" && (djvu || djvi)) + { + if (!bg44) + { + if (bgpm) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + // First chunk + GP<IW44Image> bg44=IW44Image::create_decode(IW44Image::COLOR); + bg44->decode_chunk(gbs); + this->bg44 = bg44; + desc.format( ERR_MSG("DjVuFile.IW44_bg1") "\t%d\t%d\t%d", + bg44->get_width(), bg44->get_height(), + get_dpi(bg44->get_width(), bg44->get_height())); + } + else + { + // Refinement chunks + GP<IW44Image> bg44 = this->bg44; + bg44->decode_chunk(gbs); + desc.format( ERR_MSG("DjVuFile.IW44_bg2") "\t%d\t%d", + bg44->get_serial(), get_dpi(bg44->get_width(), bg44->get_height())); + } + } + + // FG44 (foreground wavelets) + else if (chkid == "FG44" && (djvu || djvu)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); + GP<IW44Image> gfg44=IW44Image::create_decode(IW44Image::COLOR); + IW44Image &fg44=*gfg44; + fg44.decode_chunk(gbs); + fgpm=fg44.get_pixmap(); + desc.format( ERR_MSG("DjVuFile.IW44_fg") "\t%d\t%d\t%d", + fg44.get_width(), fg44.get_height(), + get_dpi(fg44.get_width(), fg44.get_height())); + } + + // LINK (background LINK) + else if (chkid == "LINK" && (djvu || djvi)) + { + if (bg44 || bgpm) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + if(djvu_decode_codec) + { + set_modified(true); + set_can_compress(true); + set_needs_compression(true); + this->bgpm = djvu_decode_codec(bs); + desc.format( ERR_MSG("DjVuFile.color_import1") "\t%d\t%d\t%d", + bgpm->columns(), bgpm->rows(), + get_dpi(bgpm->columns(), bgpm->rows())); + }else + { + desc.format( ERR_MSG("DjVuFile.color_import2") ); + } + } + + // BGjp (background JPEG) + else if (chkid == "BGjp" && (djvu || djvi)) + { + if (bg44 || bgpm) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + set_can_compress(true); +#ifdef NEED_JPEG_DECODER + this->bgpm = JPEGDecoder::decode(bs); + desc.format( ERR_MSG("DjVuFile.JPEG_bg1") "\t%d\t%d\t%d", + bgpm->columns(), bgpm->rows(), + get_dpi(bgpm->columns(), bgpm->rows())); +#else + desc.format( ERR_MSG("DjVuFile.JPEG_bg2") ); +#endif + } + + // FGjp (foreground JPEG) + else if (chkid == "FGjp" && (djvu || djvi)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); +#ifdef NEED_JPEG_DECODER + this->fgpm = JPEGDecoder::decode(bs); + desc.format( ERR_MSG("DjVuFile.JPEG_fg1") "\t%d\t%d\t%d", + fgpm->columns(), fgpm->rows(), + get_dpi(fgpm->columns(), fgpm->rows())); +#else + desc.format( ERR_MSG("DjVuFile.JPEG_fg2") ); +#endif + } + + // BG2k (background JPEG-2000) Note: JPEG2K bitstream not finalized. + else if (chkid == "BG2k" && (djvu || djvi)) + { + if (bg44) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + desc.format( ERR_MSG("DjVuFile.JPEG2K_bg") ); + } + + // FG2k (foreground JPEG-2000) Note: JPEG2K bitstream not finalized. + else if (chkid == "FG2k" && (djvu || djvi)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); + desc.format( ERR_MSG("DjVuFile.JPEG2K_fg") ); + } + + // FGbz (foreground color vector) + else if (chkid == "FGbz" && (djvu || djvi)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); + GP<DjVuPalette> fgbc = DjVuPalette::create(); + fgbc->decode(gbs); + this->fgbc = fgbc; + desc.format( ERR_MSG("DjVuFile.JB2_fg") "\t%d\t%d", + fgbc->size(), fgbc->colordata.size()); + } + + // BM44/PM44 (IW44 data) + else if ((chkid == "PM44" || chkid=="BM44") && iw44) + { + if (!bg44) + { + // First chunk + GP<IW44Image> bg44 = IW44Image::create_decode(IW44Image::COLOR); + bg44->decode_chunk(gbs); + GP<DjVuInfo> info = DjVuInfo::create(); + info->width = bg44->get_width(); + info->height = bg44->get_height(); + info->dpi = 100; + this->bg44 = bg44; + this->info = info; + desc.format( ERR_MSG("DjVuFile.IW44_data1") "\t%d\t%d\t%d", + bg44->get_width(), bg44->get_height(), + get_dpi(bg44->get_width(), bg44->get_height())); + } + else + { + // Refinement chunks + GP<IW44Image> bg44 = this->bg44; + bg44->decode_chunk(gbs); + desc.format( ERR_MSG("DjVuFile.IW44_data2") "\t%d\t%d", + bg44->get_serial(), + get_dpi(bg44->get_width(), bg44->get_height())); + } + } + + // NDIR (obsolete navigation chunk) + else if (chkid == "NDIR") + { + GP<DjVuNavDir> dir=DjVuNavDir::create(url); + dir->decode(bs); + this->dir=dir; + desc.format( ERR_MSG("DjVuFile.nav_dir") ); + } + + // FORM:ANNO (obsolete) (must be before other annotations) + else if (chkid == "FORM:ANNO") + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&anno_lock); + if (! anno) + { + anno=ByteStream::create(); + } + anno->seek(0,SEEK_END); + if (anno->tell()) + { + anno->write((void*)"", 1); + } + // Copy data + anno->copy(achunk); + desc.format( ERR_MSG("DjVuFile.anno1") ); + } + + // ANTa/ANTx/TXTa/TXTz annotations + else if (is_annotation(chkid)) // but not FORM:ANNO + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&anno_lock); + if (! anno) + { + anno = ByteStream::create(); + } + anno->seek(0,SEEK_END); + if (anno->tell() & 1) + { + anno->write((const void*)"", 1); + } + // Recreate chunk header + const GP<IFFByteStream> giffout(IFFByteStream::create(anno)); + IFFByteStream &iffout=*giffout; + iffout.put_chunk(id); + iffout.copy(achunk); + iffout.close_chunk(); + desc.format( ERR_MSG("DjVuFile.anno2") ); + } + else if (is_text(chkid)) + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&text_lock); + if (! text) + { + text = ByteStream::create(); + } + text->seek(0,SEEK_END); + if (text->tell()) + { + text->write((const void*)"", 1); + } + // Recreate chunk header + const GP<IFFByteStream> giffout(IFFByteStream::create(text)); + IFFByteStream &iffout=*giffout; + iffout.put_chunk(id); + iffout.copy(achunk); + iffout.close_chunk(); + desc.format( ERR_MSG("DjVuFile.text") ); + } + else if (is_meta(chkid)) + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&text_lock); + if (! meta) + { + meta = ByteStream::create(); + } + meta->seek(0,SEEK_END); + if (meta->tell()) + { + meta->write((const void*)"", 1); + } + // Recreate chunk header + const GP<IFFByteStream> giffout(IFFByteStream::create(meta)); + IFFByteStream &iffout=*giffout; + iffout.put_chunk(id); + iffout.copy(achunk); + iffout.close_chunk(); +// desc.format( ERR_MSG("DjVuFile.text") ); + } + + // Return description + return desc; +} + +void +DjVuFile::set_decode_codec(GP<GPixmap> (*codec)(ByteStream &bs)) +{ + djvu_decode_codec=codec; +} + +void +DjVuFile::decode(const GP<ByteStream> &gbs) +{ + check(); + DEBUG_MSG("DjVuFile::decode(), url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + DjVuPortcaster * pcaster=get_portcaster(); + + // Get form chunk + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(gbs)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + // Check file format + bool djvi = (chkid=="FORM:DJVI")?true:false; + bool djvu = (chkid=="FORM:DJVU")?true:false; + bool iw44 = ((chkid=="FORM:PM44") || (chkid=="FORM:BM44")); + if (djvi || djvu) + mimetype = "image/x.djvu"; + else if (iw44) + mimetype = "image/x-iw44"; + else + G_THROW( ERR_MSG("DjVuFile.unexp_image") ); + + // Process chunks + int size_so_far=iff.tell(); + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + + // Decode and get chunk description + GUTF8String str = decode_chunk(chkid, iff.get_bytestream(), djvi, djvu, iw44); + // Add parameters to the chunk description to give the size and chunk id + GUTF8String desc; + desc.format("\t%5.1f\t%s", chksize/1024.0, (const char*)chkid); + // Append the whole thing to the growing file description + description = description + str + desc + "\n"; + + pcaster->notify_chunk_done(this, chkid); + // Close chunk + iff.seek_close_chunk(); + // Record file size + size_so_far=iff.tell(); + } + if (chunks_number < 0) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if(!ex.cmp_cause(ByteStream::EndOfFile)) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + }else + { + report_error(ex,true); + } + } + G_ENDCATCH; + + // Record file size + file_size=size_so_far; + // Close form chunk + iff.close_chunk(); + // Close BG44 codec + if (bg44) + bg44->close_codec(); + + // Complete description + if (djvu && !info) + G_THROW( ERR_MSG("DjVuFile.corrupt_missing_info") ); + if (iw44 && !info) + G_THROW( ERR_MSG("DjVuFile.corrupt_missing_IW44") ); + if (info) + { + GUTF8String desc; + if (djvu || djvi) + desc.format( ERR_MSG("DjVuFile.djvu_header") "\t%d\t%d\t%d\t%d", + info->width, info->height, + info->dpi, info->version); + else if (iw44) + desc.format( ERR_MSG("DjVuFile.IW44_header") "\t%d\t%d\t%d", + info->width, info->height, info->dpi); + description=desc + "\n" + description; + int rawsize=info->width*info->height*3; + desc.format( ERR_MSG("DjVuFile.ratio") "\t%0.1f\t%0.1f", + (double)rawsize/file_size, file_size/1024.0 ); + description=description+desc; + } +} + +void +DjVuFile::start_decode(void) +{ + check(); + DEBUG_MSG("DjVuFile::start_decode(), url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GThread * thread_to_delete=0; + flags.enter(); + G_TRY { + if (!(flags & DONT_START_DECODE) && !is_decoding()) + { + if (flags & DECODE_STOPPED) reset(); + flags&=~(DECODE_OK | DECODE_STOPPED | DECODE_FAILED); + flags|=DECODING; + + // Don't delete the thread while you're owning the flags lock + // Beware of deadlock! + thread_to_delete=decode_thread; decode_thread=0; + + // We want to create it right here to be able to stop the + // decoding thread even before its function is called (it starts) + decode_data_pool=DataPool::create(data_pool); + decode_life_saver=this; + + decode_thread=new GThread(); + decode_thread->create(static_decode_func, this); + } + } + G_CATCH_ALL + { + flags&=~DECODING; + flags|=DECODE_FAILED; + flags.leave(); + get_portcaster()->notify_file_flags_changed(this, DECODE_FAILED, DECODING); + delete thread_to_delete; + G_RETHROW; + } + G_ENDCATCH; + flags.leave(); + delete thread_to_delete; +} + +bool +DjVuFile::resume_decode(const bool sync) +{ + bool retval=false; + { + GMonitorLock lock(&flags); + if( !is_decoding() && !is_decode_ok() && !is_decode_failed() ) + { + start_decode(); + retval=true; + } + } + if(sync) + { + wait_for_finish(); + } + return retval; +} + +void +DjVuFile::stop_decode(bool sync) +{ + check(); + + DEBUG_MSG("DjVuFile::stop_decode(), url='" << url << + "', sync=" << (int) sync << "\n"); + DEBUG_MAKE_INDENT(3); + + G_TRY + { + flags|=DONT_START_DECODE; + + // Don't stop SYNCHRONOUSLY from the thread where the decoding is going!!! + { + // First - ask every included child to stop in async mode + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + inc_files_list[pos]->stop_decode(0); + +// if (decode_data_pool) decode_data_pool->stop(); + } + + if (sync) + { + while(1) + { + GP<DjVuFile> file; + { + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + { + GP<DjVuFile> & f=inc_files_list[pos]; + if (f->is_decoding()) + { + file=f; break; + } + } + } + if (!file) break; + + file->stop_decode(1); + } + + wait_for_finish(1); // Wait for self termination + + // Don't delete the thread here. Until GPBase::preserve() is + // reimplemented somehow at the GThread level. + // delete decode_thread; decode_thread=0; + } + flags&=~(DONT_START_DECODE); + } G_CATCH_ALL { + flags&=~(DONT_START_DECODE); + G_RETHROW; + } G_ENDCATCH; +} + +void +DjVuFile::stop(bool only_blocked) +// This is a one-way function. There is no way to undo the stop() +// command. +{ + DEBUG_MSG("DjVuFile::stop(): Stopping everything\n"); + DEBUG_MAKE_INDENT(3); + + flags|=only_blocked ? BLOCKED_STOPPED : STOPPED; + if (data_pool) data_pool->stop(only_blocked); + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + inc_files_list[pos]->stop(only_blocked); +} + +GP<DjVuNavDir> +DjVuFile::find_ndir(GMap<GURL, void *> & map) +{ + check(); + + DEBUG_MSG("DjVuFile::find_ndir(): looking for NDIR in '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (dir) return dir; + + if (!map.contains(url)) + { + map[url]=0; + + GPList<DjVuFile> list=get_included_files(false); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuNavDir> d=list[pos]->find_ndir(map); + if (d) return d; + } + } + return 0; +} + +GP<DjVuNavDir> +DjVuFile::find_ndir(void) +{ + GMap<GURL, void *> map; + return find_ndir(map); +} + +GP<DjVuNavDir> +DjVuFile::decode_ndir(GMap<GURL, void *> & map) +{ + check(); + + DEBUG_MSG("DjVuFile::decode_ndir(): decoding for NDIR in '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (dir) return dir; + + if (!map.contains(url)) + { + map[url]=0; + + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (chkid=="NDIR") + { + GP<DjVuNavDir> d=DjVuNavDir::create(url); + d->decode(*iff.get_bytestream()); + dir=d; + break; + } + iff.seek_close_chunk(); + } + if ((!dir)&&(chunks_number < 0)) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if(!ex.cmp_cause(ByteStream::EndOfFile)) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors<=SKIP_PAGES)); + }else + { + report_error(ex,true); + } + } + G_ENDCATCH; + + data_pool->clear_stream(); + if (dir) return dir; + + GPList<DjVuFile> list=get_included_files(false); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuNavDir> d=list[pos]->decode_ndir(map); + if (d) return d; + } + data_pool->clear_stream(); + } + return 0; +} + +GP<DjVuNavDir> +DjVuFile::decode_ndir(void) +{ + GMap<GURL, void *> map; + return decode_ndir(map); +} + +void +DjVuFile::get_merged_anno(const GP<DjVuFile> & file, + const GP<ByteStream> &gstr_out, const GList<GURL> & ignore_list, + int level, int & max_level, GMap<GURL, void *> & map) +{ + DEBUG_MSG("DjVuFile::get_merged_anno()\n"); + GURL url=file->get_url(); + if (!map.contains(url)) + { + ByteStream &str_out=*gstr_out; + map[url]=0; + + // Do the included files first (To make sure that they have + // less precedence) + // Depending on if we have all data present, we will + // either create all included files or will use only + // those that have already been created + GPList<DjVuFile> list=file->get_included_files(!file->is_data_present()); + for(GPosition pos=list;pos;++pos) + get_merged_anno(list[pos], gstr_out, ignore_list, level+1, max_level, map); + + // Now process the DjVuFile's own annotations + if (!ignore_list.contains(file->get_url())) + { + if (!file->is_data_present() || + file->is_modified() && file->anno) + { + // Process the decoded (?) anno + GCriticalSectionLock lock(&file->anno_lock); + if (file->anno && file->anno->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->anno->seek(0); + str_out.copy(*file->anno); + } + } else if (file->is_data_present()) + { + // Copy all annotations chunks, but do NOT modify + // this->anno (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str(file->data_pool->get_stream()); + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + while(iff.get_chunk(chkid)) + { + if (chkid=="FORM:ANNO") + { + if (max_level<level) + max_level=level; + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + str_out.copy(*iff.get_bytestream()); + } + else if (is_annotation(chkid)) // but not FORM:ANNO + { + if (max_level<level) + max_level=level; + if (str_out.tell()&&chkid != "ANTz") + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + file->data_pool->clear_stream(); + } + } + } +} + +GP<ByteStream> +DjVuFile::get_merged_anno(const GList<GURL> & ignore_list, + int * max_level_ptr) + // Will do the same thing as get_merged_anno(int *), but will + // ignore DjVuFiles with URLs from the ignore_list +{ + DEBUG_MSG("DjVuFile::get_merged_anno()\n"); + GP<ByteStream> gstr(ByteStream::create()); + GMap<GURL, void *> map; + int max_level=0; + get_merged_anno(this, gstr, ignore_list, 0, max_level, map); + if (max_level_ptr) + *max_level_ptr=max_level; + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +GP<ByteStream> +DjVuFile::get_merged_anno(int * max_level_ptr) +// Will go down the DjVuFile's hierarchy and decode all DjVuAnno even +// when the DjVuFile is not fully decoded yet. To avoid correlations +// with DjVuFile::decode(), we do not modify DjVuFile::anno data. +// +// Files deeper in the hierarchy have less influence on the +// results. It means, for example, that the if annotations are +// specified in the top level page file and in a shared file, +// the top level page file settings will take precedence. +// +// NOTE! This function guarantees correct results only if the +// DjVuFile has all data +{ + GList<GURL> ignore_list; + return get_merged_anno(ignore_list, max_level_ptr); +} + + +// [LB->BCR] The following six functions get_anno, get_text, get_meta +// contain the same code in triplicate!!! + +void +DjVuFile::get_anno( + const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out) +{ + DEBUG_MSG("DjVuFile::get_anno()\n"); + ByteStream &str_out=*gstr_out; + if (!file->is_data_present() || + file->is_modified() && file->anno) + { + // Process the decoded (?) anno + GCriticalSectionLock lock(&file->anno_lock); + if (file->anno && file->anno->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->anno->seek(0); + str_out.copy(*file->anno); + } + } else if (file->is_data_present()) + { + // Copy all anno chunks, but do NOT modify + // DjVuFile::anno (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str=file->data_pool->get_stream(); + const GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + { + while(iff.get_chunk(chkid)) + { + if (is_annotation(chkid)) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + } + file->data_pool->clear_stream(); + } +} + +void +DjVuFile::get_text( + const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out) +{ + DEBUG_MSG("DjVuFile::get_text()\n"); + ByteStream &str_out=*gstr_out; + if (!file->is_data_present() || + file->is_modified() && file->text) + { + // Process the decoded (?) text + GCriticalSectionLock lock(&file->text_lock); + if (file->text && file->text->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->text->seek(0); + str_out.copy(*file->text); + } + } else if (file->is_data_present()) + { + // Copy all text chunks, but do NOT modify + // DjVuFile::text (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str=file->data_pool->get_stream(); + const GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + { + while(iff.get_chunk(chkid)) + { + if (is_text(chkid)) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + } + file->data_pool->clear_stream(); + } +} + +void +DjVuFile::get_meta( + const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out) +{ + DEBUG_MSG("DjVuFile::get_meta()\n"); + ByteStream &str_out=*gstr_out; + if (!file->is_data_present() || + file->is_modified() && file->meta) + { + // Process the decoded (?) meta + GCriticalSectionLock lock(&file->meta_lock); + if (file->meta && file->meta->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->meta->seek(0); + str_out.copy(*file->meta); + } + } else if (file->is_data_present()) + { + // Copy all meta chunks, but do NOT modify + // DjVuFile::meta (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str=file->data_pool->get_stream(); + const GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + { + while(iff.get_chunk(chkid)) + { + if (is_meta(chkid)) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + } + file->data_pool->clear_stream(); + } +} + +GP<ByteStream> +DjVuFile::get_anno(void) +{ + DEBUG_MSG("DjVuFile::get_text(void)\n"); + GP<ByteStream> gstr(ByteStream::create()); + get_anno(this, gstr); + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +GP<ByteStream> +DjVuFile::get_text(void) +{ + DEBUG_MSG("DjVuFile::get_text(void)\n"); + GP<ByteStream> gstr(ByteStream::create()); + get_text(this, gstr); + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +GP<ByteStream> +DjVuFile::get_meta(void) +{ + DEBUG_MSG("DjVuFile::get_meta(void)\n"); + GP<ByteStream> gstr(ByteStream::create()); + get_meta(this, gstr); + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +void +DjVuFile::static_trigger_cb(void * cl_data) +{ + DjVuFile * th=(DjVuFile *) cl_data; + G_TRY { + GP<DjVuPort> port=DjVuPort::get_portcaster()->is_port_alive(th); + if (port && port->inherits("DjVuFile")) + ((DjVuFile *) (DjVuPort *) port)->trigger_cb(); + } G_CATCH(exc) { + G_TRY { + get_portcaster()->notify_error(th, exc.get_cause()); + } G_CATCH_ALL {} G_ENDCATCH; + } G_ENDCATCH; +} + +void +DjVuFile::trigger_cb(void) +{ + GP<DjVuFile> life_saver=this; + + DEBUG_MSG("DjVuFile::trigger_cb(): got data for '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + file_size=data_pool->get_length(); + flags|=DATA_PRESENT; + get_portcaster()->notify_file_flags_changed(this, DATA_PRESENT, 0); + + if (!are_incl_files_created()) + process_incl_chunks(); + + bool all=true; + inc_files_lock.lock(); + GPList<DjVuFile> files_list=inc_files_list; + inc_files_lock.unlock(); + for(GPosition pos=files_list;pos&&(all=files_list[pos]->is_all_data_present());++pos) + EMPTY_LOOP; + if (all) + { + DEBUG_MSG("DjVuFile::trigger_cb(): We have ALL data for '" << url << "'\n"); + flags|=ALL_DATA_PRESENT; + get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0); + } +} + +void +DjVuFile::progress_cb(int pos, void * cl_data) +{ + DEBUG_MSG("DjVuFile::progress_cb() called\n"); + DEBUG_MAKE_INDENT(3); + + DjVuFile * th=(DjVuFile *) cl_data; + + int length=th->decode_data_pool->get_length(); + if (length>0) + { + float progress=(float) pos/length; + DEBUG_MSG("progress=" << progress << "\n"); + get_portcaster()->notify_decode_progress(th, progress); + } else + { + DEBUG_MSG("DataPool size is still unknown => ignoring\n"); + } +} + +//***************************************************************************** +//******************************** Utilities ********************************** +//***************************************************************************** + +void +DjVuFile::move(GMap<GURL, void *> & map, const GURL & dir_url) +// This function may block for data. +{ + if (!map.contains(url)) + { + map[url]=0; + + url=GURL::UTF8(url.name(),dir_url); + + + // Leave the lock here! + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + inc_files_list[pos]->move(map, dir_url); + } +} + +void +DjVuFile::move(const GURL & dir_url) +// This function may block for data. +{ + check(); + DEBUG_MSG("DjVuFile::move(): dir_url='" << dir_url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GMap<GURL, void *> map; + move(map, dir_url); +} + +void +DjVuFile::set_name(const GUTF8String &name) +{ + DEBUG_MSG("DjVuFile::set_name(): name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + url=GURL::UTF8(name,url.base()); +} + +//***************************************************************************** +//****************************** Data routines ******************************** +//***************************************************************************** + +int +DjVuFile::get_chunks_number(void) +{ + if(chunks_number < 0) + { + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chksize; + for(;(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + iff.seek_close_chunk(); + } + chunks_number=last_chunk; + } + G_CATCH(ex) + { + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors<=SKIP_PAGES)); + } + G_ENDCATCH; + data_pool->clear_stream(); + } + return chunks_number; +} + +GUTF8String +DjVuFile::get_chunk_name(int chunk_num) +{ + if(chunk_num < 0) + { + G_THROW( ERR_MSG("DjVuFile.illegal_chunk") ); + } + if((chunks_number >= 0)&&(chunk_num > chunks_number)) + { + G_THROW( ERR_MSG("DjVuFile.missing_chunk") ); + } + check(); + + GUTF8String name; + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + if (chunks++==chunk_num) { name=chkid; break; } + iff.seek_close_chunk(); + } + } + G_CATCH(ex) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + if (!name.length()) + { + if (chunks_number < 0) chunks_number=chunks; + G_THROW( ERR_MSG("DjVuFile.missing_chunk") ); + } + return name; +} + +bool +DjVuFile::contains_chunk(const GUTF8String &chunk_name) +{ + check(); + DEBUG_MSG("DjVuFile::contains_chunk(): url='" << url << "', chunk_name='" << + chunk_name << "'\n"); + DEBUG_MAKE_INDENT(3); + + bool contains=0; + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF((recover_errors<=SKIP_PAGES)) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (chkid==chunk_name) { contains=1; break; } + iff.seek_close_chunk(); + } + if (!contains &&(chunks_number < 0)) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + data_pool->clear_stream(); + return contains; +} + +bool +DjVuFile::contains_anno(void) +{ + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + while(iff.get_chunk(chkid)) + { + if (is_annotation(chkid)) + return true; + iff.close_chunk(); + } + + data_pool->clear_stream(); + return false; +} + +bool +DjVuFile::contains_text(void) +{ + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + while(iff.get_chunk(chkid)) + { + if (is_text(chkid)) + return true; + iff.close_chunk(); + } + + data_pool->clear_stream(); + return false; +} + +bool +DjVuFile::contains_meta(void) +{ + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + while(iff.get_chunk(chkid)) + { + if (is_meta(chkid)) + return true; + iff.close_chunk(); + } + + data_pool->clear_stream(); + return false; +} + +//***************************************************************************** +//****************************** Save routines ******************************** +//***************************************************************************** + +static void +copy_chunks(const GP<ByteStream> &from, IFFByteStream &ostr) +{ + from->seek(0); + const GP<IFFByteStream> giff(IFFByteStream::create(from)); + IFFByteStream &iff=*giff; + GUTF8String chkid; + int chksize; + while ((chksize=iff.get_chunk(chkid))) + { + ostr.put_chunk(chkid); + int ochksize=ostr.copy(*iff.get_bytestream()); + ostr.close_chunk(); + iff.seek_close_chunk(); + if(ochksize != chksize) + { + G_THROW( ByteStream::EndOfFile ); + } + } +} + + +void +DjVuFile::add_djvu_data(IFFByteStream & ostr, GMap<GURL, void *> & map, + const bool included_too, const bool no_ndir) +{ + check(); + if (map.contains(url)) return; + bool top_level = !map.size(); + map[url]=0; + bool processed_annotation = false; + bool processed_text = false; + bool processed_meta = false; + + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + // Open toplevel form + if (top_level) + ostr.put_chunk(chkid); + // Process chunks + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (is_info(chkid) && info) + { + ostr.put_chunk(chkid); + info->encode(*ostr.get_bytestream()); + ostr.close_chunk(); + } + else if (chkid=="INCL" && included_too) + { + GP<DjVuFile> file = process_incl_chunk(*iff.get_bytestream()); + if (file) + { + if(recover_errors!=ABORT) + file->set_recover_errors(recover_errors); + if(verbose_eof) + file->set_verbose_eof(verbose_eof); + file->add_djvu_data(ostr, map, included_too, no_ndir); + } + } + else if (is_annotation(chkid) && anno && anno->size()) + { + if (!processed_annotation) + { + processed_annotation = true; + GCriticalSectionLock lock(&anno_lock); + copy_chunks(anno, ostr); + } + } + else if (is_text(chkid) && text && text->size()) + { + if (!processed_text) + { + processed_text = true; + GCriticalSectionLock lock(&text_lock); + copy_chunks(text, ostr); + } + } + else if (is_meta(chkid) && meta && meta->size()) + { + if (!processed_meta) + { + processed_meta = true; + GCriticalSectionLock lock(&meta_lock); + copy_chunks(meta, ostr); + } + } + else if (chkid!="NDIR"||!(no_ndir || dir)) + { // Copy NDIR chunks, but never generate new ones. + ostr.put_chunk(chkid); + ostr.copy(*iff.get_bytestream()); + ostr.close_chunk(); + } + iff.seek_close_chunk(); + } + if (chunks_number < 0) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if(!ex.cmp_cause(ByteStream::EndOfFile)) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors<=SKIP_PAGES)); + }else + { + report_error(ex,true); + } + } + G_ENDCATCH; + + // Otherwise, writes annotation at the end (annotations could be big) + if (!processed_annotation && anno && anno->size()) + { + processed_annotation = true; + GCriticalSectionLock lock(&anno_lock); + copy_chunks(anno, ostr); + } + if (!processed_text && text && text->size()) + { + processed_text = true; + GCriticalSectionLock lock(&text_lock); + copy_chunks(text, ostr); + } + if (!processed_meta && meta && meta->size()) + { + processed_meta = true; + GCriticalSectionLock lock(&meta_lock); + copy_chunks(meta, ostr); + } + // Close iff + if (top_level) + ostr.close_chunk(); + + data_pool->clear_stream(); +} + +GP<ByteStream> +DjVuFile::get_djvu_bytestream(const bool included_too, const bool no_ndir) +{ + check(); + DEBUG_MSG("DjVuFile::get_djvu_bytestream(): creating DjVu raw file\n"); + DEBUG_MAKE_INDENT(3); + const GP<ByteStream> pbs(ByteStream::create()); + const GP<IFFByteStream> giff=IFFByteStream::create(pbs); + IFFByteStream &iff=*giff; + GMap<GURL, void *> map; + add_djvu_data(iff, map, included_too, no_ndir); + iff.flush(); + pbs->seek(0, SEEK_SET); + return pbs; +} + +GP<DataPool> +DjVuFile::get_djvu_data(const bool included_too, const bool no_ndir) +{ + const GP<ByteStream> pbs = get_djvu_bytestream(included_too, no_ndir); + return DataPool::create(pbs); +} + +void +DjVuFile::merge_anno(ByteStream &out) +{ + // Reuse get_merged_anno(), which is better than the previous + // implementation due to three things: + // 1. It works even before the file is completely decoded + // 2. It merges annotations taking into account where a child DjVuFile + // is included. + // 3. It handles loops in DjVuFile's hierarchy + + const GP<ByteStream> str(get_merged_anno()); + if (str) + { + str->seek(0); + if (out.tell()) + { + out.write((void *) "", 1); + } + out.copy(*str); + } +} + +void +DjVuFile::get_text(ByteStream &out) +{ + const GP<ByteStream> str(get_text()); + if (str) + { + str->seek(0); + if (out.tell()) + { + out.write((void *) "", 1); + } + out.copy(*str); + } +} + +void +DjVuFile::get_meta(ByteStream &out) +{ + const GP<ByteStream> str(get_meta()); + if (str) + { + str->seek(0); + if (out.tell()) + { + out.write((void *) "", 1); + } + out.copy(*str); + } +} + + + +//**************************************************************************** +//******************************* Modifying ********************************** +//**************************************************************************** + +void +DjVuFile::remove_anno(void) +{ + DEBUG_MSG("DjVuFile::remove_anno()\n"); + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<ByteStream> gstr_out(ByteStream::create()); + + GUTF8String chkid; + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + if (!iff_in.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + + while(iff_in.get_chunk(chkid)) + { + if (!is_annotation(chkid)) + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + + iff_out.close_chunk(); + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + anno=0; + + flags|=MODIFIED; + data_pool->clear_stream(); +} + +void +DjVuFile::remove_text(void) +{ + DEBUG_MSG("DjVuFile::remove_text()\n"); + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<ByteStream> gstr_out(ByteStream::create()); + + GUTF8String chkid; + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + if (!iff_in.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + + while(iff_in.get_chunk(chkid)) + { + if (!is_text(chkid)) + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + + iff_out.close_chunk(); + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + text=0; + + flags|=MODIFIED; + data_pool->clear_stream(); +} + +void +DjVuFile::remove_meta(void) +{ + DEBUG_MSG("DjVuFile::remove_meta()\n"); + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<ByteStream> gstr_out(ByteStream::create()); + + GUTF8String chkid; + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + if (!iff_in.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + + while(iff_in.get_chunk(chkid)) + { + if (!is_meta(chkid)) + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + + iff_out.close_chunk(); + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + meta=0; + + flags|=MODIFIED; + data_pool->clear_stream(); +} + +void +DjVuFile::rebuild_data_pool(void) +{ + data_pool=get_djvu_data(false,false); + chunks_number=1; + flags|=MODIFIED; +} + +// Do NOT comment this function out. It's used by DjVuDocEditor to convert +// old-style DjVu documents to BUNDLED format. + +GP<DataPool> +DjVuFile::unlink_file(const GP<DataPool> & data, const GUTF8String &name) +// Will process contents of data[] and remove any INCL chunk +// containing 'name' +{ + DEBUG_MSG("DjVuFile::unlink_file()\n"); + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + const GP<ByteStream> str_in(data->get_stream()); + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + + int chksize; + GUTF8String chkid; + if (!iff_in.get_chunk(chkid)) return data; + + iff_out.put_chunk(chkid); + + while((chksize=iff_in.get_chunk(chkid))) + { + if (chkid=="INCL") + { + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + { + incl_str.setat(incl_str.length()-1, 0); + } + if (incl_str!=name) + { + iff_out.put_chunk(chkid); + iff_out.get_bytestream()->writestring(incl_str); + iff_out.close_chunk(); + } + } else + { + iff_out.put_chunk(chkid); + char buffer[1024]; + int length; + for(const GP<ByteStream> gbs(iff_out.get_bytestream()); + (length=iff_in.read(buffer, 1024));) + { + gbs->writall(buffer, length); + } + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + iff_out.close_chunk(); + iff_out.flush(); + gstr_out->seek(0, SEEK_SET); + data->clear_stream(); + return DataPool::create(gstr_out); +} + +#ifndef NEED_DECODER_ONLY +void +DjVuFile::insert_file(const GUTF8String &id, int chunk_num) +{ + DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "', chunk_num=" + << chunk_num << "\n"); + DEBUG_MAKE_INDENT(3); + + // First: create new data + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + int chunk_cnt=0; + bool done=false; + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid); + int chksize; + while((chksize=iff_in.get_chunk(chkid))) + { + if (chunk_cnt++==chunk_num) + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(id); + iff_out.close_chunk(); + done=true; + } + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + iff_in.close_chunk(); + } + if (!done) + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(id); + iff_out.close_chunk(); + } + iff_out.close_chunk(); + } + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + // Second: create missing DjVuFiles + process_incl_chunks(); + + flags|=MODIFIED; + data_pool->clear_stream(); +} +#endif + +void +DjVuFile::unlink_file(const GUTF8String &id) +{ + DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Remove the file from the list of included files + { + GURL url=DjVuPort::get_portcaster()->id_to_url(this, id); + if (url.is_empty()) url=GURL::UTF8(id,this->url.base()); + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;) + if (inc_files_list[pos]->get_url()==url) + { + GPosition this_pos=pos; + ++pos; + inc_files_list.del(this_pos); + } else ++pos; + } + + // And update the data. + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid); + int chksize; + while((chksize=iff_in.get_chunk(chkid))) + { + if (chkid!="INCL") + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } else + { + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + incl_str.setat(incl_str.length()-1, 0); + if (incl_str!=id) + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(incl_str); + iff_out.close_chunk(); + } + } + iff_in.close_chunk(); + } + iff_out.close_chunk(); + } + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + flags|=MODIFIED; +} + +void +DjVuFile::change_info(GP<DjVuInfo> xinfo,const bool do_reset) +{ + DEBUG_MSG("DjVuFile::change_text()\n"); + // Mark this as modified + set_modified(true); + if(do_reset) + reset(); + info=xinfo; +} + +#ifndef NEED_DECODER_ONLY +void +DjVuFile::change_text(GP<DjVuTXT> txt,const bool do_reset) +{ + DEBUG_MSG("DjVuFile::change_text()\n"); + GP<DjVuText> gtext_c=DjVuText::create(); + DjVuText &text_c=*gtext_c; + if(contains_text()) + { + const GP<ByteStream> file_text(get_text()); + if(file_text) + { + text_c.decode(file_text); + } + } + GCriticalSectionLock lock(&text_lock); + // Mark this as modified + set_modified(true); + if(do_reset) + reset(); + text_c.txt = txt; + text=ByteStream::create(); + text_c.encode(text); +} + +void +DjVuFile::change_meta(const GUTF8String &xmeta,const bool do_reset) +{ + DEBUG_MSG("DjVuFile::change_meta()\n"); + // Mark this as modified + set_modified(true); + if(contains_meta()) + { + (void)get_meta(); + } + if(do_reset) + reset(); + GCriticalSectionLock lock(&meta_lock); + meta=ByteStream::create(); + if(xmeta.length()) + { + const GP<IFFByteStream> giff=IFFByteStream::create(meta); + IFFByteStream &iff=*giff; + iff.put_chunk("METz"); + { + GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50); + gbsiff->writestring(xmeta); + } + iff.close_chunk(); + } +} +#endif + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFile.h b/kviewshell/plugins/djvu/libdjvu/DjVuFile.h new file mode 100644 index 00000000..ea0e6db3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFile.h @@ -0,0 +1,848 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFile.h,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUFILE_H +#define _DJVUFILE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuInfo.h" +#include "DjVuPalette.h" +#include "DjVuPort.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DjVuTXT; +class ByteStream; +class DataPool; +class JB2Image; +class JB2Dict; +class IW44Image; +class IFFByteStream; +class GPixmap; +class DjVuNavDir; + + +/** @name DjVuFile.h + Files #"DjVuFile.h"# and #"DjVuFile.cpp"# contain implementation of the + \Ref{DjVuFile} class, which takes the leading role in decoding of + \Ref{DjVuImage}s. + + In the previous releases of the library the work of decoding has been + entirely done in \Ref{DjVuImage}. Now, due to the introduction of multipage + documents, the decoding procedure became significantly more complex and + has been moved out from \Ref{DjVuImage} into \Ref{DjVuFile}. + + There is not much point though in creating just \Ref{DjVuFile} alone. + The maximum power of the decoder is achieved when you create the + \Ref{DjVuDocument} and work with {\bf it} when decoding the image. + + @memo Classes representing DjVu files. + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DjVuFile.h,v 1.9 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** #DjVuFile# plays the central role in decoding \Ref{DjVuImage}s. + First of all, it represents a DjVu file whether it's part of a + multipage all-in-one-file DjVu document, or part of a multipage + DjVu document where every page is in a separate file, or the whole + single page document. #DjVuFile# can read its contents from a file + and store it back when necessary. + + Second, #DjVuFile# does the greatest part of decoding work. In the + past this was the responsibility of \Ref{DjVuImage}. Now, with the + introduction of the multipage DjVu formats, the decoding routines + have been extracted from the \Ref{DjVuImage} and put into this separate + class #DjVuFile#. + + As \Ref{DjVuImage} before, #DjVuFile# now contains public class + variables corresponding to every component, that can ever be decoded + from a DjVu file (such as #INFO# chunk, #BG44# chunk, #SJBZ# chunk, etc.). + + As before, the decoding is initiated by a single function + (\Ref{start_decode}() in this case, and \Ref{DjVuImage::decode}() before). + The difference is that #DjVuFile# now handles threads creation itself. + When you call the \Ref{start_decode}() function, it creates the decoding + thread, which starts decoding, and which can create additional threads: + one per each file included into this one. + + {\bf Inclusion} is also a new feature specifically designed for a + multipage document. Indeed, inside a given document there can be a lot + of things shared between its pages. Examples can be the document + annotation (\Ref{DjVuAnno}) and other things like shared shapes and + dictionary (to be implemented). To avoid putting these chunks into + every page, we have invented new chunk called #INCL# which purpose is + to make the decoder open the specified file and decode it. + + {\bf Source of data.} The #DjVuFile# can be initialized in two ways: + \begin{itemize} + \item With #URL# and \Ref{DjVuPort}. In this case #DjVuFile# will + request its data thru the communication mechanism provided by + \Ref{DjVuPort} in the constructor. If this file references + (includes) any other file, data for them will also be requested + in the same way. + \item With \Ref{ByteStream}. In this case the #DjVuFile# will read + its data directly from the passed stream. This constructor + has been added to simplify creation of #DjVuFile#s, which do + no include anything else. In this case the \Ref{ByteStream} + is enough for the #DjVuFile# to initialize. + \end{itemize} + + {\bf Progress information.} #DjVuFile# does not do decoding silently. + Instead, it sends a whole set of notifications through the mechanism + provided by \Ref{DjVuPort} and \Ref{DjVuPortcaster}. It tells the user + of the class about the progress of the decoding, about possible errors, + chunk being decoded, etc. The data is requested using this mechanism too. + + {\bf Creating.} Depending on where you have data of the DjVu file, the + #DjVuFile# can be initialized in two ways: + \begin{itemize} + \item By providing #URL# and pointer to \Ref{DjVuPort}. In this case + #DjVuFile# will request data using communication mechanism + provided by \Ref{DjVuPort}. This is useful when the data is on + the web or when this file includes other files. + \item By providing a \Ref{ByteStream} with the data for the file. Use + it only when the file doesn't include other files. + \end{itemize} + There is also a bunch of functions provided for composing + the desired \Ref{DjVuDocument} and modifying #DjVuFile# structure. The + examples are \Ref{delete_chunks}(), \Ref{insert_chunk}(), + \Ref{include_file}() and \Ref{unlink_file}(). + + {\bf Caching.} In the case of plugin it's important to do the caching + of decoded images or files. #DjVuFile# appears to be the best candidate + for caching, and that's why it supports this procedure. Whenever a + #DjVuFile# is successfully decoded, it's added to the cache by + \Ref{DjVuDocument}. Next time somebody needs it, it will be extracted + from the cache directly by \Ref{DjVuDocument} and won't be decoded again. + + {\bf URLs.} Historically the biggest strain is put on making the decoder + available for Netscape and IE plugins where the original files reside + somewhere in the net. That is why #DjVuFile# uses {\bf URLs} to + identify itself and other files. If you're working with files on the + hard disk, you have to use the local URLs instead of file names. + A good way to do two way conversion is the \Ref{GOS} class. Sometimes it + happens that a given file does not reside anywhere but the memory. No + problem in this case either. There is a special port \Ref{DjVuMemoryPort}, + which can associate any URL with the corresponding data in the memory. + All you need to do is to invent your own URL prefix for this case. + "#memory:#" will do. The usage of absolute URLs has many advantages among + which is the capability to cache files with their URL being the cache key. + + Please note, that the #DjVuFile# class has been designed to work closely + with \Ref{DjVuDocument}. So please review the documentation on this class + too. */ + +class DjVuFile : public DjVuPort +{ +public: + enum { DECODING=1, DECODE_OK=2, DECODE_FAILED=4, DECODE_STOPPED=8, + DATA_PRESENT=16, ALL_DATA_PRESENT=32, INCL_FILES_CREATED=64, + MODIFIED=128, DONT_START_DECODE=256, STOPPED=512, + BLOCKED_STOPPED=1024, CAN_COMPRESS=2048, NEEDS_COMPRESSION=4096 }; + enum { STARTED=1, FINISHED=2 }; + + /** @name Decoded file contents */ + //@{ + /// Pointer to the DjVu file information component. + GP<DjVuInfo> info; + /// Pointer to the background component of DjVu image (IW44 encoded). + GP<IW44Image> bg44; + /// Pointer to the background component of DjVu image (Raw). + GP<GPixmap> bgpm; + /// Pointer to the mask of foreground component of DjVu image (JB2 encoded). + GP<JB2Image> fgjb; + /// Pointer to the optional shape dictionary for the mask (JB2 encoded). + GP<JB2Dict> fgjd; + /// Pointer to a colors layer for the foreground component of DjVu image. + GP<GPixmap> fgpm; + /// Pointer to a colors vector for the foreground component of DjVu image. + GP<DjVuPalette> fgbc; + /// Pointer to collected annotation chunks. + GP<ByteStream> anno; + /// Pointer to collected hiddentext chunks. + GP<ByteStream> text; + /// Pointer to meta data chunks. + GP<ByteStream> meta; + /// Pointer to the *old* navigation directory contained in this file + GP<DjVuNavDir> dir; + /// Description of the file formed during decoding + GUTF8String description; + /// MIME type string describing the DjVu data. + GUTF8String mimetype; + /// Size of the file. + int file_size; + //@} + +protected: + /** Default constructor. Must follow with an init() */ + DjVuFile(void); +public: + virtual ~DjVuFile(void); + + /** Initializes a #DjVuFile# object. This is a simplified initializer, + which is not supposed to be used for decoding or creating + #DjVuFile#s, which include other files. + + If the file is stored on the hard drive, you may also use the + other constructor and pass it the file's URL and #ZERO# #port#. + The #DjVuFile# will read the data itself. + + If you want to receive error messages and notifications, you + may connect the #DjVuFile# to your own \Ref{DjVuPort} after + it has been constructed. + + @param str The stream containing data for the file. */ + void init(const GP<ByteStream> & str); + + /** Creator, does the init(ByteStream &str) */ + static GP<DjVuFile> create( const GP<ByteStream> & str, + const ErrorRecoveryAction recover_action=ABORT, + const bool verbose_eof=true); + + /** Initializes a #DjVuFile# object. As you can notice, the data is not + directly passed to this function. The #DjVuFile# will ask for it + through the \Ref{DjVuPort} mechanism before the constructor + finishes. If the data is stored locally on the hard disk then the + pointer to \Ref{DjVuPort} may be set to #ZERO#, which will make + #DjVuFile# read all data from the hard disk and report all errors + to #stderr#. + + {\bf Note}. If the file includes (by means of #INCL# chunks) other + files then you should be ready to + \begin{enumerate} + \item Reply to requests \Ref{DjVuPort::id_to_url}() issued to + translate IDs (used in #INCL# chunks) to absolute URLs. + Usually, when the file is created by \Ref{DjVuDocument} + this job is done by it. If you construct such a file + manually, be prepared to do the ID to URL translation + \item Provide data for all included files. + \end{enumerate} + + @param url The URL assigned to this file. It will be used when + the #DjVuFile# asks for data. + @param port All communication between #DjVuFile#s and \Ref{DjVuDocument}s + is done through the \Ref{DjVuPort} mechanism. If the {\em url} + is not local or the data does not reside on the hard disk, + the {\em port} parameter must not be #ZERO#. If the {\em port} + is #ZERO# then #DjVuFile# will create an internal instance + of \Ref{DjVuSimplePort} for accessing local files and + reporting errors. It can later be disabled by means + of \Ref{disable_standard_port}() function. */ + void init(const GURL & url, GP<DjVuPort> port=0); + + /** Creator, does the init(const GURL &url, GP<DjVuPort> port=0) */ + static GP<DjVuFile> create( + const GURL & url, GP<DjVuPort> port=0, + const ErrorRecoveryAction recover_action=ABORT, + const bool verbose_eof=true); + + /** Disables the built-in port for accessing local files, which may + have been created in the case when the #port# argument to + the \Ref{DjVuFile::DjVuFile}() constructor is #ZERO# */ + void disable_standard_port(void); + + /** Looks for #decoded# navigation directory (\Ref{DjVuNavDir}) in this + or included files. Returns #ZERO# if nothing could be found. + + {\bf Note.} This function does {\bf not} attempt to decode #NDIR# + chunks. It is looking for predecoded components. #NDIR# can be + decoded either during regular decoding (initiated by + \Ref{start_decode}() function) or by \Ref{decode_ndir}() function, + which processes this and included files recursively in search + of #NDIR# chunks and decodes them. */ + GP<DjVuNavDir> find_ndir(void); + + /** @name #DjVuFile# flags query functions */ + //@{ + /** Returns the #DjVuFile# flags. The value returned is the + result of ORing one or more of the following constants: + \begin{itemize} + \item #DECODING# The decoding is in progress + \item #DECODE_OK# The decoding has finished successfully + \item #DECODE_FAILED# The decoding has failed + \item #DECODE_STOPPED# The decoding has been stopped by + \Ref{stop_decode}() function + \item #DATA_PRESENT# All data for this file has been received. + It's especially important in the case of Netscape or IE + plugins when the data is being received while the + decoding is done. + \item #ALL_DATA_PRESENT# Not only data for this file, but also + for all included file has been received. + \item #INCL_FILES_CREATED# All #INCL# and #INCF# chunks have been + processed and the corresponding #DjVuFile#s created. This + is important to know to be sure that the list returned by + \Ref{get_included_files}() is OK. + \end{itemize} */ + long get_flags(void) const; + /// Returns #TRUE# if the file is being decoded. + bool is_decoding(void) const; + /// Returns #TRUE# if decoding of the file has finished successfully. + bool is_decode_ok(void) const; + /// Returns #TRUE# if decoding of the file has failed. + bool is_decode_failed(void) const; + /** Returns #TRUE# if decoding of the file has been stopped by + \Ref{stop_decode}() function. */ + bool is_decode_stopped(void) const; + /// Returns #TRUE# if this file has received all data. + bool is_data_present(void) const; + /** Returns #TRUE# if this file {\bf and} all included files have + received all data. */ + bool is_all_data_present(void) const; + /** Returns #TRUE# if all included files have been created. Only when + this function returns 1, the \Ref{get_included_files}() returns + the correct information. */ + bool are_incl_files_created(void) const; + bool is_modified(void) const; + bool needs_compression(void) const; + bool can_compress(void) const; + void set_modified(bool m); + void set_needs_compression(bool m); + void set_can_compress(bool m); + //@} + + /// Returns the URL assigned to this file + GURL get_url(void) const; + + /** @name Decode control routines */ + //@{ + /** Starts decode. If threads are enabled, the decoding will be + done in another thread. Be sure to use \Ref{wait_for_finish}() + or listen for notifications sent through the \Ref{DjVuPortcaster} + to remain in sync. */ + void start_decode(void); + /** Start the decode iff not already decoded. If sync is true, wait + wait for decode to complete. Returns true of start_decode is called. + */ + bool resume_decode(const bool sync=false); + /** Stops decode. If #sync# is 1 then the function will not return + until the decoding thread actually dies. Otherwise it will + just signal the thread to stop and will return immediately. + Decoding of all included files will be stopped too. */ + void stop_decode(bool sync); + /** Recursively stops all data-related operations. + + Depending on the value of #only_blocked# flag this works as follows: + \begin{itemize} + \item If #only_blocked# is #TRUE#, the function will make sure, + that any further access to the file's data will result + in a #STOP# exception if the desired data is not available + (and the thread would normally block). + \item If #only_blocked# is #FALSE#, then {\bf any} further + access to the file's data will result in immediate + #STOP# exception. + \end{itemize} + + The action of this function is recursive, meaning that any #DjVuFile# + included into this one will also be stopped. + + Use this function when you don't need the #DjVuFile# anymore. The + results cannot be undone, and the whole idea is to make all threads + working with this file exit with the #STOP# exception. */ + void stop(bool only_blocked); + /** Wait for the decoding to finish. This will wait for the + termination of included files too. */ + void wait_for_finish(void); + /** Looks for #NDIR# chunk (navigation directory), and decodes its + contents. If the #NDIR# chunk has not been found in {\em this} file, + but this file includes others, the procedure will continue + recursively. This function is useful to obtain the document + navigation directory before any page has been decoded. After it + returns the directory can be obtained by calling \Ref{find_ndir}() + function. + + {\bf Warning.} Contrary to \Ref{start_decode}(), this function + does not return before it completely decodes the directory. + Make sure, that this file and all included files have enough data. */ + GP<DjVuNavDir> decode_ndir(void); + /// Clears all decoded components. + void reset(void); + /** Processes #INCL# chunks and creates included files. + Normally you won't need to call this function because included + files are created automatically when the file is being decoded. + But if due to some reason you'd like to obtain the list of included + files without decoding this file, this is an ideal function to call. + + {\bf Warning.} This function does not return before it reads the + whole file, which may block your application under some circumstances + if not all data is available. */ + void process_incl_chunks(void); + //@} + + // Function needed by the cache + unsigned int get_memory_usage(void) const; + + /** Returns the list of included DjVuFiles. + + {\bf Warning.} Included files are normally created during decoding. + Before that they do not exist. If you call this function at + that time and set #only_created# to #FALSE# then it will have to + read all the data from this file in order to find #INCL# chunks, + which may block your application, if not all data is available. + + @param only_created If #TRUE#, the file will not try to process + #INCL# chunks and load referenced files. It will return + just those files, which have already been created during + the decoding procedure. */ + GPList<DjVuFile> get_included_files(bool only_created=true); + + /** Includes a #DjVuFile# with the specified #id# into this one. + This function will also insert an #INCL# chunk at position + #chunk_num#. The function will request data for the included + file and will create it before returning. */ + void insert_file(const GUTF8String &id, int chunk_num=1); + /// Will get rid of included file with the given #id# + void unlink_file(const GUTF8String &id); + /** Will find an #INCL# chunk containing #name# in input #data# and + will remove it */ + static GP<DataPool> unlink_file(const GP<DataPool> & data, const GUTF8String &name); + + /// Returns the number of chunks in the IFF file data + int get_chunks_number(void); + /// Returns the name of chunk number #chunk_num# + GUTF8String get_chunk_name(int chunk_num); + /// Returns 1 if this file contains chunk with name #chunk_name# + bool contains_chunk(const GUTF8String &chunk_name); + + /** Processes the included files hierarchy and returns merged + annotations. This function may be used even when the #DjVuFile# + has not been decoded yet. If all data has been received for + this #DjVuFile# and all included #DjVuFile#s, it will will + gather annotations from them and will return the result. + If no annotations have been found, #ZERO# will be returned. + If either this #DjVuFile# or any of the included files do not + have all the data, the function will use the results of + decoding, which may have been started with the \Ref{start_decode}() + function. Otherwise #ZERO# will be returned as well. + + If #max_level_ptr# pointer is not zero, the function will use + it to store the maximum level number from which annotations + have been obtained. #ZERO# level corresponds to the top-level + page file. + + {\bf Summary:} This function will return complete annotations only + when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_merged_anno(int * max_level_ptr=0); + + /** Returns the annotation chunks (#"ANTa"# and #"ANTz"#). This + function may be used even when the #DjVuFile# has not been decoded + yet. If all data has been received for this #DjVuFile#, it will + gather hidden text and return the result. If no hidden text has + been found, #ZERO# will be returned. + + {\bf Summary:} This function will return complete annotations + only when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_anno(void); + + /** Returns the text chunks (#"TXTa"# and #"TXTz"#). This function may + be used even when the #DjVuFile# has not been decoded yet. If all + data has been received for this #DjVuFile#, it will gather hidden + text and return the result. If no hidden text has been found, + #ZERO# will be returned. + + {\bf Summary:} This function will return complete hidden text layers + only when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_text(void); + + /** Returns the meta chunks (#"METa"# and #"METz"#). This function may + be used even when the #DjVuFile# has not been decoded yet. If all + data has been received for this #DjVuFile#, it will gather metadata + and return the result. If no hidden text has been found, #ZERO# + will be returned. + + {\bf Summary:} This function will return complete meta data only + when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_meta(void); + + /** Goes down the hierarchy of #DjVuFile#s and merges their annotations. + (shouldn't this one be private?). + @param max_level_ptr If this pointer is not ZERO, the function + will use it to store the maximum level at which annotations + were found. Top-level page files have ZERO #level#. + @param ignore_list The function will not process included #DjVuFile#s + with URLs matching those mentioned in this #ignore_list#. */ + GP<ByteStream> get_merged_anno(const GList<GURL> & ignore_list, + int * max_level_ptr); + + /** Clears this file of all annotations. */ + void remove_anno(void); + + /** Clears the hidden text. */ + void remove_text(void); + + /// Clears the meta data. + void remove_meta(void); + + /** Returns #TRUE# if the file contains annotation chunks. + Known annotation chunks at the time of writing this help are: + {\bf ANTa}, {\bf ANTz}, {\bf FORM:ANNO}. */ + bool contains_anno(void); + + /** Returns #TRUE# if the file contains hiddentext chunks. + Known hiddentext chunks at the time of writing this help are: + {\bf TXTa}, and {\bf TXTz}. */ + bool contains_text(void); + + /** Returns #TRUE# if the file contains metadata chunks. + Known metadata chunks at the time of writing this help are: + {\bf METa}, and {\bf METz}. */ + bool contains_meta(void); + + /** Changes the value of the hiddentext. */ + void change_info(GP<DjVuInfo> info, const bool do_reset=false); + + /** Changes the value of the hiddentext. */ + void change_text(GP<DjVuTXT> txt, const bool do_reset=false); + + /** Changes the value of the metadata. */ + void change_meta(const GUTF8String &meta, const bool do_reset=false); + + /** @name Encoding routines */ + //@{ + /** The main function that encodes data back into binary stream. + The data returned will reflect possible changes made into the + chunk structure, annotation chunks and navigation directory + chunk #NDIR#. + + {\bf Note:} The file stream will not have the magic + #0x41,0x54,0x26,0x54# + at the beginning. + + @param included_too Process included files too. */ + GP<ByteStream> get_djvu_bytestream(const bool included_too, const bool no_ndir=true); + + /** Same as \Ref{get_djvu_bytestream}(), returning a DataPool. + @param included_too Process included files too. */ + GP<DataPool> get_djvu_data(const bool included_too, const bool no_ndir=true ); + //@} + + // Internal. Used by DjVuDocument + GP<DataPool> get_init_data_pool(void) const { return data_pool; }; + + // Internal. Used by DjVuDocument. May block for data. + void move(const GURL & dir_url); + + /** Internal. Used by DjVuDocument. The #name# should {\bf not} + be encoded with \Ref{GOS::encode_reserved}(). */ + void set_name(const GUTF8String &name); + + // Internal. Used by DjVuDocument + GSafeFlags & get_safe_flags(void); + + // Internal. Used by DjVuImage + void merge_anno(ByteStream &out); + + // Internal. Used by DjVuImage + void get_text(ByteStream &out); + + // Internal. Used by DjVuImage + void get_meta(ByteStream &out); + + // Internal. Used by DjVuDocEditor + void rebuild_data_pool(void); + + // Functions inherited from DjVuPort + virtual bool inherits(const GUTF8String &class_name) const; + virtual void notify_chunk_done(const DjVuPort * source, const GUTF8String &name); + virtual void notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask); + virtual void set_recover_errors(const ErrorRecoveryAction=ABORT); + virtual void set_verbose_eof(const bool verbose_eof=true); + virtual void report_error(const GException &ex,const bool=true); + static void set_decode_codec(GP<GPixmap> (*codec)(ByteStream &bs)); + +protected: + GURL url; + GP<DataPool> data_pool; + + GPList<DjVuFile> inc_files_list; + GCriticalSection inc_files_lock; + GCriticalSection anno_lock; + GCriticalSection text_lock; + GCriticalSection meta_lock; + ErrorRecoveryAction recover_errors; + bool verbose_eof; + int chunks_number; +private: + bool initialized; + GSafeFlags flags; + + GThread * decode_thread; + GP<DataPool> decode_data_pool; + GP<DjVuFile> decode_life_saver; + + GP<DjVuPort> simple_port; + + GMonitor chunk_mon, finish_mon; + + // Functions called when the decoding thread starts + static void static_decode_func(void *); + void decode_func(void); + void decode(const GP<ByteStream> &str); + GUTF8String decode_chunk(const GUTF8String &chkid, + const GP<ByteStream> &str, bool djvi, bool djvu, bool iw44); + int get_dpi(int w, int h); + + // Functions dealing with the shape directory (fgjd) + static GP<JB2Dict> static_get_fgjd(void *); + GP<JB2Dict> get_fgjd(int block=0); + + // Functions used to wait for smth + void wait_for_chunk(void); + bool wait_for_finish(bool self); + + // INCL chunk processor + GP<DjVuFile> process_incl_chunk(ByteStream & str, int file_num=-1); + + // Trigger: called when DataPool has all data + static void static_trigger_cb(void *); + void trigger_cb(void); + + // Progress callback: called from time to time + static void progress_cb(int pos, void *); + static void get_merged_anno(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out, const GList<GURL> & ignore_list, + int level, int & max_level, GMap<GURL, void *> & map); + static void get_anno(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out); + static void get_text(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out); + static void get_meta(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out); + + void check() const; + GP<DjVuNavDir>find_ndir(GMap<GURL, void *> & map); + GP<DjVuNavDir>decode_ndir(GMap<GURL, void *> & map); + void add_djvu_data(IFFByteStream & str, + GMap<GURL, void *> & map, + const bool included_too, const bool no_ndir=true); + void move(GMap<GURL, void *> & map, const GURL & dir_url); +private: // dummy stuff + static void decode(ByteStream *); + static GUTF8String decode_chunk(const GUTF8String &, ByteStream *,bool,bool,bool); + static void get_merged_anno(const GP<DjVuFile> &,ByteStream *, + const GList<GURL> &, int, int &, GMap<GURL, void *> &); + static void get_text(const GP<DjVuFile> &,ByteStream *); + static void get_meta(const GP<DjVuFile> &,ByteStream *); + +}; + +inline long +DjVuFile::get_flags(void) const +{ + return flags; +} + +inline GSafeFlags & +DjVuFile::get_safe_flags(void) +{ + return flags; +} + +inline bool +DjVuFile::is_decoding(void) const +{ + return (flags & DECODING)!=0; +} + +inline bool +DjVuFile::is_decode_ok(void) const +{ + return (flags & DECODE_OK)!=0; +} + +inline bool +DjVuFile::is_decode_failed(void) const +{ + return (flags & DECODE_FAILED)!=0; +} + +inline bool +DjVuFile::is_decode_stopped(void) const +{ + return (flags & DECODE_STOPPED)!=0; +} + +inline bool +DjVuFile::is_data_present(void) const +{ + return (flags & DATA_PRESENT)!=0; +} + +inline bool +DjVuFile::is_all_data_present(void) const +{ + return (flags & ALL_DATA_PRESENT)!=0; +} + +inline bool +DjVuFile::are_incl_files_created(void) const +{ + return (flags & INCL_FILES_CREATED)!=0; +} + +inline bool +DjVuFile::is_modified(void) const +{ + return (flags & MODIFIED)!=0; +} + +inline void +DjVuFile::set_modified(bool m) +{ + flags=m ? (flags | MODIFIED) : (flags & ~MODIFIED); +} + +inline bool +DjVuFile::needs_compression(void) const +{ + return (flags & NEEDS_COMPRESSION)!=0; +} + +inline void +DjVuFile::set_needs_compression(bool m) +{ + if (m) flags=flags | NEEDS_COMPRESSION; + else flags=flags & ~NEEDS_COMPRESSION; +} + +inline bool +DjVuFile::can_compress(void) const +{ + return (flags & CAN_COMPRESS)!=0; +} + +inline void +DjVuFile::set_can_compress(bool m) +{ + if(info) + { + info->compressable=m; + } + if (m) + { + flags=flags | CAN_COMPRESS; + } else + { + flags=flags & ~CAN_COMPRESS; + } +} + +inline void +DjVuFile::disable_standard_port(void) +{ + simple_port=0; +} + +inline bool +DjVuFile::inherits(const GUTF8String &class_name) const +{ + return + (GUTF8String("DjVuFile") == class_name) || + DjVuPort::inherits(class_name); +// !strcmp("DjVuFile", class_name) || +// DjVuPort::inherits(class_name); +} + +inline void +DjVuFile::wait_for_finish(void) +{ + while(wait_for_finish(1)) + EMPTY_LOOP; +} + +inline GURL +DjVuFile::get_url(void) const +{ + return url; +} + +inline void +DjVuFile::set_verbose_eof +(const bool verbose) +{ + verbose_eof=verbose; +} + +inline void +DjVuFile::set_recover_errors +(const ErrorRecoveryAction action) +{ + recover_errors=action; +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.cpp new file mode 100644 index 00000000..13220a96 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.cpp @@ -0,0 +1,272 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFileCache.cpp,v 1.9 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuFileCache.h" +#include "debug.h" + +#include <stdlib.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +DjVuFileCache::~DjVuFileCache(void) {} + +int +DjVuFileCache::Item::qsort_func(const void * el1, const void * el2) +{ + const Item * item1=*(Item **) el1; + const Item * item2=*(Item **) el2; + time_t time1=item1->get_time(); + time_t time2=item2->get_time(); + return time1<time2 ? -1 : time1>time2 ? 1 : 0; +} + +void +DjVuFileCache::set_max_size(int xmax_size) +{ + DEBUG_MSG("DjVuFileCache::set_max_size(): resizing to " << xmax_size << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + max_size=xmax_size; + cur_size=calculate_size(); + + if (max_size>=0) clear_to_size(enabled ? max_size : 0); +} + +void +DjVuFileCache::enable(bool en) +{ + enabled=en; + set_max_size(max_size); +} + +void +DjVuFileCache::add_file(const GP<DjVuFile> & file) +{ + DEBUG_MSG("DjVuFileCache::add_file(): trying to add a new item\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + // See if the file is already cached + GPosition pos; + for(pos=list;pos;++pos) + if (list[pos]->get_file()==file) break; + + if (pos) list[pos]->refresh(); // Refresh the timestamp + else + { + // Doesn't exist in the list yet + int _max_size=enabled ? max_size : 0; + if (max_size<0) _max_size=max_size; + + int add_size=file->get_memory_usage(); + + if (_max_size>=0 && add_size>_max_size) + { + DEBUG_MSG("but this item is way too large => doing nothing\n"); + return; + } + + if (_max_size>=0) clear_to_size(_max_size-add_size); + + list.append(new Item(file)); + cur_size+=add_size; + file_added(file); + } +} + +void +DjVuFileCache::clear_to_size(int size) +{ + DEBUG_MSG("DjVuFileCache::clear_to_size(): dropping cache size to " << size << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + if (size==0) + { + list.empty(); + cur_size=0; + } else + if (list.size()>20) + { + // More than 20 elements in the cache: use qsort to + // sort them before picking up the oldest + GTArray<void *> item_arr(list.size()-1); + GPosition pos; + int i; + for(pos=list, i=0;pos;++pos, i++) + { + GP<Item> item=list[pos]; + item->list_pos=pos; + item_arr[i]=item; + } + + qsort(&item_arr[0], item_arr.size(), sizeof(item_arr[0]), Item::qsort_func); + + for(i=0;i<item_arr.size() && cur_size>(int) size;i++) + { + Item * item=(Item *) item_arr[i]; + cur_size-=item->get_size(); + GP<DjVuFile> file=item->file; + list.del(item->list_pos); + file_cleared(file); + if (cur_size<=0) cur_size=calculate_size(); + } + } else + { + // Less than 20 elements: no reason to presort + while(cur_size>(int) size) + { + if (!list.size()) + { + // Oops. Discrepancy due to an item changed its size + cur_size=0; + break; + } + + // Remove the oldest cache item + GPosition oldest_pos=list; + GPosition pos=list; + for(++pos;pos;++pos) + if (list[pos]->get_time()<list[oldest_pos]->get_time()) + oldest_pos=pos; + cur_size-=list[oldest_pos]->get_size(); + GP<DjVuFile> file=list[oldest_pos]->file; + list.del(oldest_pos); + file_cleared(file); + + // cur_size *may* become negative because items may change their + // size after they've been added to the cache + if (cur_size<=0) cur_size=calculate_size(); + } + } + + DEBUG_MSG("done: current cache size=" << cur_size << "\n"); +} + +int +DjVuFileCache::calculate_size(void) +{ + GCriticalSectionLock lock(&class_lock); + + int size=0; + for(GPosition pos=list;pos;++pos) + size+=list[pos]->get_size(); + return size; +} + +void +DjVuFileCache::del_file(const DjVuFile * file) +{ + DEBUG_MSG("DjVuFileCache::del_file(): Removing an item from cache\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + for(GPosition pos=list;pos;++pos) + if (list[pos]->get_file()==file) + { + GP<DjVuFile> file=list[pos]->get_file(); + cur_size-=list[pos]->get_size(); + list.del(pos); + file_deleted(file); + break; + } + if (cur_size<0) cur_size=calculate_size(); + DEBUG_MSG("current cache size=" << cur_size << "\n"); +} + +GPList<DjVuFileCache::Item> +DjVuFileCache::get_items(void) +{ + GCriticalSectionLock lock(&class_lock); + + return list; +} + +void +DjVuFileCache::file_added(const GP<DjVuFile> &) {} + +void +DjVuFileCache::file_deleted(const GP<DjVuFile> &) {} + +void +DjVuFileCache::file_cleared(const GP<DjVuFile> &) {} + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.h b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.h new file mode 100644 index 00000000..9898b8f3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.h @@ -0,0 +1,292 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFileCache.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUFILECACHE_H +#define _DJVUFILECACHE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuFile.h" + +#ifndef macintosh //MCW can't compile +# ifndef UNDER_CE +# include <sys/types.h> +# include <time.h> +# endif +#else +# include <time.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name DjVuFileCache.h + Files #"DjVuFileCache.h"# and #"DjVuFileCache.cpp"# implement a simple + caching mechanism for keeping a given number of \Ref{DjVuFile} instances + alive. The cache estimates the size of its elements and gets rid of + the oldest ones when necessary. + + See \Ref{DjVuFileCache} for details. + + @memo Simple DjVuFile caching class. + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DjVuFileCache.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** #DjVuFileCache# is a simple list of \Ref{DjVuFile} instances. It keeps + track of the total size of all elements and can get rid of the oldest + one once the total size becomes over some threshold. Its main purpose + is to keep the added \Ref{DjVuFile} instances alive until their size + exceeds some given threshold (set by \Ref{set_maximum_size}() function). + The user is supposed to use \Ref{DjVuPortcaster::name_to_port}() to + find a file corresponding to a given name. The cache provides no + naming services */ +#ifdef UNDER_CE +class DjVuFileCache : public GPEnabled +{ +protected: + DjVuFileCache(const int) {} +public: + static GP<DjVuFileCache> create(const int); + virtual ~DjVuFileCache(void); + void del_file(const DjVuFile *) {} + void add_file(const GP<DjVuFile> &) {} + void clear(void) {} + void set_max_size(int) {} + int get_max_size(void) const {return 0;} + void enable(bool en) {} + bool is_enabled(void) const {return false;} +} ; +#else +class DjVuFileCache : public GPEnabled +{ +protected: + DjVuFileCache(const int max_size=5*2*1024*1024); +public: + /** Constructs the #DjVuFileCache# + @param max_size Maximum allowed size of the cache in bytes. */ + static GP<DjVuFileCache> create(const int max_size=5*2*1024*1024); + + virtual ~DjVuFileCache(void); + + /** Removes file #file# from the cache */ + void del_file(const DjVuFile * file); + + /** Adds the given file to the cache. It it's already there, its + timestamp will be refreshed. */ + void add_file(const GP<DjVuFile> & file); + + /** Clears the cache. All items will be deleted. */ + void clear(void); + /** Sets new maximum size. If the total size of all items in the cache + is greater than #max_size#, the cache will be deleting the oldest + items until the size is OK. */ + void set_max_size(int max_size); + + /** Returns the maximum allowed size of the cache. */ + int get_max_size(void) const; + + /** Enables or disables the cache. See \Ref{is_enabled}() for details + @param en - If {\em en} is TRUE, the cache will be enabled. + Otherwise it will be disabled. + */ + void enable(bool en); + + /** Returns #TRUE# if the cache is enabled, #FALSE# otherwise. + When a cache is disabled, \Ref{add_file}(), and + \Ref{del_file}() do nothing. But the {\em maximum size} is preserved + inside the class so that next time the cache is enabled, it will + be configured the same way. Clearly this "enable/disable" thing is + for convenience only. One could easily simulate this behavior by + setting the {\em maximum size} of the cache to #ZERO#. */ + bool is_enabled(void) const; + +public: + class Item; + + class Item : public GPEnabled + { + public: + virtual ~Item(void); + time_t get_time(void) const; + + GP<DjVuFile> get_file(void) const; + unsigned int get_size(void) const; + + void refresh(void); + + public: + GP<DjVuFile> file; + time_t time; + GPosition list_pos; + static int qsort_func(const void * el1, const void * el2); + + Item(void); + Item(const GP<DjVuFile> & xfile); + }; + +protected: + GCriticalSection class_lock; + + /** This function is called right after the given file has been added + to the cache for management. */ + virtual void file_added(const GP<DjVuFile> & file); + /** This function is called when the given file is no longer + managed by the cache. */ + virtual void file_deleted(const GP<DjVuFile> & file); + /** This function is called when after the cache decides to get rid + of the file. */ + virtual void file_cleared(const GP<DjVuFile> & file); + + GPList<Item> get_items(void); +private: + GPList<Item> list; + bool enabled; + int max_size; + int cur_size; + + int calculate_size(void); + void clear_to_size(int size); +}; + + + +//@} + +inline +DjVuFileCache::Item::Item(void) : time(::time(0)) {} + +inline +DjVuFileCache::Item::Item(const GP<DjVuFile> & xfile) : + file(xfile), time(::time(0)) {} + +inline +DjVuFileCache::Item::~Item(void) {} + +inline GP<DjVuFile> +DjVuFileCache::Item::get_file(void) const +{ + return file; +} + +inline unsigned int +DjVuFileCache::Item::get_size(void) const +{ + return file->get_memory_usage(); +} + +inline time_t +DjVuFileCache::Item::get_time(void) const +{ + return time; +} + +inline void +DjVuFileCache::Item::refresh(void) +{ + time=::time(0); +} + +inline +DjVuFileCache::DjVuFileCache(const int xmax_size) : + enabled(true), max_size(xmax_size), cur_size(0) {} + +inline void +DjVuFileCache::clear(void) +{ + clear_to_size(0); +} + +inline bool +DjVuFileCache::is_enabled(void) const +{ + return enabled; +} + +inline int +DjVuFileCache::get_max_size(void) const +{ + return max_size; +} + +#endif + +inline GP<DjVuFileCache> +DjVuFileCache::create(const int max_size) +{ + return new DjVuFileCache(max_size); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.cpp new file mode 100644 index 00000000..b31b04bf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.cpp @@ -0,0 +1,255 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuGlobal.cpp,v 1.7 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +/** This file impliments the DjVuProgressTask elements. The memory + functions are implimented in a separate file, because only the memory + functions should be compiled with out overloading of the memory functions. + */ + + +#ifdef NEED_DJVU_PROGRESS +#include "DjVuGlobal.h" + + +// ---------------------------------------- + +#include "GOS.h" +#include "GThreads.h" +#include "GException.h" +#include "GContainer.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define INITIAL 500 +#define INTERVAL 250 + +class DjVuProgressTask::Data : public GPEnabled +{ +public: + djvu_progress_callback *callback; + DjVuProgressTask *head; + const char *gtask; + unsigned long lastsigdate; + Data(djvu_progress_callback *_callback): + callback(_callback), head(0), gtask(0), lastsigdate(0) {} +}; + + +static GPMap<void *,DjVuProgressTask::Data> & +get_map(void) +{ + static GPMap<void *,DjVuProgressTask::Data> xmap; + return xmap; +} + +djvu_progress_callback * +DjVuProgressTask::set_callback(djvu_progress_callback *_callback) +{ + djvu_progress_callback *retval=0; + if(_callback) + { + GMap<void *,GP<DjVuProgressTask::Data> > &map=get_map(); + void *threadID=GThread::current(); + if(map.contains(threadID)) + { + DjVuProgressTask::Data &data=*(map[threadID]); + retval=data.callback; + data.callback=_callback; + data.head=0; + data.gtask=0; + data.lastsigdate=0; + }else + { + map[threadID]=new Data(_callback); + } + }else + { + GMap<void *,GP<DjVuProgressTask::Data> > &map=get_map(); + void *threadID=GThread::current(); + if(map.contains(threadID)) + { + DjVuProgressTask::Data &data=*(map[threadID]); + retval=data.callback; + data.callback=0; + data.head=0; + data.gtask=0; + data.lastsigdate=0; + map.del(threadID); + } + } + return retval; +} + +DjVuProgressTask::DjVuProgressTask(const char *xtask,int nsteps) + : task(xtask),parent(0), nsteps(nsteps), runtostep(0), gdata(0), data(0) +{ + // gtask=task; + { + GMap<void *,GP<DjVuProgressTask::Data> > &map=get_map(); + void *threadID=GThread::current(); + if(map.contains(threadID)) + { + gdata=new GP<Data>; + Data &d=*(data=((*(GP<Data> *)gdata)=map[threadID])); + if(d.callback) + { + unsigned long curdate = GOS::ticks(); + startdate = curdate; + if (!d.head) + d.lastsigdate = curdate + INITIAL; + parent = d.head; + d.head = this; + } + } + } +} + +DjVuProgressTask::~DjVuProgressTask() +{ + if (data && data->callback) + { + if (data->head != this) + G_THROW( ERR_MSG("DjVuGlobal.not_compatible") ); + data->head = parent; + if (!parent) + { + unsigned long curdate = GOS::ticks(); + if((*(data->callback))(data->gtask?data->gtask:"",curdate-startdate, curdate-startdate)) + { + G_THROW("INTERRUPT"); + } + } + } + delete (GP<Data> *)gdata; +} + +void +DjVuProgressTask::run(int tostep) +{ + if(data) + { + data->gtask=task; + if ((data->callback)&&(tostep>runtostep)) + { + unsigned long curdate = GOS::ticks(); + if (curdate > data->lastsigdate + INTERVAL) + signal(curdate, curdate); + runtostep = tostep; + } + } +} + +void +DjVuProgressTask::signal(unsigned long curdate, unsigned long estdate) +{ + int inprogress = runtostep; + if (inprogress > nsteps) + inprogress = nsteps; + if (inprogress > 0) + { + const unsigned long enddate = startdate+ + (unsigned long)(((float)(estdate-startdate) * (float)nsteps) / (float)inprogress); + if (parent) + { + parent->signal(curdate, enddate); + } + else if (data && data->callback && curdate<enddate) + { + if((*(data->callback))(data->gtask?data->gtask:"",curdate-startdate, enddate-startdate)) + { + G_THROW("INTERRUPT"); + } + data->lastsigdate = curdate; + } + } +} + +// Progress callback +// +djvu_progress_callback * +djvu_set_progress_callback( djvu_progress_callback *callback ) +{ + return DjVuProgressTask::set_callback(callback); +} + +int djvu_supports_progress_callback(void) {return 1;} + +#else + +#ifndef HAS_DJVU_PROGRESS_TYPEDEF +extern "C" +{ + void *djvu_set_progress_callback(void *); + int djvu_supports_progress_callback(void); +} +void *djvu_set_progress_callback(void *) { return 0; } +int djvu_supports_progress_callback(void) {return 0;} +#else +int djvu_supports_progress_callback(void) {return 0;} +djvu_progress_callback * +djvu_set_progress_callback( djvu_progress_callback *) { return 0; } +#endif + +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.h b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.h new file mode 100644 index 00000000..bfdd4dd1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.h @@ -0,0 +1,398 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuGlobal.h,v 1.10 2004/08/04 02:36:59 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUGLOBAL_H +#define _DJVUGLOBAL_H +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#if defined(UNDER_CE) +# ifndef __WCEALT_H__ +inline void * operator new(size_t, void * ptr) { return ptr; } +# endif +#elif defined(HAVE_STDINCLUDES) +# include <new> +#else +# include <new.h> +#endif + +#ifdef WIN32 +# ifdef DLL_EXPORT +# define DJVUAPI __declspec(dllexport) +# else +# ifdef LIBDJVU_DLL_IMPORT +# define DJVUAPI __declspec(dllimport) +# endif +# endif +#endif +#ifndef DJVUAPI +# define DJVUAPI +#endif + + +/** @name DjVuGlobal.h + + This file is included by all include files in the DjVu reference library. + + If compilation symbols #NEED_DJVU_MEMORY#, #NEED_DJVU_PROGRESS# + or #NEED_DJVU_NAMES# are defined, this file enables + features which are useful for certain applications of the + DjVu Reference Library. These features are still experimental and + therefore poorly documented. + + @memo + Global definitions. + @version + #$Id: DjVuGlobal.h,v 1.10 2004/08/04 02:36:59 leonb Exp $# + @author + L\'eon Bottou <leonb@research.att.com> -- empty file.\\ + Bill Riemers <docbill@sourceforge.net> -- real work. */ +//@{ + + +/** @name DjVu Memory + + This section is enabled when compilation symbol #NEED_DJVU_MEMORY# is + defined. Function #_djvu_memory_callback# can be used to redefine the C++ + memory allocation operators. Some operating systems (e.g. Macintoshes) + require very peculiar memory allocation in shared objects. We redefine + the operators #new# and #delete# as #STATIC_INLINE# because we do not + want to export these redefined versions to other libraries. */ +//@{ +//@} + +#ifdef NEED_DJVU_MEMORY + +# include "DjVu.h" + +// These define the two callbacks needed for C++ +typedef void djvu_delete_callback(void *); +typedef void *djvu_new_callback(size_t); + +// These functions allow users to set the callbacks. +int djvu_memoryObject_callback ( djvu_delete_callback*, djvu_new_callback*); +int djvu_memoryArray_callback ( djvu_delete_callback*, djvu_new_callback*); + +// We need to use this inline function in all modules, but we never want it to +// appear in the symbol table. It seems different compilers need different +// directives to do this... +# ifndef STATIC_INLINE +# ifdef __GNUC__ +# define STATIC_INLINE extern inline +# else /* !__GNUC__ */ +# define STATIC_INLINE static inline +# endif /* __GNUC__ */ +# endif /* STATIC_INLINE */ + +// This clause is used when overriding operator new +// because the standard has slightly changed. +# if defined( __GNUC__ ) && ( __GNUC__*1000 + __GNUC_MINOR__ >= 2091 ) +# ifndef new_throw_spec +# define new_throw_spec throw(std::bad_alloc) +# endif /* new_throw_spec */ +# ifndef delete_throw_spec +# define delete_throw_spec throw() +# endif /* delete_throw_spec */ +# endif /* __GNUC__ ... */ +// Old style +# ifndef new_throw_spec +# define new_throw_spec +# endif /* new_throw_spec */ +# ifndef delete_throw_spec +# define delete_throw_spec +# endif /* delete_throw_spec */ + +# ifdef UNIX +extern djvu_new_callback *_djvu_new_ptr; +extern djvu_new_callback *_djvu_newArray_ptr; +extern djvu_delete_callback *_djvu_delete_ptr; +extern djvu_delete_callback *_djvu_deleteArray_ptr; + +# ifndef NEED_DJVU_MEMORY_IMPLEMENTATION +void *operator new (size_t) new_throw_spec; +void *operator new[] (size_t) new_throw_spec; +void operator delete (void *) delete_throw_spec; +void operator delete[] (void *) delete_throw_spec; + +STATIC_INLINE void * +operator new(size_t sz) new_throw_spec +{ return (*_djvu_new_ptr)(sz); } +STATIC_INLINE void +operator delete(void *addr) delete_throw_spec +{ return (*_djvu_delete_ptr)(addr); } +STATIC_INLINE void * +operator new [] (size_t sz) new_throw_spec +{ return (*_djvu_newArray_ptr)(sz); } +STATIC_INLINE void +operator delete [] (void *addr) delete_throw_spec +{ return (*_djvu_deleteArray_ptr)(addr); } +# endif /* NEED_DJVU_MEMORY_IMPLEMENTATION */ + +# else /* UNIX */ + +# ifndef NEED_DJVU_MEMORY_IMPLEMENTATION +STATIC_INLINE void * +operator new(size_t sz) new_throw_spec +{ return _djvu_new(sz); } +inline_as_macro void +operator delete(void *addr) delete_throw_spec +{ return _djvu_delete(addr); } +inline_as_macro void * +operator new [] (size_t sz) new_throw_spec +{ return _djvu_new(sz); } +inline_as_macro void +operator delete [] (void *addr) delete_throw_spec +{ _djvu_deleteArray(addr); } +# endif /* !NEED_DJVU_MEMORY_IMPLEMENTATION */ + +# endif /* UNIX */ + +#else + +# define _djvu_free(ptr) free((ptr)) +# define _djvu_malloc(siz) malloc((siz)) +# define _djvu_realloc(ptr,siz) realloc((ptr),(siz)) +# define _djvu_calloc(siz,items) calloc((siz),(items)) + +#endif /* NEED_DJVU_MEMORY */ + +/** @name DjVu Progress + + This section is enabled when compilation symbol #NEED_DJVU_PROGRESS# is + defined. This macro setups callback function that may be used to + implement a progress indicator for the encoding routines. The decoding + routines do not need such a facility because it is sufficient to monitor + the calls to function \Ref{ByteStream::read} in class \Ref{ByteStream}. + + {\bf Code tracing macros} --- + Monitoring the progress of such complex algorithms requires significant + code support. This is achieved by inserting {\em code tracing macros} + in strategic regions of the code. + \begin{description} + \item[DJVU_PROGRESS_TASK(name,task,nsteps)] indicates that the current + scope performs a task roughly divided in #nsteps# equal steps, with + the specified #task# string used in the callback. + \item[DJVU_PROGRESS_RUN(name,tostep)] indicates that we are starting + an operation which will take us to step #tostep#. The operation + will be considered finished when #DJVU_PROGRESS_RUN# will be called + again with an argument greater than #tostep#. The execution of + this operation of course can be described by one subtask and so on. + \end{description} + + {\bf Progress callback} --- Before defining the outermost task, you can + store a callback function pointer into the static member variable + #DjVuProgressTask::callback#. This callback function is called + periodically with two unsigned long arguments. The first argument is the + elapsed time. The second argument is the estimated total execution time. + Both times are given in milliseconds. + + {\bf Important Note} --- This monitoring mechanism should not be used by + multithreaded programs. */ +//@{ + +#ifndef HAS_DJVU_PROGRESS_CALLBACKS +# define HAS_DJVU_PROGRESS_CALLBACKS + +# ifdef NEED_DJVU_PROGRESS +# include "DjVu.h" + +extern djvu_progress_callback *_djvu_progress_ptr; + +# define DJVU_PROGRESS_TASK(name,task,nsteps) DjVuProgressTask task_##name(task,nsteps) +# define DJVU_PROGRESS_RUN(name,tostep) { task_##name.run(tostep); } + +class DjVuProgressTask +{ +public: + class Data; + ~DjVuProgressTask(); + DjVuProgressTask(const char *task,int nsteps); + void run(int tostep); + const char *task; + static djvu_progress_callback *set_callback(djvu_progress_callback *ptr=0); +private: + DjVuProgressTask *parent; + int nsteps; + int runtostep; + unsigned long startdate; + // Statics + void *gdata; + Data *data; + // Helpers + void signal(unsigned long curdate, unsigned long estdate); +}; + +# else // ! NEED_DJVU_PROGRESS + +# define DJVU_PROGRESS_TASK(name,task,nsteps) +# define DJVU_PROGRESS_RUN(name,step) + +# endif // ! NEED_DJVU_PROGRESS +#endif // HAS_DJVU_PROGRESS_CALLBACKS +//@} + + +/** @name General functions. + + This section contains functions that replace some of the standard + system calls without any other header file dependancies. + */ + +#ifdef __cplusplus +# define DJVUEXTERNCAPI(x) extern "C" DJVUAPI x; +#else +# define DJVUEXTERNCAPI(x) extern DJVUAPI x +#endif + +/** This replaces fprintf(stderr,...), but with UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintErrorUTF8(const char *fmt, ...)) + +/** This replaces fprintf(stderr,...), but with UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintErrorNative(const char *fmt, ...)) + +/** This replaces printf(...), but requires UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintMessageUTF8(const char *fmt, ...)) + +/** This replaces printf(...), but requires UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintMessageNative(const char *fmt, ...)) + +/** The format (fmt) and arguments define a MessageList to be looked + up in the external messages and printed to stderr. */ +DJVUEXTERNCAPI(void DjVuFormatErrorUTF8(const char *fmt, ...)) + +/** The format (fmt) and arguments define a MessageList to be looked + up in the external messages and printed to stderr. */ +DJVUEXTERNCAPI(void DjVuFormatErrorNative(const char *fmt, ...)) + +/** Prints the translation of message to stderr. */ +DJVUEXTERNCAPI(void DjVuWriteError( const char *message )) + +/** Prints the translation of message to stdout. */ +DJVUEXTERNCAPI(void DjVuWriteMessage( const char *message )) + +/** A C function to perform a message lookup. Arguments are a buffer to + received the translated message, a buffer size (bytes), and a + message_list. The translated result is returned in msg_buffer encoded + in UTF-8. In case of error, msg_buffer is empty + (i.e., msg_buffer[0] == '\0'). +*/ +DJVUEXTERNCAPI(void DjVuMessageLookUpUTF8( + char *msg_buffer, const unsigned int buffer_size, + const char *message )) +DJVUEXTERNCAPI(void DjVuMessageLookUpNative( + char *msg_buffer, const unsigned int buffer_size, + const char *message )) + +/** This function sets the program name used when + searching for language files. +*/ +DJVUEXTERNCAPI(const char *djvu_programname(const char *programname)) + + +/** @name DjVu Names + + This section is enabled when compilation symbol #NEED_DJVU_NAMES# is + defined. This section redefines class names in order to unclutter the + name space of shared objects. This is useful on systems which + automatically export all global symbols when building a shared object. + @args */ +//@{ +//@} + +#ifdef NEED_DJVU_NAMES +/* The contents of this section may be generated by this shell command : + * % egrep -h '^(class|struct) +[A-Z_][A-Za-z0-9_]*' *.h *.cpp |\ + * sed -e 's:[a-z]* *\([A-Za-z_][A-Za-z0-9_]*\).*:#define \1 DJVU_\1:g' |\ + * sort + */ +#endif // NEED_DJVU_NAMES + +//@} + +#if defined(macintosh) +# define EMPTY_LOOP continue +#else +# define EMPTY_LOOP /* nop */ +#endif + +// The ERR_MSG(x) macro is intended to permit automated checking of the +// externalized error message names against the source code. It has no +// effect on the executed program. It should be used to surround each +// message name that will need to be looked up in the external message +// files. In particular, it should use on all strings passed to G_THROW. +#ifndef HAS_CTRL_C_IN_ERR_MSG +# define HAS_CTRL_C_IN_ERR_MSG 1 +#endif +#ifndef ERR_MSG +# if HAS_CTRL_C_IN_ERR_MSG +// This hack allows for the coexistence of internationalized +// and non-internationalized code. All internationalized error +// message names are prefixed with a ctrl-c. Only these will +// be looked for in the message files. Messages that do no +// start with a ctrl-c will remain untranslated. +# define ERR_MSG(x) "\003" x +# else +# define ERR_MSG(x) x +# endif +#endif + +#endif /* _DJVUGLOBAL_H_ */ + + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuGlobalMemory.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuGlobalMemory.cpp new file mode 100644 index 00000000..1c684336 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuGlobalMemory.cpp @@ -0,0 +1,306 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuGlobalMemory.cpp,v 1.6 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#ifdef NEED_DJVU_MEMORY +#ifndef NEED_DJVU_MEMORY_IMPLEMENTATION +#define NEED_DJVU_MEMORY_IMPLEMENTATION +#endif /* NEED_DJVU_MEMORY_IMPLEMENTATION */ + +#include "DjVuGlobal.h" +#include "GException.h" +#include <stdlib.h> +#include <string.h> +#include "debug.h" + +#ifdef UNIX +djvu_delete_callback * +_djvu_delete_ptr=(djvu_delete_callback *)&(operator delete); +djvu_delete_callback * +_djvu_deleteArray_ptr=(djvu_delete_callback *)&(operator delete []); +djvu_new_callback * +_djvu_new_ptr=(djvu_new_callback *)&(operator new); +djvu_new_callback * +_djvu_newArray_ptr=(djvu_new_callback *)&(operator new []); +#endif + +static djvu_delete_callback *_djvu_delete_handler = 0; +static djvu_new_callback *_djvu_new_handler = 0; +static djvu_delete_callback *deleteArray_handler = 0; +static djvu_new_callback *newArray_handler = 0; + +static djvu_free_callback *_djvu_free_handler = 0; +static djvu_realloc_callback *_djvu_realloc_handler = 0; +static djvu_calloc_callback *_djvu_calloc_handler = 0; +static djvu_malloc_callback *_djvu_malloc_handler = 0; + +int +djvu_memoryObject_callback ( + djvu_delete_callback* delete_handler, + djvu_new_callback* new_handler +) { + if(delete_handler && new_handler) + { +#ifdef UNIX + _djvu_new_ptr=&_djvu_new; + _djvu_delete_ptr=&_djvu_delete; +#endif + _djvu_delete_handler=delete_handler; + _djvu_new_handler=new_handler; + return 1; + }else + { +#ifdef UNIX + _djvu_new_ptr=(djvu_new_callback *)&(operator new); + _djvu_delete_ptr=(djvu_delete_callback *)&(operator delete); +#endif + _djvu_delete_handler=0; + _djvu_new_handler=0; + return (delete_handler||new_handler)?0:1; + } + return 0; +} + +int +djvu_set_memory_callbacks +( + djvu_free_callback *free_handler, + djvu_realloc_callback *realloc_handler, + djvu_malloc_callback *malloc_handler, + djvu_calloc_callback *calloc_handler +) +{ + if(free_handler && realloc_handler && malloc_handler) + { +#ifdef UNIX + _djvu_new_ptr=(djvu_new_callback *)&_djvu_new; + _djvu_delete_ptr=(djvu_delete_callback *)&_djvu_delete; +#endif + _djvu_new_handler=(djvu_new_callback *)malloc_handler; + _djvu_delete_handler=(djvu_delete_callback *)free_handler; + _djvu_malloc_handler=(djvu_malloc_callback *)malloc_handler; + _djvu_free_handler=(djvu_free_callback *)free_handler; + _djvu_realloc_handler=(djvu_realloc_callback *)realloc_handler; + if(calloc_handler) + { + _djvu_calloc_handler=(djvu_calloc_callback *)&calloc_handler; + }else + { + _djvu_calloc_handler=0; + } + return 1; + }else + { +#ifdef UNIX + _djvu_new_ptr=(djvu_new_callback *)&(operator new); + _djvu_delete_ptr=(djvu_delete_callback *)&(operator delete); +#endif + _djvu_delete_handler=0; + _djvu_new_handler=0; + _djvu_malloc_handler=0; + _djvu_free_handler=0; + _djvu_realloc_handler=0; + _djvu_calloc_handler=0; + return !(_djvu_malloc_handler + ||_djvu_free_handler + ||_djvu_realloc_handler + ||_djvu_calloc_handler); + } +} + +DJVUAPI void * +_djvu_new(size_t siz) +{ + void *ptr; +#ifndef UNIX + if(_djvu_new_handler) + { +#endif + if(!(ptr=(*_djvu_new_handler)(siz?siz:1))) + { + G_THROW( ERR_MSG("DjVuGlobalMemory.exhausted") ); + } +#ifndef UNIX + }else + { + ptr=::operator new(siz?siz:1); + } +#endif + return ptr; +} + +void +_djvu_delete(void *addr) +{ + if(addr) + { + if(_djvu_delete_handler) + { + (*_djvu_delete_handler)(addr); + }else + { + operator delete(addr); + } + } +} + +void * +_djvu_newArray(size_t siz) +{ + void *ptr; +#ifndef UNIX + if(newArray_handler) + { +#endif + if(!(ptr=(*newArray_handler)(siz?siz:1))) + { + G_THROW( ERR_MSG("DjVuGlobalMemory.exhausted") ); + } +#ifndef UNIX + }else + { + ptr=::new unsigned char[siz?siz:1]; + } +#endif + return ptr; +} + +void +_djvu_deleteArray(void *addr) +{ + if(addr) + { + if(deleteArray_handler) + { + (*deleteArray_handler)(addr); + }else + { +#ifdef WIN32 + delete [] (addr) ; +#else + operator delete [] (addr); +#endif + } + } +} + +void * +_djvu_malloc(size_t siz) +{ + DEBUG_MSG("_djvu_malloc: siz="<<siz<<"\n"); + return _djvu_malloc_handler?(*_djvu_malloc_handler)(siz?siz:1):malloc(siz?siz:1); +} + +void * +_djvu_calloc(size_t siz, size_t items) +{ + DEBUG_MSG("_djvu_calloc: siz="<<siz<<" items="<<items<<"\n"); + void *ptr; + if( _djvu_calloc_handler ) + { + ptr = (*_djvu_calloc_handler)(siz?siz:1, items?items:1); + }else if( _djvu_malloc_handler ) + { + if((ptr = (*_djvu_malloc_handler)((siz?siz:1)*(items?items:1)))&&siz&&items) + { + memset(ptr,0,siz*items); + } + }else + { + ptr = calloc(siz?siz:1, items?items:1); + } + return ptr; +} + +void * +_djvu_realloc(void* ptr, size_t siz) +{ + DEBUG_MSG("_djvu_realloc: ptr="<<ptr<<" siz="<<siz<<"\n"); + void *newptr; + if( _djvu_realloc_handler ) + { + newptr = (*_djvu_realloc_handler)(ptr, siz); + }else + { + newptr = realloc(ptr, siz?siz:1); + } + return newptr; +} + +void +_djvu_free(void *ptr) +{ + DEBUG_MSG("_djvu_free: ptr="<<ptr<<"\n"); + if(ptr) + { + if( _djvu_free_handler ) + { + (*_djvu_free_handler)(ptr); + }else + { + free(ptr); + } + } +} + +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp new file mode 100644 index 00000000..f384ce97 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp @@ -0,0 +1,1486 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuImage.cpp,v 1.10 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuImage.h" +#include "GScaler.h" +#include "DjVuDocument.h" +#include "DjVuPalette.h" +#include "GContainer.h" +#include "GSmartPointer.h" +#include "JB2Image.h" +#include "IW44Image.h" +#include "DataPool.h" +#include "ByteStream.h" +#include "GMapAreas.h" +#include "DjVuText.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "debug.h" +#include <stdarg.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +//// DJVUIMAGE: CONSTRUCTION + +DjVuImage::DjVuImage(void) +: rotate_count(-1),relayout_sent(false) +{ +} + +void +DjVuImage::connect(const GP<DjVuFile> & xfile) +{ + file=xfile; + DjVuPort::get_portcaster()->add_route(file, this); +} + + + + +//// DJVUIMAGE: DATA COLLECTORS + +GP<DjVuInfo> +DjVuImage::get_info(const GP<DjVuFile> & file) const +{ + if (file->info) + { + if(rotate_count<0) + { + const_cast<DjVuImage *>(this)->init_rotate(*(file->info)); + } + return file->info; + } + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuInfo> info=get_info(list[pos]); + if (info) + { + if(rotate_count<0) + { + const_cast<DjVuImage *>(this)->init_rotate(*(file->info)); + } + return info; + } + } + return 0; +} + +GP<IW44Image> +DjVuImage::get_bg44(const GP<DjVuFile> & file) const +{ + if (file->bg44) + return file->bg44; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<IW44Image> bg44=get_bg44(list[pos]); + if (bg44) + return bg44; + } + return 0; +} + +GP<GPixmap> +DjVuImage::get_bgpm(const GP<DjVuFile> & file) const +{ + if (file->bgpm) + return file->bgpm; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<GPixmap> bgpm=get_bgpm(list[pos]); + if (bgpm) return bgpm; + } + return 0; +} + +GP<JB2Image> +DjVuImage::get_fgjb(const GP<DjVuFile> & file) const +{ + if (file->fgjb) + return file->fgjb; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<JB2Image> fgjb=get_fgjb(list[pos]); + if (fgjb) + return fgjb; + } + return 0; +} + +GP<GPixmap> +DjVuImage::get_fgpm(const GP<DjVuFile> & file) const +{ + if (file->fgpm) + return file->fgpm; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<GPixmap> fgpm=get_fgpm(list[pos]); + if (fgpm) + return fgpm; + } + return 0; +} + +GP<DjVuPalette> +DjVuImage::get_fgbc(const GP<DjVuFile> & file) const +{ + if (file->fgbc) + return file->fgbc; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuPalette> fgbc=get_fgbc(list[pos]); + if (fgbc) return fgbc; + } + return 0; +} + +GP<DjVuInfo> +DjVuImage::get_info() const +{ + if (file) + { + return get_info(file); + }else + { + return 0; + } +} + +GP<ByteStream> +DjVuImage::get_anno() const +{ + GP<ByteStream> out = ByteStream::create(); + ByteStream &mbs = *out; + if (file) + { + file->merge_anno(mbs); + } + mbs.seek(0); + if(!mbs.size()) + { + out=0; + } + return out; +} + +GP<ByteStream> +DjVuImage::get_text() const +{ + GP<ByteStream> out = ByteStream::create(); + ByteStream &mbs = *out; + if (file) + { + file->get_text(mbs); + } + mbs.seek(0); + if(!mbs.size()) + { + out=0; + } + return out; +} + +GP<ByteStream> +DjVuImage::get_meta() const +{ + GP<ByteStream> out = ByteStream::create(); + ByteStream &mbs = *out; + if (file) + { + file->get_meta(mbs); + } + mbs.seek(0); + if(!mbs.size()) + { + out=0; + } + return out; +} + +GP<IW44Image> +DjVuImage::get_bg44() const +{ + if (file) + return get_bg44(file); + else + return 0; +} + +GP<GPixmap> +DjVuImage::get_bgpm() const +{ + if (file) + return get_bgpm(file); + else + return 0; +} + +GP<JB2Image> +DjVuImage::get_fgjb() const +{ + if (file) + return get_fgjb(file); + else + return 0; +} + +GP<GPixmap> +DjVuImage::get_fgpm() const +{ + if (file) + return get_fgpm(file); + else + return 0; +} + +GP<DjVuPalette> +DjVuImage::get_fgbc() const +{ + if (file) + return get_fgbc(file); + else + return 0; +} + +int +DjVuImage::get_width() const +{ + GP<DjVuInfo> info=get_info(); + return info?((rotate_count&1)?(info->height):(info->width)):0; +} + +int +DjVuImage::get_height() const +{ + GP<DjVuInfo> info=get_info(); + return info?((rotate_count&1)?(info->width):(info->height)):0; +} + +int +DjVuImage::get_real_width() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->width : 0; +} + +int +DjVuImage::get_real_height() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->height : 0; +} + +int +DjVuImage::get_version() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->version : DJVUVERSION; +} + +int +DjVuImage::get_dpi() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->dpi : 300; +} + +int +DjVuImage::get_rounded_dpi() const +{ + return (get_dpi()+5)/10*10; +#if 0 + /* This code used to round the reported dpi to 25, 50, 75, 100, 150, + 300, and 600. Now we just round the dpi to 10ths and return it */ + int dpi=get_dpi(); + if (dpi>700) return dpi; + + const int std_dpi[]={ 25, 50, 75, 100, 150, 300, 600 }; + const int std_dpis=sizeof(std_dpi)/sizeof(std_dpi[0]); + int min_dist=abs(dpi-std_dpi[0]); + int min_idx=0; + for(int i=1;i<std_dpis;i++) + if (abs(std_dpi[i]-dpi)<min_dist) + { + min_dist=abs(std_dpi[i]-dpi); + min_idx=i; + }; + return std_dpi[min_idx]; +#endif +} + +double +DjVuImage::get_gamma() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->gamma : 2.2; +} + +GUTF8String +DjVuImage::get_mimetype() const +{ + return file ? file->mimetype : GUTF8String(); +} + + +//// DJVUIMAGE: UTILITIES + +GUTF8String +DjVuImage::get_short_description() const +{ + GUTF8String msg = "Empty"; + int width = get_width(); + int height = get_height(); + if (width && height) + if (file && file->file_size>100) + //msg.format("%dx%d in %0.1f Kb", width, height, file->file_size/1024.0); + msg.format( ERR_MSG("DjVuImage.short1") "\t%d\t%d\t%0.1f", width, height, file->file_size/1024.0 ); + else + //msg.format("%dx%d", width, height); + msg.format( ERR_MSG("DjVuImage.short2") "\t%d\t%d", width, height ); + return msg; +} + +GUTF8String +DjVuImage::get_long_description() const +{ + return file?(file->description):GUTF8String(); +} + + +void +DjVuImage::notify_chunk_done(const DjVuPort *, const GUTF8String & name) +{ + if (!relayout_sent && + ( !name.cmp("INFO", 4) || + !name.cmp("PMxx", 2) || + !name.cmp("BMxx", 2) ) ) + { + DjVuPort::get_portcaster()->notify_relayout(this); + relayout_sent=true; + } + else if (!name.cmp("Sxxx", 1) || + !name.cmp("BGxx", 2) || + !name.cmp("FGxx", 2) || + !name.cmp("BMxx", 2) || + !name.cmp("PMxx", 2) ) + DjVuPort::get_portcaster()->notify_redisplay(this); +} + + + + + + +//// DJVUIMAGE: OLD-STYLE DECODING + +DjVuInterface::~DjVuInterface() +{ +} + +class DjVuImageNotifier : public DjVuPort +{ + friend class DjVuImage; + DjVuInterface *notifier; + GP<DataPool> stream_pool; + GURL stream_url; +public: + DjVuImageNotifier(DjVuInterface *notifier); + GP<DataPool> request_data(const DjVuPort *src, const GURL & url); + void notify_chunk_done(const DjVuPort *, const GUTF8String &name); + void notify_redisplay(const class DjVuImage * source); + void notify_relayout(const class DjVuImage * source); +}; + +DjVuImageNotifier::DjVuImageNotifier(DjVuInterface *notifier) + : notifier(notifier) +{ +} + +GP<DataPool> +DjVuImageNotifier::request_data(const DjVuPort *src, const GURL & url) +{ + if (url!=stream_url) + G_THROW( ERR_MSG("DjVuImage.not_decode") ); + return stream_pool; +} + +void +DjVuImageNotifier::notify_redisplay(const class DjVuImage * source) +{ + if (notifier) + notifier->notify_redisplay(); +} + +void +DjVuImageNotifier::notify_relayout(const class DjVuImage * source) +{ + if (notifier) + notifier->notify_relayout(); +} + +void +DjVuImageNotifier::notify_chunk_done(const DjVuPort *, const GUTF8String &name) +{ + if (notifier) + notifier->notify_chunk(name, "" ); +} + +void +DjVuImage::decode(ByteStream & str, DjVuInterface *notifier) +{ + DEBUG_MSG("DjVuImage::decode(): decoding old way...\n"); + DEBUG_MAKE_INDENT(3); + if (file) + G_THROW( ERR_MSG("DjVuImage.bad_call") ); + GP<DjVuImageNotifier> pport = new DjVuImageNotifier(notifier); + pport->stream_url=GURL::UTF8("internal://fake/fake.djvu"); + pport->stream_pool=DataPool::create(); + // Get all the data first + int length; + char buffer[1024]; + while((length=str.read(buffer, 1024))) + pport->stream_pool->add_data(buffer, length); + pport->stream_pool->set_eof(); + GP<DjVuDocument> doc = DjVuDocument::create_wait(pport->stream_url, (DjVuImageNotifier*)pport); + GP<DjVuImage> dimg=doc->get_page(-1, true, (DjVuImageNotifier*)pport); + file=dimg->get_djvu_file(); + if (file->is_decode_stopped()) + G_THROW( DataPool::Stop ); + if (file->is_decode_failed()) + G_THROW( ByteStream::EndOfFile ); // guess + if (!file->is_decode_ok()) + G_THROW( ERR_MSG("DjVuImage.mult_error") ); + DEBUG_MSG("decode DONE\n"); +} + + +//// DJVUIMAGE: CHECKING + +static int +compute_red(int w, int h, int rw, int rh) +{ + for (int red=1; red<16; red++) + if (((w+red-1)/red==rw) && ((h+red-1)/red==rh)) + return red; + return 16; +} + + +int +DjVuImage::is_legal_bilevel() const +{ + // Components + GP<DjVuInfo> info = get_info(); + GP<JB2Image> fgjb = get_fgjb(); + GP<IW44Image> bg44 = get_bg44(); + GP<GPixmap> bgpm = get_bgpm(); + GP<GPixmap> fgpm = get_fgpm(); + // Check info + if (! info) + return 0; + int width = info->width; + int height = info->height; + if (! (width>0 && height>0)) + return 0; + // Check fgjb + if (!fgjb) + return 0; + if (fgjb->get_width()!=width || fgjb->get_height()!=height) + return 0; + // Check that color information is not present. + if (bg44 || bgpm || fgpm) + return 0; + // Ok. + return 1; +} + +int +DjVuImage::is_legal_photo() const +{ + // Components + GP<DjVuInfo> info = get_info(); + GP<JB2Image> fgjb = get_fgjb(); + GP<IW44Image> bg44 = get_bg44(); + GP<GPixmap> bgpm = get_bgpm(); + GP<GPixmap> fgpm = get_fgpm(); + // Check info + if (! info) + return 0; + int width = info->width; + int height = info->height; + if (! (width>0 && height>0)) + return 0; + // Check that extra information is not present. + if (fgjb || fgpm) + return 0; + // Check bg44 + if (bg44 && bg44->get_width()==width && bg44->get_height()==height) + return 1; + // Check bgpm + if (bgpm && (int)bgpm->columns()==width && (int)bgpm->rows()==height) + return 1; + // Ok. + return 0; +} + +int +DjVuImage::is_legal_compound() const +{ + // Components + GP<DjVuInfo> info = get_info(); + GP<JB2Image> fgjb = get_fgjb(); + GP<IW44Image> bg44 = get_bg44(); + GP<GPixmap> bgpm = get_bgpm(); + GP<GPixmap> fgpm = get_fgpm(); + GP<DjVuPalette> fgbc = get_fgbc(); + // Check size + if (! info) + return 0; + int width = info->width; + int height = info->height; + if (! (width>0 && height>0)) + return 0; + // Check fgjb + if (!fgjb) + return 0; + if (fgjb->get_width()!=width || fgjb->get_height()!=height) + return 0; + // Check background + int bgred = 0; + if (bg44) + bgred = compute_red(width, height, bg44->get_width(), bg44->get_height()); + else if (bgpm) + bgred = compute_red(width, height, bgpm->columns(), bgpm->rows()); + if (bgred<1 || bgred>12) + return 0; + // Check foreground colors + int fgred = 0; + if (fgbc) + fgred = 1; + else if (fgpm) + fgred = compute_red(width, height, fgpm->columns(), fgpm->rows()); + if (fgred<1 || fgred>12) + return 0; + // Check that all components are present + if (fgjb && bgred && fgred) + return 1; + // Unrecognized + return 0; +} + + +//// DJVUIMAGE: LOW LEVEL RENDERING + +GP<GBitmap> +DjVuImage::get_bitmap(const GRect &rect, + int subsample, int align) const +{ + // Access image size + int width = get_real_width(); + int height = get_real_height(); + GP<JB2Image> fgjb = get_fgjb(); + if ( width && height && fgjb && + (fgjb->get_width() == width) && + (fgjb->get_height() == height) ) + { + return fgjb->get_bitmap(rect, subsample, align); + } + return 0; +} + +GP<GPixmap> +DjVuImage::get_bg_pixmap(const GRect &rect, + int subsample, double gamma) const +{ + GP<GPixmap> pm = 0; + // Access image size + + GP<DjVuInfo> info = get_info(); + int width = get_real_width(); + int height = get_real_height(); + + + if (width<=0 || height<=0 || !info) return 0; + // Compute gamma_correction + double gamma_correction = 1.0; + if (gamma > 0) + gamma_correction = gamma / info->gamma; + if (gamma_correction < 0.1) + gamma_correction = 0.1; + else if (gamma_correction > 10) + gamma_correction = 10; + + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + // Determine how much bg44 is reduced + int red = compute_red(width,height,w,h); + if (red<1 || red>12) + return 0; + // Handle pure downsampling cases + if (subsample == red) + pm = bg44->get_pixmap(1,rect); + else if (subsample == 2*red) + pm = bg44->get_pixmap(2,rect); + else if (subsample == 4*red) + pm = bg44->get_pixmap(4,rect); + else if (subsample == 8*red) + pm = bg44->get_pixmap(8,rect); + // Handle fractional downsampling case + else if (red*4 == subsample*3) + { + GRect nrect = rect; + GRect xrect = rect; + xrect.xmin = (xrect.xmin/3)*4; + xrect.ymin = (xrect.ymin/3)*4; + xrect.xmax = ((xrect.xmax+2)/3)*4; + xrect.ymax = ((xrect.ymax+2)/3)*4; + nrect.translate(-xrect.xmin*3/4, -xrect.ymin*3/4); + if (xrect.xmax > w) + xrect.xmax = w; + if (xrect.ymax > h) + xrect.ymax = h; + GP<GPixmap> ipm = bg44->get_pixmap(1,xrect); + pm = GPixmap::create(); + pm->downsample43(ipm, &nrect); + } + // Handle all other cases with pixmapscaler + else + { + // find suitable power of two + int po2 = 16; + while (po2>1 && subsample<po2*red) + po2 >>= 1; + // setup pixmap scaler + int inw = (w+po2-1)/po2; + int inh = (h+po2-1)/po2; + int outw = (width+subsample-1)/subsample; + int outh = (height+subsample-1)/subsample; + GP<GPixmapScaler> gps=GPixmapScaler::create(inw, inh, outw, outh); + GPixmapScaler &ps=*gps; + ps.set_horz_ratio(red*po2, subsample); + ps.set_vert_ratio(red*po2, subsample); + // run pixmap scaler + GRect xrect; + ps.get_input_rect(rect,xrect); + GP<GPixmap> ipm = bg44->get_pixmap(po2,xrect); + pm = GPixmap::create(); + ps.scale(xrect, *ipm, rect, *pm); + } + // Apply gamma correction + if (pm && gamma_correction!=1.0) + pm->color_correct(gamma_correction); + return pm; + } + + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + // Determine how much bgpm is reduced + int red = compute_red(width,height,w,h); + if (red<1 || red>12) + return 0; + // Handle pure downsampling cases + int ratio = subsample/red; + if (subsample==ratio*red && ratio>=1) + { + pm = GPixmap::create(); + if (ratio == 1) + pm->init(*bgpm, rect); + else if (ratio > 1) + pm->downsample(bgpm, ratio, &rect); + } + // Handle all other cases with pixmapscaler + else + { + // setup pixmap scaler + int outw = (width+subsample-1)/subsample; + int outh = (height+subsample-1)/subsample; + GP<GPixmapScaler> gps=GPixmapScaler::create(w, h, outw, outh); + GPixmapScaler &ps=*gps; + ps.set_horz_ratio(red, subsample); + ps.set_vert_ratio(red, subsample); + // run pixmap scaler + pm = GPixmap::create(); + GRect xrect(0,0,w,h); + ps.scale(xrect, *bgpm, rect, *pm); + } + // Apply gamma correction + if (pm && gamma_correction!=1.0) + pm->color_correct(gamma_correction); + return pm; + } + + // FAILURE + return 0; +} + + + +int +DjVuImage::stencil(GPixmap *pm, const GRect &rect, + int subsample, double gamma) const +{ + // Warping and blending. + if (!pm) + return 0; + // Access components + + GP<DjVuInfo> info = get_info(); + int width = get_real_width(); + int height = get_real_height(); + + + if (width<=0 || height<=0 || !info) return 0; + GP<JB2Image> fgjb = get_fgjb(); + GP<GPixmap> fgpm = get_fgpm(); + GP<DjVuPalette> fgbc = get_fgbc(); + + // Compute gamma_correction + double gamma_correction = 1.0; + if (gamma > 0) + gamma_correction = gamma / info->gamma; + if (gamma_correction < 0.1) + gamma_correction = 0.1; + else if (gamma_correction > 10) + gamma_correction = 10; + + // Compute alpha map and relevant JB2Image components + GList<int> components; + GP<GBitmap> bm; + if (fgjb) + { + JB2Image *jimg = fgjb; + if (! (width && height && + jimg->get_width() == width && + jimg->get_height() == height ) ) + return 0; + // Decode bitmap + bm = GBitmap::create(rect.height(), rect.width()); + bm->set_grays(1+subsample*subsample); + int rxmin = rect.xmin * subsample; + int rymin = rect.ymin * subsample; + for (int blitno = 0; blitno < jimg->get_blit_count(); blitno++) + { + const JB2Blit *pblit = jimg->get_blit(blitno); + const JB2Shape &pshape = jimg->get_shape(pblit->shapeno); + if (pshape.bits && + pblit->left <= rect.xmax * subsample && + pblit->bottom <= rect.ymax * subsample && + pblit->left + (int)pshape.bits->columns() >= rect.xmin * subsample && + pblit->bottom + (int)pshape.bits->rows() >= rect.ymin * subsample ) + { + // Record component list + if (fgbc) components.append(blitno); + // Blit + bm->blit(pshape.bits, + pblit->left - rxmin, pblit->bottom - rymin, + subsample); + } + } + } + + + // TWO LAYER MODEL + if (bm && fgbc) + { + // Perform attenuation from scratch + pm->attenuate(bm, 0, 0); + // Check that fgbc has the correct size + JB2Image *jimg = fgjb; + DjVuPalette *fg = fgbc; + if (jimg->get_blit_count() != fg->colordata.size()) + return 0; + // Copy and color correct palette + int palettesize = fg->size(); + GTArray<GPixel> colors(0,palettesize-1); + for (int i=0; i<palettesize; i++) + fg->index_to_color(i, colors[i]); + GPixmap::color_correct(gamma_correction, colors, palettesize); + // Blit all components (one color at a time) + while (components.size() > 0) + { + GPosition nullpos; + GPosition pos = components; + int lastx = 0; + int colorindex = fg->colordata[components[pos]]; + if (colorindex >= palettesize) + G_THROW( ERR_MSG("DjVuImage.corrupted") ); + // Gather relevant components and relevant rectangle + GList<int> compset; + GRect comprect; + while (pos) + { + int blitno = components[pos]; + const JB2Blit *pblit = jimg->get_blit(blitno); + if (pblit->left < lastx) break; + lastx = pblit->left; + if (fg->colordata[blitno] == colorindex) + { + const JB2Shape &pshape = jimg->get_shape(pblit->shapeno); + GRect rect(pblit->left, pblit->bottom, + pshape.bits->columns(), pshape.bits->rows()); + comprect.recthull(comprect, rect); + compset.insert_before(nullpos, components, pos); + continue; + } + ++pos; + } + // Round alpha map rectangle + comprect.xmin = comprect.xmin / subsample; + comprect.ymin = comprect.ymin / subsample; + comprect.xmax = (comprect.xmax+subsample-1) / subsample; + comprect.ymax = (comprect.ymax+subsample-1) / subsample; + comprect.intersect(comprect, rect); + // Compute alpha map for that color + bm = 0; + bm = GBitmap::create(comprect.height(), comprect.width()); + bm->set_grays(1+subsample*subsample); + int rxmin = comprect.xmin * subsample; + int rymin = comprect.ymin * subsample; + for (pos=compset; pos; ++pos) + { + int blitno = compset[pos]; + const JB2Blit *pblit = jimg->get_blit(blitno); + const JB2Shape &pshape = jimg->get_shape(pblit->shapeno); + bm->blit(pshape.bits, + pblit->left - rxmin, pblit->bottom - rymin, + subsample); + } + // Blend color into background pixmap + pm->blit(bm, comprect.xmin-rect.xmin, comprect.ymin-rect.ymin, &colors[colorindex]); + } + return 1; + } + + + // THREE LAYER MODEL + if (bm && fgpm) + { + // This follows fig. 4 in Adelson "Layered representations for image + // coding" (1991) http://www-bcs.mit.edu/people/adelson/papers.html. + // The properly warped background is already in PM. The properly warped + // alpha map is already in BM. We just have to warp the foreground and + // perform alpha blending. +#ifdef SIMPLE_THREE_LAYER_RENDERING + int w = fgpm->columns(); + int h = fgpm->rows(); + // Determine foreground reduction + int red = compute_red(width,height, w, h); + if (red<1 || red>12) + return 0; + // Warp foreground pixmap + GPixmapScaler ps(w,h,width/subsample+1,height/subsample+1); + ps.set_horz_ratio(red,subsample); + ps.set_vert_ratio(red,subsample); + GP<GPixmap> nfg = new GPixmap; + GRect provided(0,0,w,h); + ps.scale(provided, *fgpm, rect, *nfg); + // Attenuate background and blit + nfg->color_correct(gamma_correction); + pm->blend(bm, 0, 0, nfg); // blend == attenuate + blit + return 1; +#else + // Things are now a little bit more complex because the convenient + // function GPixmap::stencil() simultaneously upsamples the foreground + // by an integer factor and performs the alpha blending. We have + // to determine how and when this facility can be used. + int w = fgpm->columns(); + int h = fgpm->rows(); + // Determine foreground reduction + int red = compute_red(width,height,w,h); + if (red<1 || red>12) + return 0; + // Try supersampling foreground pixmap by an integer factor + int supersample = ( red>subsample ? red/subsample : 1); + int wantedred = supersample*subsample; + // Try simple foreground upsampling + if (red == wantedred) + { + // Simple foreground upsampling is enough. + pm->stencil(bm, fgpm, supersample, &rect, gamma_correction); + return 1; + } + else + { + // Must pre-warp foreground pixmap + GP<GPixmap> nfg; + int desw = (w*red+wantedred-1)/wantedred; + int desh = (h*red+wantedred-1)/wantedred; + // Cache rescaled fgpm for speed + static const DjVuImage *tagimage = 0; + static const GPixmap *tagfgpm = 0; + static GP<GPixmap> cachednfg = 0; + // Check whether cached fgpm applies. + if ( cachednfg && this==tagimage && fgpm==tagfgpm + && desw==(int)cachednfg->columns() + && desh==(int)cachednfg->rows() ) + { + nfg = cachednfg; + } + else + { + GP<GPixmapScaler> gps=GPixmapScaler::create(w,h,desw,desh); + GPixmapScaler &ps=*gps; + ps.set_horz_ratio(red, wantedred); + ps.set_vert_ratio(red, wantedred); + nfg = GPixmap::create(); + GRect provided(0,0,w,h); + GRect desired(0,0,desw,desh); + ps.scale(provided, *fgpm, desired, *nfg); + } + // Use combined warp+blend function + pm->stencil(bm, nfg, supersample, &rect, gamma_correction); + // Cache + tagimage = this; + tagfgpm = fgpm; + cachednfg = nfg; + return 1; + } +#endif + } + + // FAILURE + return 0; +} + + +GP<GPixmap> +DjVuImage::get_fg_pixmap(const GRect &rect, + int subsample, double gamma) const +{ + // Obtain white background pixmap + GP<GPixmap> pm; + // Access components + const int width = get_real_width(); + const int height = get_real_height(); + if (width && height) + { + pm = GPixmap::create(rect.height(),rect.width(), &GPixel::WHITE); + if (!stencil(pm, rect, subsample, gamma)) + pm=0; + } + return pm; +} + + +GP<GPixmap> +DjVuImage::get_pixmap(const GRect &rect, int subsample, double gamma) const +{ + // Get background + GP<GPixmap> pm = get_bg_pixmap(rect, subsample, gamma); + // Superpose foreground + if (! stencil(pm, rect, subsample, gamma)) + // Avoid ugly progressive display (hack) + if (get_fgjb()) return 0; + // Return + return pm; +} + + +//// DJVUIMAGE: RENDERING (ARBITRARY SCALE) + +typedef GP<GBitmap>(DjVuImage::*BImager)(const GRect &, int, int) const; +typedef GP<GPixmap>(DjVuImage::*PImager)(const GRect &, int, double) const; + +static GP<GBitmap> +do_bitmap(const DjVuImage &dimg, BImager get, + const GRect &inrect, const GRect &inall, int align ) +{ + GRect rect=inrect; + GRect all=inall; +///* rotate code + if( dimg.get_rotate()%4 ) + { + GRectMapper mapper; + mapper.rotate((4-dimg.get_rotate())%4); + mapper.map(rect); + mapper.map(all); + } +///* rotate code ends + + // Sanity + if (! ( all.contains(rect.xmin, rect.ymin) && + all.contains(rect.xmax-1, rect.ymax-1) )) + G_THROW( ERR_MSG("DjVuImage.bad_rect") ); + // Check for integral reduction + int red; + int w = dimg.get_real_width(); + int h = dimg.get_real_height(); + + int rw = all.width(); + int rh = all.height(); + GRect zrect = rect; + zrect.translate(-all.xmin, -all.ymin); + for (red=1; red<=15; red++) + if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red) + { + GP<GBitmap> bm=(dimg.*get)(zrect, red, align); + if(bm) + return bm->rotate((4-dimg.get_rotate())%4); + else + return NULL; + } + // Find best reduction + for (red=15; red>1; red--) + if ( (rw*red < w && rh*red < h) || + (rw*red*3 < w || rh*red*3 < h) ) + break; + // Setup bitmap scaler + if (! (w && h)) return 0; + GP<GBitmapScaler> gbs=GBitmapScaler::create(); + GBitmapScaler &bs=*gbs; + bs.set_input_size( (w+red-1)/red, (h+red-1)/red ); + bs.set_output_size( rw, rh ); + bs.set_horz_ratio( rw*red, w ); + bs.set_vert_ratio( rh*red, h ); + // Scale + GRect srect; + bs.get_input_rect(zrect, srect); + GP<GBitmap> sbm = (dimg.*get)(srect, red, 1); + if (!sbm) return 0; + int border = ((zrect.width() + align - 1) & ~(align - 1)) - zrect.width(); + GP<GBitmap> bm = GBitmap::create(zrect.height(), zrect.width(), border); + bs.scale(srect, *sbm, zrect, *bm); + if( bm ) + return bm->rotate((4-dimg.get_rotate())%4); + else + return NULL; +} + +static GP<GPixmap> +do_pixmap(const DjVuImage &dimg, PImager get, + const GRect &inrect, const GRect &inall, double gamma ) +{ + + GRect rect=inrect; + GRect all=inall; +///* rotate code + if( dimg.get_rotate()%4 ) + { + GRectMapper mapper; + mapper.rotate((4-dimg.get_rotate())%4); + mapper.map(rect); + mapper.map(all); + } +///* rotate code ends + + // Sanity + if (! ( all.contains(rect.xmin, rect.ymin) && + all.contains(rect.xmax-1, rect.ymax-1) )) + G_THROW( ERR_MSG("DjVuImage.bad_rect2") ); + // Check for integral reduction + int red, w=0, h=0, rw=0, rh=0; + w = dimg.get_real_width(); + h = dimg.get_real_height(); + + + rw = all.width(); + rh = all.height(); + GRect zrect = rect; + zrect.translate(-all.xmin, -all.ymin); + for (red=1; red<=15; red++) + if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red) + { + GP<GPixmap> pm = (dimg.*get)(zrect, red, gamma); + if( pm ) + return pm->rotate((4-dimg.get_rotate())%4); + else + return NULL; + } + // These reductions usually go faster (improve!) + static int fastred[] = { 12,6,4,3,2,1 }; + // Find best reduction + for (int i=0; (red=fastred[i])>1; i++) + if ( (rw*red < w && rh*red < h) || + (rw*red*3 < w || rh*red*3 < h) ) + break; + // Setup pixmap scaler + if (w<0 || h<0) return 0; + GP<GPixmapScaler> gps=GPixmapScaler::create(); + GPixmapScaler &ps=*gps; + ps.set_input_size( (w+red-1)/red, (h+red-1)/red ); + ps.set_output_size( rw, rh ); + ps.set_horz_ratio( rw*red, w ); + ps.set_vert_ratio( rh*red, h ); + // Scale + GRect srect; + ps.get_input_rect(zrect, srect); + GP<GPixmap> spm = (dimg.*get)(srect, red, gamma); + if (!spm) return 0; + GP<GPixmap> pm = GPixmap::create(); + ps.scale(srect, *spm, zrect, *pm); + if(pm) + return pm->rotate((4-dimg.get_rotate())%4); + else + return NULL; +} + +GP<GPixmap> +DjVuImage::get_pixmap(const GRect &rect, const GRect &all, double gamma) const +{ + return do_pixmap(*this, & DjVuImage::get_pixmap, rect, all, gamma); +} + +GP<GBitmap> +DjVuImage::get_bitmap(const GRect &rect, const GRect &all, int align) const +{ + return do_bitmap(*this, & DjVuImage::get_bitmap, rect, all, align); +} + +GP<GPixmap> +DjVuImage::get_bg_pixmap(const GRect &rect, const GRect &all, double gamma) const +{ + return do_pixmap(*this, & DjVuImage::get_bg_pixmap, rect, all, gamma); +} + +GP<GPixmap> +DjVuImage::get_fg_pixmap(const GRect &rect, const GRect &all, double gamma) const +{ + return do_pixmap(*this, & DjVuImage::get_fg_pixmap, rect, all, gamma); +} + +int +DjVuImage::get_rotate() const +{ + return (rotate_count<0)?0:rotate_count; +} + +void +DjVuImage::init_rotate(const DjVuInfo &info) +{ + rotate_count=((360-GRect::findangle(info.orientation))/90)%4; +} + +void DjVuImage::set_rotate(int count) +{ + rotate_count=((count%4)+4)%4; +} + +GP<DjVuAnno> +DjVuImage::get_decoded_anno() +{ + GP<DjVuAnno> djvuanno = DjVuAnno::create(); + GP<ByteStream> bs=get_anno(); + if( bs ) + { + djvuanno->decode(bs); + + const int rotate_count=get_rotate(); + if( rotate_count % 4 ) + { + ///map hyperlinks correctly for rotation + GRect input, output; + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + + GPList<GMapArea> &list=djvuanno->ant->map_areas; + for(GPosition pos=list;pos;++pos) + { + list[pos]->unmap(mapper); + } + } + return djvuanno; + } + else + return NULL; +} + + +void +DjVuImage::map(GRect &rect) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.map(rect); + } +} + +void +DjVuImage::unmap(GRect &rect) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.unmap(rect); + } +} + +void +DjVuImage::map(int &x, int &y) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.map(x, y); + } +} + +void +DjVuImage::unmap(int &x, int &y) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.unmap(x, y); + } +} + +bool +DjVuImage::wait_for_complete_decode(void) +{ + if (file) + { + file->resume_decode(true); + return file->is_decode_ok(); + } + return 0; +} + +// Write out a DjVuXML object tag and map tag. +void +DjVuImage::writeXML(ByteStream &str_out,const GURL &doc_url,const int flags) const +{ + const int height=get_height(); + + static const char *Object="<OBJECT data=\""; + const GURL url(get_djvu_file()->get_url()); + const GUTF8String pagename(url.fname()); + GUTF8String page_param; + if(doc_url.is_valid() && !doc_url.is_empty() && (doc_url != url)) + { + str_out.writestring(Object+doc_url.get_string()); + page_param="<PARAM name=\"PAGE\" value=\""+pagename+"\" />\n"; + }else + { + str_out.writestring(Object+doc_url.get_string()); + } + str_out.writestring("\" type=\""+get_mimetype()+"\" height=\"" + +GUTF8String(height)+"\" width=\""+GUTF8String(get_width()) + +"\" usemap=\""+pagename.toEscaped()+"\" >\n"); + if(!(flags & NOINFO)) + { + const GP<DjVuInfo> info(get_info()); + if(info) + { + info->writeParam(str_out); + } + } + str_out.writestring(page_param); + const GP<DjVuAnno> anno(DjVuAnno::create()); + if(!(flags & NOINFO)||!(flags&NOMAP)) + { + const GP<ByteStream> anno_str(get_anno()); + if(anno_str) + { + anno->decode(anno_str); + } + if(!(flags & NOINFO)) + { + anno->writeParam(str_out); + } + } + if(!(flags & NOTEXT)) + { + const GP<DjVuText> text(DjVuText::create()); + { + const GP<ByteStream> text_str(get_text()); + if(text_str) + { + text->decode(text_str); + } + text->writeText(str_out,height); + } + } + if(!(flags & NOMETA)) + { + const GP<ByteStream> meta_str(get_meta()); + if(meta_str) + { + GP<IFFByteStream> giff=IFFByteStream::create(meta_str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + while( iff.get_chunk(chkid)) + { + GP<ByteStream> gbs(iff.get_bytestream()); + if(chkid == "METa") + { + str_out.copy(*gbs); + //str_out.writestring(gbs->getAsUTF8()); + }else if(chkid == "METz") + { + gbs=BSByteStream::create(gbs); + str_out.copy(*gbs); + //str_out.writestring(gbs->getAsUTF8()); + } + iff.close_chunk(); + } + } + } + str_out.writestring(GUTF8String("</OBJECT>\n")); + if(!(flags & NOMAP)) + { + anno->writeMap(str_out,pagename,height); + } +} + +// Write out a DjVuXML object tag and map tag. +void +DjVuImage::writeXML(ByteStream &str_out) const +{ + writeXML(str_out,GURL()); +} + +// Write out a DjVuXML object tag and map tag. +GUTF8String +DjVuImage::get_XML(const GURL &doc_url,const int flags) const +{ + GP<ByteStream> gbs(ByteStream::create()); + ByteStream &bs=*gbs; + writeXML(bs,doc_url); + bs.seek(0L); + return bs.getAsUTF8(); +} + +// Write out a DjVuXML object tag and map tag. +GUTF8String +DjVuImage::get_XML(void) const +{ + return get_XML(GURL()); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuImage.h b/kviewshell/plugins/djvu/libdjvu/DjVuImage.h new file mode 100644 index 00000000..57b40938 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuImage.h @@ -0,0 +1,449 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuImage.h,v 1.9 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUIMAGE_H +#define _DJVUIMAGE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVuImage.h + + Files #"DjVuImage.h"# and #"DjVuImage.cpp"# implement \Ref{DjVuImage} + class produced as a result of decoding of DjVu files. In the previous + version of the library both the rendering {\bf and} decoding have been + handled by \Ref{DjVuImage}. This is no longer true. Now the + \Ref{DjVuDocument} and \Ref{DjVuFile} classes handle decoding of both + single page and multi page documents. + + For compatibility reasons though, we still support old-style decoding + interface through the \Ref{DjVuImage} class for single page documents + {\em only}. As before, the display programs can call the decoding + function from a separate thread. The user interface thread may call + the rendering functions at any time. Rendering will be performed using + the most recent data generated by the decoding thread. This multithreaded + capability enables progressive display of remote images. + + {\bf Creating DjVu images} --- Class \Ref{DjVuImage} does not provide a + direct way to create a DjVu image. The recommended procedure consists of + directly writing the required chunks into an \Ref{IFFByteStream} as + demonstrated in program \Ref{djvumake}. Dealing with too many encoding + issues (such as chunk ordering and encoding quality) would indeed make the + decoder unnecessarily complex. + + {\bf ToDo: Layered structure} --- Class #DjVuImage# currently contains an + unstructured collection of smart pointers to various data structures. + Although it simplifies the rendering routines, this choice does not + reflect the layered structure of DjVu images and does not leave much room + for evolution. We should be able to do better. + + @memo + Decoding DjVu and IW44 images. + @author + L\'eon Bottou <leonb@research.att.com> - initial implementation + Andrei Erofeev <eaf@geocities.com> - multipage support + @version + #$Id: DjVuImage.h,v 1.9 2005/04/27 16:34:13 leonb Exp $# */ +//@{ + + + +#include "DjVuFile.h" +#include "DjVuAnno.h" +#include "GRect.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/* Obsolete class included for backward compatibility. */ + +class DjVuInterface +{ +public: + virtual ~DjVuInterface(); + virtual void notify_chunk(const char *chkid, const char *msg) = 0; + virtual void notify_relayout(void) = 0; + virtual void notify_redisplay(void) = 0; +}; + + +/** Main DjVu Image data structure. This class defines the internal + representation of a DjVu image. This representation consists of a few + pointers referencing the various components of the DjVu image. These + components are created and populated by the decoding function. The + rendering functions then can use the available components to compute a + pixel representation of the desired segment of the DjVu image. */ + +class DjVuImage : public DjVuPort +{ +protected: + DjVuImage(void); +public: + enum { NOINFO, NOTEXT=1, NOMAP=4, NOMETA=8 }; + // CONSTRUCTION + /** @name Construction. */ + //@{ + /** Creates an empty DjVu image. After the image has been constructed, + it may be connected to an existing \Ref{DjVuFile} or left as is. + + In the former case #DjVuImage# will look for its decoded components + (like #Sjbz# or #BG44#) by decending the hierarchy of \Ref{DjVuFile}s + starting from the one passed to \Ref{connect}(). + + In the latter case you can use \Ref{decode}() function to decode + {\bf single-page} DjVu documents in the old-style way. */ + static GP<DjVuImage> create(void) {return new DjVuImage();} + + /** Connects this #DjVuImage# to the passed \Ref{DjVuFile}. The #DjVuImage# + will use this \Ref{DjVuFile} to retrieve components necessary for + decoding. It will also connect itself to \Ref{DjVuFile} using the + communication mechanism provided by \Ref{DjVuPort} and \Ref{DjVuPortcaster}. + This will allow it to receive and relay messages and requests generated + by the passed \Ref{DjVuFile} and any file included into it. */ + void connect(const GP<DjVuFile> & file); + + /** This combines the above two steps for simplier code operations. */ + static GP<DjVuImage> create(const GP<DjVuFile> &file) + { const GP<DjVuImage> retval=create(); retval->connect(file); return retval; } + + //@} + + // COMPONENTS + /** @name Components. */ + //@{ + /** Returns a pointer to a DjVu information component. + This function returns a null pointer until the decoder + actually processes an #"INFO"# chunk. */ + GP<DjVuInfo> get_info() const; + /** Returns a pointer to the IW44 encoded background component of a DjVu + image. This function returns a null pointer until the decoder actually + processes an #"BG44"# chunk. */ + GP<IW44Image> get_bg44() const; + /** Returns a pointer to the raw background component of a DjVu image. The + background component is used for JPEG encoded backgrounds. This + function returns a null pointer until the decoder actually processes an + #"BGjp"# chunk. */ + GP<GPixmap> get_bgpm() const; + /** Returns a pointer to the mask of the foreground component of a DjVu + image. The mask of the foreground component is always a JB2 image in + this implementation. This function returns a null pointer until the + decoder actually processes an #"Sjbz"# chunk. */ + GP<JB2Image> get_fgjb() const; + /** Returns a pointer to the colors of the foreground component of a DjVu + image. The mask of the foreground component is always a small pixmap in + this implementation. This function returns a null pointer until the + decoder actually processes an #"FG44"# chunk. */ + GP<GPixmap> get_fgpm() const; + /** Returns a pointer to a palette object containing colors for the + foreground components of a DjVu image. These colors are only + pertinent with respect to the JB2Image. */ + GP<DjVuPalette> get_fgbc() const; + /** Returns a pointer to a ByteStream containing all the annotation + chunks collected so far for this image. Individual chunks can be + retrieved using \Ref{IFFByteStream}. Returns NULL if no chunks have been + collected yet. */ + GP<ByteStream> get_anno() const; + /** Returns a pointer to a ByteStream containing all the hidden text. + Returns NULL if no chunks have been collected yet. */ + GP<ByteStream> get_text() const; + /** Returns a pointer to a ByteStream containing all the metadata. + Returns NULL if no chunks have been collected yet. */ + GP<ByteStream> get_meta() const; + //@} + + // NEW STYLE DECODING + /** @name New style decoding. */ + //@{ + /** The decoder is now started when the image is created + by function \Ref{DjVuDocument::get_page} in \Ref{DjVuDocument}. + This function waits until the decoding thread terminates + and returns TRUE if the image has been successfully decoded. */ + bool wait_for_complete_decode(void); + //@} + + // OLD STYLE DECODING + /** @name Old style decoding (backward compatibility). */ + //@{ + /** This function is here for backward compatibility. Now, with the + introduction of multipage DjVu documents, the decoding is handled + by \Ref{DjVuFile} and \Ref{DjVuDocument} classes. For single page + documents though, we still have this wrapper. */ + void decode(ByteStream & str, DjVuInterface *notifier=0); + //@} + + // UTILITIES + /** @name Utilities */ + //@{ + /** Returns the width of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available. This gives rotated width if there is any + rotation of image. If you need real width, use #get_real_width()#.*/ + int get_width() const; + /** Returns the height of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available. This gives rotated height if there is any + rotation of image. If you need real width, use #get_real_height()#.*/ + int get_height() const; + /** Returns the width of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available.*/ + int get_real_width() const; + /** Returns the height of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available.*/ + int get_real_height() const; + /** Returns the format version the DjVu data. This function just extracts + this information from the DjVu information component. It returns zero if + such a component is not yet available. This version number should + be compared with the \Ref{DjVu version constants}. */ + int get_version() const; + /** Returns the resolution of the DjVu image. This information is given in + pixels per 2.54 cm. Display programs can use this information to + determine the natural magnification to use for rendering a DjVu + image. */ + int get_dpi() const; + /** Same as \Ref{get_dpi}() but instead of precise value returns the closest + "standard" one: 25, 50, 75, 100, 150, 300, 600. If dpi is greater than + 700, it's returned as is. */ + int get_rounded_dpi() const; + /** Returns the gamma coefficient of the display for which the image was + designed. The rendering functions can use this information in order to + perform color correction for the intended display device. */ + double get_gamma() const; + /** Returns a MIME type string describing the DjVu data. This information + is auto-sensed by the decoder. The MIME type can be #"image/djvu"# or + #"image/iw44"# depending on the data stream. */ + GUTF8String get_mimetype() const; + /** Returns a short string describing the DjVu image. + Example: #"2500x3223 in 23.1 Kb"#. */ + GUTF8String get_short_description() const; + /** Returns a verbose description of the DjVu image. This description lists + all the chunks with their size and a brief comment, as shown in the + following example. + \begin{verbatim} + DJVU Image (2325x3156) version 17: + 0.0 Kb 'INFO' Page information. + 17.3 Kb 'Sjbz' JB2 foreground mask (2325x3156) + 2.5 Kb 'BG44' IW44 background (775x1052) + 1.0 Kb 'FG44' IW44 foreground colors (194x263) + 3.0 Kb 'BG44' IW44 background (part 2). + 0.9 Kb 'BG44' IW44 background (part 3). + 7.1 Kb 'BG44' IW44 background (part 4). + Compression ratio: 676 (31.8 Kb) + \end{verbatim} */ + GUTF8String get_long_description() const; + /** Returns pointer to \Ref{DjVuFile} which contains this image in + compressed form. */ + GP<DjVuFile> get_djvu_file(void) const; + /// Write out a DjVuXML object tag and map tag. + void writeXML(ByteStream &str_out,const GURL &doc_url, const int flags=0) const; + /// Write out a DjVuXML object tag and map tag. + void writeXML(ByteStream &str_out) const; + /// Get a DjVuXML object tag and map tag. + GUTF8String get_XML(const GURL &doc_url, const int flags=0) const; + /// Get a DjVuXML object tag and map tag. + GUTF8String get_XML(void) const; + //@} + + // CHECKING + /** @name Checking for legal DjVu files. */ + //@{ + /** This function returns true if this object contains a well formed {\em + Photo DjVu Image}. Calling function #get_pixmap# on a well formed photo + image should always return a non zero value. Note that function + #get_pixmap# works as soon as sufficient information is present, + regardless of the fact that the image follows the rules or not. */ + int is_legal_photo() const; + /** This function returns true if this object contains a well formed {\em + Bilevel DjVu Image}. Calling function #get_bitmap# on a well formed + bilevel image should always return a non zero value. Note that function + #get_bitmap# works as soon as a foreground mask component is present, + regardless of the fact that the image follows the rules or not. */ + int is_legal_bilevel() const; + /** This function returns true if this object contains a well formed {\em + Compound DjVu Image}. Calling function #get_bitmap# or #get_pixmap# on + a well formed compound DjVu image should always return a non zero value. + Note that functions #get_bitmap# or #get_pixmap# works as soon as + sufficient information is present, regardless of the fact that the image + follows the rules or not. */ + int is_legal_compound() const; + //@} + + // RENDERING + /** @name Rendering. + All these functions take two rectangles as argument. Conceptually, + these function first render the whole image into a rectangular area + defined by rectangle #all#. The relation between this rectangle and the + image size define the appropriate scaling. The rendering function then + extract the subrectangle #rect# and return the corresponding pixels as a + #GPixmap# or #GBitmap# object. The #all# and #rect# should take the any + rotation in to effect, The actual implementation performs these + two operation simultaneously for obvious efficiency reasons. The best + rendering speed is achieved by making sure that the size of rectangle + #all# and the size of the DjVu image are related by an integer ratio. */ + //@{ + /** Renders the image and returns a color pixel image. Rectangles #rect# + and #all# are used as explained above. Color correction is performed + according to argument #gamma#, which represents the gamma coefficient of + the display device on which the pixmap will be rendered. The default + value, zero, means that no color correction should be performed. + This function returns a null pointer if there is not enough information + in the DjVu image to properly render the desired image. */ + GP<GPixmap> get_pixmap(const GRect &rect, const GRect &all, double gamma=0) const; + /** Renders the mask of the foreground layer of the DjVu image. This + functions is a wrapper for \Ref{JB2Image::get_bitmap}. Argument #align# + specified the alignment of the rows of the returned images. Setting + #align# to #4#, for instance, will adjust the bitmap border in order to + make sure that each row of the returned image starts on a word (four + byte) boundary. This function returns a null pointer if there is not + enough information in the DjVu image to properly render the desired + image. */ + GP<GBitmap> get_bitmap(const GRect &rect, const GRect &all, int align = 1) const; + /** Renders the background layer of the DjVu image. Rectangles #rect# and + #all# are used as explained above. Color correction is performed + according to argument #gamma#, which represents the gamma coefficient of + the display device on which the pixmap will be rendered. The default + value, zero, means that no color correction should be performed. This + function returns a null pointer if there is not enough information in + the DjVu image to properly render the desired image. */ + GP<GPixmap> get_bg_pixmap(const GRect &rect, const GRect &all, double gamma=0) const; + /** Renders the foreground layer of the DjVu image. Rectangles #rect# and + #all# are used as explained above. Color correction is performed + according to argument #gamma#, which represents the gamma coefficient of + the display device on which the pixmap will be rendered. The default + value, zero, means that no color correction should be performed. This + function returns a null pointer if there is not enough information in + the DjVu image to properly render the desired image. */ + GP<GPixmap> get_fg_pixmap(const GRect &rect, const GRect &all, double gamma=0) const; + + + /** set the rotation count(angle) in counter clock wise for the image + values (0,1,2,3) correspond to (0,90,180,270) degree rotation*/ + void set_rotate(int count=0); + /** returns the rotation count*/ + int get_rotate() const; + /** returns decoded annotations in DjVuAnno object in which all hyperlinks + and hilighted areas are rotated as per rotation setting*/ + GP<DjVuAnno> get_decoded_anno(); + /** maps the given #rect# from rotated co-ordinates to unrotated document + co-ordinates*/ + void map(GRect &rect) const; + /** unmaps the given #rect# from unrotated document co-ordinates to rotated + co-ordinates*/ + void unmap(GRect &rect) const; + /** maps the given #x#, #y# from rotated co-ordinates to unrotated document + co-ordinates*/ + void map(int &x, int &y) const; + /** unmaps the given #x#, #y# from unrotated document co-ordinates to rotated + co-ordinates*/ + void unmap(int &x, int &y) const; + + + + //@} + + // Inherited from DjVuPort. + virtual void notify_chunk_done(const DjVuPort *, const GUTF8String &name); + + // SUPERSEDED + GP<GPixmap> get_pixmap(const GRect &rect, int subs=1, double gamma=0) const; + GP<GBitmap> get_bitmap(const GRect &rect, int subs=1, int align = 1) const; + GP<GPixmap> get_bg_pixmap(const GRect &rect, int subs=1, double gamma=0) const; + GP<GPixmap> get_fg_pixmap(const GRect &rect, int subs=1, double gamma=0) const; +private: + GP<DjVuFile> file; + int rotate_count; + bool relayout_sent; + + // HELPERS + int stencil(GPixmap *pm, const GRect &rect, int subs, double gcorr) const; + GP<DjVuInfo> get_info(const GP<DjVuFile> & file) const; + GP<IW44Image> get_bg44(const GP<DjVuFile> & file) const; + GP<GPixmap> get_bgpm(const GP<DjVuFile> & file) const; + GP<JB2Image> get_fgjb(const GP<DjVuFile> & file) const; + GP<GPixmap> get_fgpm(const GP<DjVuFile> & file) const; + GP<DjVuPalette> get_fgbc(const GP<DjVuFile> & file) const; + void init_rotate(const DjVuInfo &info); +}; + + +inline GP<DjVuFile> +DjVuImage::get_djvu_file(void) const +{ + return file; +} + +//@} + + + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuInfo.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.cpp new file mode 100644 index 00000000..13ae6480 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.cpp @@ -0,0 +1,205 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuInfo.cpp,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuInfo.h" +#include "GException.h" +#include "ByteStream.h" +#include "GString.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +// ---------------------------------------- +// CLASS DJVUINFO + + +#define STRINGIFY(x) STRINGIFY_(x) +#define STRINGIFY_(x) #x + + +DjVuInfo::DjVuInfo() + : width(0), height(0), +#ifdef DJVUVERSION_FOR_OUTPUT + version(DJVUVERSION_FOR_OUTPUT), +#else + version(DJVUVERSION), +#endif + dpi(300), gamma(2.2), compressable(false), orientation(GRect::BULRNR) +{ +} + +void +DjVuInfo::decode(ByteStream &bs) +{ + // Set to default values + width = 0; + height = 0; + version = DJVUVERSION; + dpi = 300; + gamma = 2.2; + compressable=false; + orientation=GRect::BULRNR; + // Read data + unsigned char buffer[10]; + int size = bs.readall((void*)buffer, sizeof(buffer)); + if (size == 0) + G_THROW( ByteStream::EndOfFile ); + if (size < 5) + G_THROW( ERR_MSG("DjVuInfo.corrupt_file") ); + // Analyze data with backward compatibility in mind! + if (size>=2) + width = (buffer[0]<<8) + buffer[1]; + if (size>=4) + height = (buffer[2]<<8) + buffer[3]; + if (size>=5) + version = buffer[4]; + if (size>=6 && buffer[5]!=0xff) + version = (buffer[5]<<8) + buffer[4]; + if (size>=8 && buffer[7]!=0xff) + dpi = (buffer[7]<<8) + buffer[6]; + if (size>=9) + gamma = 0.1 * buffer[8]; + int flags=0; + if (size>=10) + flags = buffer[9]; + // Fixup + if (gamma<0.3) + gamma=0.3; + if (gamma>5.0) + gamma=5.0; + if (dpi < 25 || dpi > 6000) + dpi = 300; + if(flags&COMPRESSABLE_FLAG) + compressable=true; + if(version>=DJVUVERSION_ORIENTATION) + { + orientation=(GRect::Orientations)(flags&((int)GRect::BOTTOM_UP|(int)GRect::MIRROR|(int)GRect::ROTATE90_CW)); + } +} + +void +DjVuInfo::encode(ByteStream &bs) +{ + bs.write16(width); + bs.write16(height); + bs.write8(version & 0xff); + bs.write8(version >> 8); + bs.write8(dpi & 0xff); + bs.write8(dpi >> 8); + bs.write8((int)(10.0*gamma+0.5) ); + unsigned char flags=orientation; + if(compressable) + { + flags|=COMPRESSABLE_FLAG; + } + bs.write8(flags); +} + +unsigned int +DjVuInfo::get_memory_usage() const +{ + return sizeof(DjVuInfo); +} + +GUTF8String +DjVuInfo::get_paramtags(void) const +{ + const int angle=GRect::findangle(orientation); + GUTF8String retval; + if(angle) + { + retval+="<PARAM name=\"ROTATE\" value=\""+GUTF8String(angle)+"\" />\n"; + } + if(orientation == GRect::rotate(angle,GRect::TDLRNR)) + { + retval+="<PARAM name=\"VFLIP\" value=\"true\" />\n"; + } + if(dpi) + { + retval+="<PARAM name=\"DPI\" value=\""+GUTF8String(dpi)+"\" />\n"; + } + if(gamma) + { + retval+="<PARAM name=\"GAMMA\" value=\""+GUTF8String(gamma)+"\" />\n"; + } + return retval; +} + +void +DjVuInfo::writeParam(ByteStream &str_out) const +{ + str_out.writestring(get_paramtags()); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuInfo.h b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.h new file mode 100644 index 00000000..67a83be9 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.h @@ -0,0 +1,193 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuInfo.h,v 1.12 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUINFO_H +#define _DJVUINFO_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVuInfo.h + Each instance of class #DjVuInfo# represents the information + contained in the information chunk of a DjVu file. This #"INFO"# + chunk is always the first chunk of a DjVu file. + @memo + DjVu information chunk. + @author + L\'eon Bottou <leonb@research.att.com> + @version + #$Id: DjVuInfo.h,v 1.12 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + + +#include "GSmartPointer.h" +#include "GRect.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVu version constants + @memo DjVu file format version. */ +//@{ +/** Current DjVu format version. The value of this macro represents the + version of the DjVu file format implemented by this release of the DjVu + Reference Library. */ +#define DJVUVERSION 25 +/** DjVu format version. This is the value used in files produced + with DjVuLibre. This is smaller than DJVUVERSION because version + number inflation causes problems with older software. + */ +#define DJVUVERSION_FOR_OUTPUT 24 +/** This is the version which introduced orientations. */ +#define DJVUVERSION_ORIENTATION 22 +/** Oldest DjVu format version supported by this library. This release of the + library cannot completely decode DjVu files whose version field is less + than or equal to this number. */ +#define DJVUVERSION_TOO_OLD 15 +/** Newest DjVu format partially supported by this library. This release of + the library will attempt to decode files whose version field is smaller + than this macro. If the version field is greater than or equal to this + number, the decoder will just throw a \Ref{GException}. */ +#define DJVUVERSION_TOO_NEW 50 +//@} + + +class GUTF8String; + +/** Information component. + Each instance of class #DjVuInfo# represents the information + contained in the information chunk of a DjVu file. This #"INFO"# + chunk is always the first chunk of a DjVu file. + */ + +class DjVuInfo : public GPEnabled +{ +protected: + DjVuInfo(void); +public: + /** Creates an empty DjVuInfo object. + The #width# and #height# fields are set to zero. + All other fields are initialized with suitable default values. */ + static GP<DjVuInfo> create(void) {return new DjVuInfo();} + + /** Decodes the DjVu #"INFO"# chunk. This function reads binary data from + ByteStream #bs# and populates the fields of this DjVuInfo object. It is + normally called after detecting an #"INFO"# chunk header with function + \Ref{IFFByteStream::get_chunk}. */ + void decode(ByteStream &bs); + /** Encodes the DjVu #"INFO"# chunk. This function writes the fields of this + DjVuInfo object into ByteStream #bs#. It is normally called after + creating an #"INFO"# chunk header with function + \Ref{IFFByteStream::put_chunk}. */ + void encode(ByteStream &bs); + /** Returns the number of bytes used by this object. */ + unsigned int get_memory_usage() const; + /** Width of the DjVu image (in pixels). */ + int width; + /** Height of the DjVu image (in pixels). */ + int height; + /** DjVu file version number. This number characterizes the file format + version used by the encoder to generate this DjVu image. A decoder + should compare this version number with the constants described in + section "\Ref{DjVu version constants}". */ + int version; + /** Resolution of the DjVu image. The resolution is given in ``pixels per + 2.54 centimeters'' (this unit is sometimes called ``pixels per + inch''). Display programs can use this information to determine the + natural magnification to use for rendering a DjVu image. */ + int dpi; + /** Gamma coefficient of the display for which the image was designed. The + rendering functions can use this information in order to perform color + correction for the intended display device. */ + double gamma; + /** The following boolian values are stored in the last character of the + info structure. Unused bits are reserved for possible future extensions + and backwards compatability. */ + bool compressable; + enum {COMPRESSABLE_FLAG=0x80,RESERVED_FLAGS1=0x7f}; + + /** We also store the current image orientation as three bits. */ + GRect::Orientations orientation; + + /// Obtain the flags for the default specifications. + GUTF8String get_paramtags(void) const; + /// Obtain the flags for the default specifications. + void writeParam(ByteStream &out_str) const; +}; + + +//@} + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessage.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.cpp new file mode 100644 index 00000000..e92b7570 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.cpp @@ -0,0 +1,647 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessage.cpp,v 1.16 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// All these XML messages are Lizardtech innovations. + +#include "DjVuMessage.h" +#include "GOS.h" +#include "XMLTags.h" +#include "ByteStream.h" +#include "GURL.h" +#include "debug.h" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#ifdef WIN32 +# include <tchar.h> +# include <atlbase.h> +# include <windows.h> +# include <winreg.h> +#endif +#ifdef UNIX +# include <unistd.h> +# include <pwd.h> +# include <sys/types.h> +#endif +#include <locale.h> +#ifndef LC_MESSAGES +# define LC_MESSAGES LC_ALL +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +GUTF8String & +DjVuMessage::programname(void) +{ + static GUTF8String xprogramname; + use_language(); + return xprogramname; +} + +static const char namestring[]="name"; +static const char srcstring[]="src"; + +static const char *failed_to_parse_XML=ERR_MSG("DjVuMessage.failed_to_parse_XML"); +static const char bodystring[]="BODY"; +static const char languagestring[]="LANGUAGE"; +static const char headstring[]="HEAD"; +static const char includestring[]="INCLUDE"; +static const char messagestring[]="MESSAGE"; +static const char localestring[]="locale"; + + +// directory names for searching messages +static const char opensourcedir[]="osi"; +#ifdef AUTOCONF +static const char DjVuDataDir[] = DIR_DATADIR "/djvu"; +static const char ModuleDjVuDir[] ="share/djvu"; +#else /* !AUTOCONF */ +static const char ModuleDjVuDir[] ="profiles"; +#endif /* !AUTOCONF */ +static const char LocalDjVuDir[] =".DjVu"; // relative to ${HOME} +#ifdef LT_DEFAULT_PREFIX +static const char DjVuPrefixDir[] = LT_DEFAULT_PREFIX "/profiles"; +#endif +#ifndef NDEBUG +static const char DebugModuleDjVuDir[] ="../TOPDIR/SRCDIR/profiles"; +#endif +#ifdef WIN32 +static const char RootDjVuDir[] ="C:/Program Files/LizardTech/Profiles"; +static const TCHAR registrypath[]= TEXT("Software\\LizardTech\\DjVu\\Profile Path"); +#else +static const char RootDjVuDir[] ="/etc/DjVu/"; // global last resort +#endif + +static const char DjVuEnv[] = "DJVU_CONFIG_DIR"; + +// The name of the message file +static const char MessageFile[]="messages.xml"; +static const char LanguageFile[]="languages.xml"; + +#ifdef WIN32 +static GURL +RegOpenReadConfig ( HKEY hParentKey ) +{ + GURL retval; + // To do: This needs to be shared with SetProfile.cpp + LPCTSTR path = registrypath; + + HKEY hKey = 0; + // MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,argv[1],strlen(argv[1])+1,wszSrcFile,sizeof(wszSrcFile)); + if (RegOpenKeyEx(hParentKey, path, 0, + KEY_READ, &hKey) == ERROR_SUCCESS ) + { + TCHAR path[1024]; + // Success + TCHAR *szPathValue = path; + LPCTSTR lpszEntry = (LPCTSTR &)TEXT(""); + DWORD dwCount = (sizeof(path)/sizeof(TCHAR))-1; + DWORD dwType; + + LONG lResult = RegQueryValueEx(hKey, lpszEntry, NULL, + &dwType, (LPBYTE) szPathValue, &dwCount); + + RegCloseKey(hKey); + + if ((lResult == ERROR_SUCCESS)) + { + szPathValue[dwCount] = 0; + USES_CONVERSION; + retval=GURL::Filename::Native(T2CA(path)); + } + } +// if (hKey) RegCloseKey(hKey); + return retval; +} + +static GURL +GetModulePath( void ) +{ + const GUTF8String cwd(GOS::cwd()); + TCHAR path[1024]; + DWORD dwCount = (sizeof(path)/sizeof(TCHAR))-1; + GetModuleFileName(0, path, dwCount); + USES_CONVERSION; + GURL retval=GURL::Filename::Native(T2CA(path)).base(); + GOS::cwd(cwd); + return retval; +} +#elif defined(UNIX) + +static GList<GURL> +parsePATH(void) +{ + GList<GURL> retval; + const char *path=getenv("PATH"); + if(path) + { + GNativeString p(path); + int from=0; + for(int to;(to=p.search(':',from))>0;from=to+1) + { + if(to > from) + { + retval.append(GURL::Filename::Native(p.substr(from,to-from))); + } + } + if((from+1)<(int)p.length()) + { + retval.append(GURL::Filename::Native(p.substr(from,-1))); + } + } + return retval; +} + +static GURL +GetModulePath( void ) +{ + GURL retval; + GUTF8String &xprogramname=DjVuMessage::programname(); + if(xprogramname.length()) + { + if(xprogramname[1]=='/' + ||!xprogramname.cmp("../",3) + ||!xprogramname.cmp("./",2)) + { + retval=GURL::Filename::UTF8(xprogramname); + } + if(retval.is_empty() || !retval.is_file()) + { + GList<GURL> paths(parsePATH()); + GMap<GUTF8String,void *> pathMAP; + for(GPosition pos=paths;pos;++pos) + { + retval=GURL::UTF8(xprogramname,paths[pos]); + const GUTF8String path(retval.get_string()); + if(!pathMAP.contains(path)) + { + if(retval.is_file()) + break; + pathMAP[path]=0; + } + } + } + if (! retval.is_empty() ) + retval = retval.follow_symlinks(); + if (! retval.is_empty() ) + retval = retval.base(); + } + return retval; +} +#endif + +static void +appendPath(const GURL &url, + GMap<GUTF8String,void *> &map, + GList<GURL> &list) +{ + if( !url.is_empty() + && !map.contains(url.get_string()) && url.is_dir() ) + { + map[url.get_string()]=0; + list.append(url); + } +} + +GList<GURL> +DjVuMessage::GetProfilePaths(void) +{ + static bool first=true; + static GList<GURL> realpaths; + if(first) + { + first=false; + GMap<GUTF8String,void *> pathsmap; + GList<GURL> paths; + GURL path; + const GUTF8String envp(GOS::getenv(DjVuEnv)); + if(envp.length()) + appendPath(GURL::Filename::UTF8(envp),pathsmap,paths); +#if defined(WIN32) || defined(UNIX) + GURL mpath(GetModulePath()); + if(!mpath.is_empty() && mpath.is_dir()) + { +#if defined(UNIX) && !defined(AUTOCONF) && !defined(NDEBUG) + appendPath(GURL::UTF8(DebugModuleDjVuDir,mpath),pathsmap,paths); +#endif + appendPath(mpath,pathsmap,paths); + mpath=mpath.base(); + appendPath(GURL::UTF8(ModuleDjVuDir,mpath),pathsmap,paths); + mpath=mpath.base(); + appendPath(GURL::UTF8(ModuleDjVuDir,mpath),pathsmap,paths); + } +#endif +#if defined(AUTOCONF) + GURL dpath = GURL::Filename::UTF8(DjVuDataDir); + appendPath(dpath,pathsmap,paths); +#endif +#ifdef WIN32 + appendPath(RegOpenReadConfig(HKEY_CURRENT_USER),pathsmap,paths); + appendPath(RegOpenReadConfig(HKEY_LOCAL_MACHINE),pathsmap,paths); +#else + GUTF8String home=GOS::getenv("HOME"); +# if HAVE_GETPWUID + if (! home.length()) { + struct passwd *pw=0; + if ((pw = getpwuid(getuid()))) + home=GNativeString(pw->pw_dir); + } +# endif + if (home.length()) { + GURL hpath = GURL::UTF8(LocalDjVuDir,GURL::Filename::UTF8(home)); + appendPath(hpath,pathsmap,paths); + } +#endif +#ifdef LT_DEFAULT_PREFIX + appendPath(GURL::Filename::UTF8(DjVuPrefixDir),pathsmap,paths); +#endif + appendPath(GURL::Filename::UTF8(RootDjVuDir),pathsmap,paths); + pathsmap.empty(); + + GPosition pos; + GList< GMap<GUTF8String,GP<lt_XMLTags> > > localemaps; + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(LanguageFile,paths[pos]); + if(path.is_file()) + { + const GP<lt_XMLTags> xml(lt_XMLTags::create(ByteStream::create(path,"rb"))); + const GPList<lt_XMLTags> Body(xml->get_Tags(bodystring)); + GPosition pos=Body; + if(!pos || (pos != Body.lastpos())) + { + G_THROW( ERR_MSG("XMLAnno.extra_body") ); + } + const GP<lt_XMLTags> GBody(Body[pos]); + if(!GBody) + { + G_THROW( ERR_MSG("XMLAnno.no_body") ); + } + GMap<GUTF8String,GP<lt_XMLTags> > localemap; + lt_XMLTags::get_Maps(languagestring,localestring,Body,localemap); + localemaps.append(localemap); + } + } + GList<GURL> localepaths; + GList<GURL> osilocalepaths; + + // Need to do it the right way! + GUTF8String defaultlocale = getenv("LANGUAGE"); + if (! defaultlocale) + { + const GUTF8String oldlocale(setlocale(LC_MESSAGES,0)); + defaultlocale = setlocale(LC_MESSAGES,""); + setlocale(LC_MESSAGES,(const char *)oldlocale); + } + // Unfathomable search. + for(int loop=0; loop<2; loop++) + { + static const char sepchars[]=" _.@"; + const char *p=sepchars+sizeof(sepchars)-1; + do + { + int sepcharpos=p[0]?defaultlocale.search(p[0]):defaultlocale.length(); + if(sepcharpos > 0) + { + const GUTF8String sublocale(defaultlocale,sepcharpos); + const GUTF8String downcasesublocale("downcase^"+sublocale.downcase()); + for(pos=localemaps;pos;++pos) + { + const GMap<GUTF8String,GP<lt_XMLTags> > &localemap=localemaps[pos]; + GPosition pos=localemap.contains(sublocale); + if(!pos) + pos=localemap.contains(downcasesublocale); + if(pos) + { + const GMap<GUTF8String,GUTF8String>&args + = localemap[pos]->get_args(); + pos = args.contains(srcstring); + if (pos) + { + const GUTF8String src(args[pos]); + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(src,paths[pos]); + if(path.is_dir()) + localepaths.append(path); + path=GURL::UTF8(GUTF8String(opensourcedir)+"/"+src,paths[pos]); + if(path.is_dir()) + osilocalepaths.append(path); + } + } + // We don't need to check anymore language files. + p=sepchars; + break; + } + } + if(!pos) + { + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(sublocale,paths[pos]); + if(path.is_dir()) + { + localepaths.append(path); + } + path=GURL::UTF8(GUTF8String(opensourcedir)+"/"+sublocale,paths[pos]); + if(path.is_dir()) + { + osilocalepaths.append(path); + } + } + } + } + } while(p-- != sepchars); + if((GPosition) localepaths) + break; + defaultlocale="C"; + } + for(pos=localepaths;pos;++pos) + appendPath(localepaths[pos],pathsmap,realpaths); + for(pos=paths;pos;++pos) + appendPath(paths[pos],pathsmap,realpaths); + for(pos=osilocalepaths;pos;++pos) + appendPath(osilocalepaths[pos],pathsmap,realpaths); + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(opensourcedir,paths[pos]); + appendPath(path,pathsmap,realpaths); + } + } + return realpaths; +} + +static GUTF8String +getbodies( + GList<GURL> &paths, + const GUTF8String &MessageFileName, + GPList<lt_XMLTags> &body, + GMap<GUTF8String, void *> & map ) +{ + GUTF8String errors; + bool isdone=false; + GPosition firstpathpos=paths; + for(GPosition pathpos=firstpathpos;!isdone && pathpos;++pathpos) + { + const GURL::UTF8 url(MessageFileName,paths[pathpos]); + if(url.is_file()) + { + map[MessageFileName]=0; + GP<lt_XMLTags> gtags; + { + GP<ByteStream> bs=ByteStream::create(url,"rb"); + G_TRY + { + gtags=lt_XMLTags::create(bs); + } + G_CATCH(ex) + { + GUTF8String mesg(failed_to_parse_XML+("\t"+url.get_string())); + if(errors.length()) + { + errors+="\n"+mesg; + }else + { + errors=mesg; + } + errors+="\n"+GUTF8String(ex.get_cause()); + } + G_ENDCATCH; + } + if(gtags) + { + lt_XMLTags &tags=*gtags; + GPList<lt_XMLTags> Bodies=tags.get_Tags(bodystring); + if(! Bodies.isempty()) + { + isdone=true; + for(GPosition pos=Bodies;pos;++pos) + { + body.append(Bodies[pos]); + } + } + GPList<lt_XMLTags> Head=tags.get_Tags(headstring); + if(! Head.isempty()) + { + isdone=true; + GMap<GUTF8String, GP<lt_XMLTags> > includes; + lt_XMLTags::get_Maps(includestring,namestring,Head,includes); + for(GPosition pos=includes;pos;++pos) + { + const GUTF8String file=includes.key(pos); + if(! map.contains(file)) + { + GList<GURL> xpaths; + xpaths.append(url.base()); + const GUTF8String err2(getbodies(xpaths,file,body,map)); + if(err2.length()) + { + if(errors.length()) + { + errors+="\n"+err2; + }else + { + errors=err2; + } + } + } + } + } + } + } + } + return errors; +} + +static GUTF8String +parse(GMap<GUTF8String,GP<lt_XMLTags> > &retval) +{ + GUTF8String errors; + GPList<lt_XMLTags> body; + { + GList<GURL> paths=DjVuMessage::GetProfilePaths(); + GMap<GUTF8String, void *> map; + GUTF8String m(MessageFile); + errors=getbodies(paths,m,body,map); + } + if(! body.isempty()) + { + lt_XMLTags::get_Maps(messagestring,namestring,body,retval); + } + return errors; +} + + +const DjVuMessageLite & +DjVuMessage::create_full(void) +{ + GP<DjVuMessageLite> &static_message=getDjVuMessageLite(); + if(!static_message) + { + DjVuMessage *mesg=new DjVuMessage; + static_message=mesg; + mesg->init(); + } + return DjVuMessageLite::create_lite(); +} + +void +DjVuMessage::set_programname(const GUTF8String &xprogramname) +{ + programname()=xprogramname; + DjVuMessageLite::create=create_full; +} + +void +DjVuMessage::use_language(void) +{ + DjVuMessageLite::create=create_full; +} + + +// Constructor +DjVuMessage::DjVuMessage( void ) {} + +void +DjVuMessage::init(void) +{ + errors=parse(Map); +} + +// Destructor +DjVuMessage::~DjVuMessage( ) +{ +} + + +// A C function to perform a message lookup. Arguments are a buffer to receiv +// translated message, a buffer size (bytes), and a message_list. The transla +// result is returned in msg_buffer encoded in Native MBS encoding. In case +// of error, msg_b empty (i.e., msg_buffer[0] == '\0'). +void +DjVuMessageLookUpNative( + char *msg_buffer, const unsigned int buffer_size, const char *message) +{ + const GNativeString converted(DjVuMessage::LookUpNative( message )); + if( converted.length() >= buffer_size ) + msg_buffer[0] = '\0'; + else + strcpy( msg_buffer, converted ); +} + +// A C function to perform a message lookup. Arguments are a buffer to receiv +// translated message, a buffer size (bytes), and a message_list. The transla +// result is returned in msg_buffer encoded in UTF8 encoding. In case +// of error, msg_b empty (i.e., msg_buffer[0] == '\0'). +void +DjVuMessageLookUpUTF8( + char *msg_buffer, const unsigned int buffer_size, const char *message) +{ + const GUTF8String converted(DjVuMessage::LookUpUTF8( message )); + if( converted.length() >= buffer_size ) + msg_buffer[0] = '\0'; + else + strcpy( msg_buffer, converted ); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +void +DjVuFormatErrorUTF8( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + DjVuWriteError( message ); +} + +void +DjVuFormatErrorNative( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GNativeString message(fmt,args); + DjVuWriteError( message ); +} + +const char * +djvu_programname(const char *xprogramname) +{ + if(xprogramname) + DjVuMessage::programname()=GNativeString(xprogramname); + return DjVuMessage::programname(); +} diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessage.h b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.h new file mode 100644 index 00000000..2302be37 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.h @@ -0,0 +1,135 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessage.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + + +#ifndef __DJVU_MESSAGE_H__ +#define __DJVU_MESSAGE_H__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// All these I18N XML messages are Lizardtech innovations. +// For DjvuLibre, I changed the path extraction logic +// and added support for non I18N messages. + + +#include "DjVuMessageLite.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GURL; + +class DjVuMessage : public DjVuMessageLite +{ +protected: + void init(void); + DjVuMessage(void); + +public: + /// Use the locale info and find the XML files on disk. + static void use_language(void); + + /// Set the program name used when searching for XML files on disk. + static void set_programname(const GUTF8String &programname); + static GUTF8String &programname(void); + + /// creates this class specifically. + static const DjVuMessageLite &create_full(void); + + /** Adds a byte stream to be parsed whenever the next DjVuMessage::create() + call is made. */ + static void AddByteStreamLater(const GP<ByteStream> &bs) + { use_language(); DjVuMessageLite::AddByteStreamLater(bs); } + + /** Destructor: Does any necessary cleanup. Actions depend on how the message + file is implemented. */ + ~DjVuMessage(); + + //// Same as LookUp, but this is a static method. + static GUTF8String LookUpUTF8( const GUTF8String & MessageList ) + { use_language();return DjVuMessageLite::LookUpUTF8(MessageList); } + + /** Same as Lookup, but returns a multibyte character string in the + current locale. */ + static GNativeString LookUpNative( const GUTF8String & MessageList ) + { use_language();return DjVuMessageLite::LookUpNative(MessageList); } + + /// This is a simple alias to the above class, but does an fprintf to stderr. + static void perror( const GUTF8String & MessageList ) + { use_language();DjVuMessageLite::perror(MessageList); } + + static GList<GURL> GetProfilePaths(void); +}; + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* __DJVU_MESSAGE_H__ */ + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.cpp new file mode 100644 index 00000000..258b0649 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.cpp @@ -0,0 +1,478 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessageLite.cpp,v 1.13 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// All these I18N XML messages are Lizardtech innovations. +// For DjvuLibre, I changed the path extraction logic +// and added support for non I18N messages. + +#include "DjVuMessageLite.h" +#include "GOS.h" +#include "XMLTags.h" +#include "ByteStream.h" +#include "GURL.h" +#include "debug.h" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +// #include <stdio.h> +#ifdef WIN32 +#include <tchar.h> +#include <atlbase.h> +#include <windows.h> +#include <winreg.h> +#endif +#ifdef UNIX +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#endif +#include <locale.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +const DjVuMessageLite& (*DjVuMessageLite::create)(void) = + DjVuMessageLite::create_lite; + +static const char *failed_to_parse_XML= + ERR_MSG("DjVuMessage.failed_to_parse_XML"); +static const char *unrecognized= + ERR_MSG("DjVuMessage.Unrecognized"); +static const char *uparameter= + ERR_MSG("DjVuMessage.Parameter"); +#ifdef LIZARDTECH_1_800_NUMBER +static const char unrecognized_default[] = + "** Unrecognized DjVu Message: [Contact LizardTech at " + LIZARDTECH_1_800_NUMBER " \n" + "\t** Message name: %1!s!"; +#else +static const char unrecognized_default[] = + "** Unrecognized DjVu Message:\n" + "\t** Message name: %1!s!"; +#endif +static const char uparameter_default[] = + "\t Parameter: %1!s!"; +static const char failed_to_parse_XML_default[]= + "Failed to parse XML message file: 	'%1!s!'."; + +static const char namestring[]="name"; +static const char valuestring[]="value"; +static const char numberstring[]="number"; +static const char bodystring[]="BODY"; +static const char messagestring[]="MESSAGE"; + +GPList<ByteStream> & +DjVuMessageLite::getByteStream(void) +{ + static GPList<ByteStream> gbs; + return gbs; +} + +GP<DjVuMessageLite> & +DjVuMessageLite::getDjVuMessageLite(void) +{ + static GP<DjVuMessageLite> message; + return message; +} + +void +DjVuMessageLite::AddByteStreamLater(const GP<ByteStream> &bs) +{ + getByteStream().append(bs); +} + +// There is only object of class DjVuMessage in a program, and here it is: +//DjVuMessage DjVuMsg; +const DjVuMessageLite & +DjVuMessageLite::create_lite(void) +{ + GP<DjVuMessageLite> &static_message=getDjVuMessageLite(); + if(!static_message) + { + static_message=new DjVuMessageLite; + } + DjVuMessageLite &m=*static_message; + GPList<ByteStream> &bs = getByteStream(); + for(GPosition pos;(pos=bs);bs.del(pos)) + { + m.AddByteStream(bs[pos]); + } + return m; +} + +// Constructor +DjVuMessageLite::DjVuMessageLite( void ) {} + +// Destructor +DjVuMessageLite::~DjVuMessageLite( ) {} + + +void +DjVuMessageLite::perror( const GUTF8String & MessageList ) +{ + DjVuPrintErrorUTF8("%s\n",(const char *)DjVuMessageLite::LookUpUTF8(MessageList)); +} + + +// Expands message lists by looking up the message IDs and inserting +// arguments into the retrieved messages. +// N.B. The resulting string may be encoded in UTF-8 format (ISO 10646-1 Annex R) +// and SHOULD NOT BE ASSUMED TO BE ASCII. +GUTF8String +DjVuMessageLite::LookUp( const GUTF8String & MessageList ) const +{ +// DEBUG_MSG( "========== DjVuMessageLite::LookUp ==========\n" << +// MessageList << +// "\n========== DjVuMessageLite::LookUp ==========\n" ); + GUTF8String result; // Result string; begins empty + if(errors.length()) + { + const GUTF8String err1(errors); + (const_cast<GUTF8String &>(errors)).empty(); + result=LookUp(err1)+"\n"; + } + + int start = 0; // Beginning of next message + int end = MessageList.length(); // End of the message string + + // Isolate single messages and process them + while( start < end ) + { + if( MessageList[start] == '\n' ) + { + result += MessageList[start++]; // move the newline to the result + // and advance to the next message + } + else + { + // Find the end of the next message and process it + int next_ending = MessageList.search((unsigned long)'\n', start); + if( next_ending < 0 ) + next_ending = end; + result += LookUpSingle( MessageList.substr(start, next_ending-start) ); + // Advance to the next message + start = next_ending; + } + } + + // All done + return result; +} + + +// Expands a single message and inserts the arguments. Single_Message +// contains no separators (newlines), but includes all the parameters +// separated by tabs. +GUTF8String +DjVuMessageLite::LookUpSingle( const GUTF8String &Single_Message ) const +{ +#if HAS_CTRL_C_IN_ERR_MSG + if (Single_Message[0] != '\003') + return Single_Message; +#endif + // Isolate the message ID and get the corresponding message text + int ending_posn = Single_Message.contains("\t\v"); + if( ending_posn < 0 ) + ending_posn = Single_Message.length(); + GUTF8String msg_text; + GUTF8String msg_number; + const GUTF8String message=Single_Message.substr(0,ending_posn); + LookUpID( message, msg_text, msg_number ); + + // Check whether we found anything + if( !msg_text.length()) + { + if(message == unrecognized) + { + msg_text = unrecognized_default; + }else if(message == uparameter) + { + msg_text = uparameter_default; + }else if(message == failed_to_parse_XML) + { + msg_text = failed_to_parse_XML_default; + }else + { + return LookUpSingle(unrecognized + ("\t" + Single_Message)); + } + } + + // Insert the parameters (if any) + unsigned int param_num = 0; + while( (unsigned int)ending_posn < Single_Message.length() ) + { + GUTF8String arg; + const int start_posn = ending_posn+1; + if(Single_Message[ending_posn] == '\v') + { + ending_posn=Single_Message.length(); + arg=LookUpSingle(Single_Message.substr(start_posn,ending_posn)); + }else + { + ending_posn = Single_Message.contains("\v\t",start_posn); + if( ending_posn < 0 ) + ending_posn = Single_Message.length(); + arg=Single_Message.substr(start_posn, ending_posn-start_posn); + } + InsertArg( msg_text, ++param_num, arg); + } + // Insert the message number + InsertArg( msg_text, 0, msg_number ); + + return msg_text; +} + + +// Looks up the msgID in the file of messages and returns a pointer to +// the beginning of the translated message, if found; and an empty string +// otherwise. +void +DjVuMessageLite::LookUpID( const GUTF8String &xmsgID, + GUTF8String &message_text, + GUTF8String &message_number ) const +{ + if(!Map.isempty()) + { + GUTF8String msgID = xmsgID; +#if HAS_CTRL_C_IN_ERR_MSG + int start = 0; + while (msgID[start] == '\003') + start ++; + if (start > 0) + msgID = msgID.substr(start, msgID.length() - start); +#endif + GPosition pos=Map.contains(msgID); + if(pos) + { + const GP<lt_XMLTags> tag=Map[pos]; + GPosition valuepos=tag->get_args().contains(valuestring); + if(valuepos) + { + message_text=tag->get_args()[valuepos]; + }else + { + const GUTF8String raw(tag->get_raw()); + const int start_line=raw.search((unsigned long)'\n',0); + + const int start_text=raw.nextNonSpace(0); + const int end_text=raw.firstEndSpace(0); + if(start_line<0 || start_text<0 || start_text < start_line) + { + message_text=raw.substr(0,end_text).fromEscaped(); + }else + { + message_text=raw.substr(start_line+1,end_text-start_line-1).fromEscaped(); + } + } + GPosition numberpos=tag->get_args().contains(numberstring); + if(numberpos) + { + message_number=tag->get_args()[numberpos]; + } + } + } +} + + +// Insert a string into the message text. Will insert into any field +// description. Except for an ArgId of zero (message number), if the ArgId +// is not found, the routine adds a line with the parameter so information +// will not be lost. +void +DjVuMessageLite::InsertArg( GUTF8String &message, + const int ArgId, const GUTF8String &arg ) const +{ + // argument target string + const GUTF8String target= "%"+GUTF8String(ArgId)+"!"; + // location of target string + int format_start = message.search( (const char *)target ); + if( format_start >= 0 ) + { + do + { + const int n=format_start+target.length()+1; + const int format_end=message.search((unsigned long)'!',n); + if(format_end > format_start) + { + const int len=1+format_end-n; + if(len && isascii(message[n-1])) + { + GUTF8String narg; + GUTF8String format="%"+message.substr(n-1,len); + switch(format[len]) + { + case 'd': + case 'i': + narg.format((const char *)format,arg.toInt()); + break; + case 'u': + case 'o': + case 'x': + case 'X': + narg.format((const char *)format,(unsigned int)arg.toInt()); + break; + case 'f': + { + int endpos; + narg.format((const char *)format, arg.toDouble(0,endpos)); + if( endpos < 0 ) + narg = arg; + } + break; + default: + narg.format((const char *)format,(const char *)arg); + break; + } + message = message.substr( 0, format_start )+narg + +message.substr( format_end+1, -1 ); + }else + { + message = message.substr( 0, format_start )+arg + +message.substr( format_end+1, -1 ); + } + } + format_start=message.search((const char*)target, format_start+arg.length()); + } while(format_start >= 0); + } + else + { + // Not found, fake it + if( ArgId != 0 ) + { + message += "\n"+LookUpSingle(uparameter+("\t"+arg)); + } + } +} + + +// A C function to perform a message lookup. Arguments are a buffer to received the +// translated message, a buffer size (bytes), and a message_list. The translated +// result is returned in msg_buffer encoded in UTF-8. In case of error, msg_buffer is +// empty (i.e., msg_buffer[0] == '\0'). +void +DjVuMessageLite_LookUp( char *msg_buffer, const unsigned int buffer_size, const char *message ) +{ + GUTF8String converted = DjVuMessageLite::LookUpUTF8( message ); + if( converted.length() >= buffer_size ) + msg_buffer[0] = '\0'; + else + strcpy( msg_buffer, converted ); +} + +void +DjVuMessageLite::AddByteStream(const GP<ByteStream> &bs) +{ + const GP<lt_XMLTags> gtags(lt_XMLTags::create(bs)); + lt_XMLTags &tags=*gtags; + GPList<lt_XMLTags> Bodies=tags.get_Tags(bodystring); + if(! Bodies.isempty()) + { + lt_XMLTags::get_Maps(messagestring,namestring,Bodies,Map); + } +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +void +DjVuWriteError( const char *message ) +{ + G_TRY { + GP<ByteStream> errout = ByteStream::get_stderr(); + if (errout) + { + const GUTF8String external = DjVuMessageLite::LookUpUTF8( message ); + errout->writestring(external+"\n"); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuWriteMessage( const char *message ) +{ + G_TRY { + GP<ByteStream> strout = ByteStream::get_stdout(); + if (strout) + { + const GUTF8String external = DjVuMessageLite::LookUpUTF8( message ); + strout->writestring(external+"\n"); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.h b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.h new file mode 100644 index 00000000..f2e941cf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.h @@ -0,0 +1,227 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessageLite.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef __DJVU_MESSAGE_LITE_H__ +#define __DJVU_MESSAGE_LITE_H__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// All these I18N XML messages are Lizardtech innovations. +// For DjvuLibre, I changed the path extraction logic +// and added support for non I18N messages. + + +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class lt_XMLTags; +class ByteStream; + +/** Exception causes and external messages are passed as message lists which + have the following syntax: + + message_list ::= single_message | + single_message separator message_list + + separator ::= newline | + newline | separator + + single_message ::= message_ID | + message_ID parameters + + parameters ::= tab string | + tab string parameters + + Message_IDs are looked up an external file and replaced by the message + text strings they are mapped to. The message text may contain the + following: + + Parameter specifications: These are modelled after printf format + specifications and have one of the following forms: + + %n!s! %n!d! %n!i! %n!u! %n!x! %n!X! + + where n is the parameter number. The parameter number is indicated + explicitly to allow for the possibility that the parameter order may + change when the message text is translated into another language. + The final letter ('s', 'd', 'i', 'u', 'x', or 'X') indicates the form + of the parameter (string, integer, integer, unsigned integer, lowercase + hexadecimal, or uppercase hexadecimal, respectively). In addition + formating options such as precision available in sprintf, may be used. + So, to print the third argument as 5 digit unsigned number, with leading + zero's one could use: + %3!05u! + + All of the arguments are actually strings. For forms that don't take + string input ('d', 'i', 'u', 'x', or 'X'), and atoi() conversion is done + on the string before formatting. In addition the form indicates to the + translater whether to expect a word or a number. + + The strings are read in from XML. To to format the strings, use the + relavent XML escape sequences, such as follows: + + [line feed] + 	 [horizontal tab] + ' [single quote] + " [double quote] + < [less than sign] + > [greater than sign] + + After parameters have been inserted in the message text, the formatting + strings are replaced by their usual equivalents (newline and tab + respectively). + + If a message_id cannot be found in the external file, a message text + is fabricated giving the message_id and the parameters (if any). + + Separators (newlines) are preserved in the translated message list. + + Expands message lists by looking up the message IDs and inserting + arguments into the retrieved messages. + + N.B. The resulting string may be encoded in UTF-8 format (ISO 10646-1 + Annex R) and SHOULD NOT BE ASSUMED TO BE ASCII. + */ + +class DjVuMessageLite : public GPEnabled +{ +protected: + // Constructor: + DjVuMessageLite( void ); + GMap<GUTF8String,GP<lt_XMLTags> > Map; + GUTF8String errors; + /// Creates a DjVuMessage class. + static const DjVuMessageLite &real_create(void); + +public: + /// Creates a DjVuMessage class. + static const DjVuMessageLite& (*create)(void); + + /// Creates this class specifically. + static const DjVuMessageLite &create_lite(void); + + /** Adds a byte stream to be parsed whenever the next DjVuMessage::create() + call is made. */ + static void AddByteStreamLater(const GP<ByteStream> &bs); + + /** Destructor: Does any necessary cleanup. Actions depend on how the message + file is implemented. */ + ~DjVuMessageLite(); + + /// Lookup the relavent string and parse the message. + GUTF8String LookUp( const GUTF8String & MessageList ) const; + + //// Same as LookUp, but this is a static method. + static GUTF8String LookUpUTF8( const GUTF8String & MessageList ) + { return create().LookUp(MessageList); } + + /** Same as Lookup, but returns the a multibyte character string in the + current locale. */ + static GNativeString LookUpNative( const GUTF8String & MessageList ) + { return create().LookUp(MessageList).getUTF82Native(); } + + /// This is a simple alias to the above class, but does an fprintf to stderr. + static void perror( const GUTF8String & MessageList ); + +protected: + + /* Looks up the msgID in the file of messages. The strings message_text + and message_number are returned if found. If not found, these strings + are empty. */ + void LookUpID( const GUTF8String & msgID, + GUTF8String &message_text, GUTF8String &message_number ) const; + + /* Expands a single message and inserts the arguments. Single_Message + contains no separators (newlines), but includes all the parameters + separated by tabs. */ + GUTF8String LookUpSingle( const GUTF8String & Single_Message ) const; + + /* Insert a string into the message text. Will insert into any field + description. Except for an ArgId of zero (message number), if the + #ArgId# is not found, the routine adds a line with the parameter + so information will not be lost. */ + void InsertArg( + GUTF8String &message, const int ArgId, const GUTF8String &arg ) const; + + void AddByteStream(const GP<ByteStream> &bs); + +protected: + /* Static storage of the DjVuMessage class. */ + static GP<DjVuMessageLite> &getDjVuMessageLite(void); + /* Static storage of the ByteStream list. */ + static GPList<ByteStream> &getByteStream(void); +}; + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* __DJVU_MESSAGE_LITE_H__ */ + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.cpp new file mode 100644 index 00000000..615041b0 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.cpp @@ -0,0 +1,237 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuNavDir.cpp,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuNavDir.h" +#include "debug.h" +#include "GException.h" +#include "GOS.h" +#include "ByteStream.h" +#include "GURL.h" +#include <ctype.h> + + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +DjVuNavDir::DjVuNavDir(const GURL &dirURL) +{ + if (!dirURL) G_THROW( ERR_MSG("DjVuNavDir.zero_dir") ); + baseURL=dirURL.base(); +} + +DjVuNavDir::DjVuNavDir(ByteStream & str, const GURL &dirURL) +{ + if (!dirURL) G_THROW( ERR_MSG("DjVuNavDir.zero_dir") ); + + baseURL=GURL(dirURL).base(); + + decode(str); +} + +void +DjVuNavDir::decode(ByteStream & str) +{ + GCriticalSectionLock lk(&lock); + + GList<GUTF8String> tmp_page2name; + int eof=0; + while(!eof) + { + char buffer[1024]; + char * ptr; + for(ptr=buffer;ptr-buffer<1024;ptr++) + if ((eof=!str.read(ptr, 1)) || *ptr=='\n') break; + if (ptr-buffer==1024) G_THROW( ERR_MSG("DjVuNavDir.long_line") ); + *ptr=0; + if (!strlen(buffer)) continue; + + if (!tmp_page2name.contains(buffer)) + tmp_page2name.append(buffer); + }; + + // Now copying lists to arrays for faster access later + int pages=tmp_page2name.size(); + page2name.resize(pages-1); + + int cnt; + GPosition pos; + for(pos=tmp_page2name, cnt=0;pos;++pos, cnt++) + page2name[cnt]=tmp_page2name[pos]; + + // Now creating reverse mapping (strings => numbers) + for(cnt=0;cnt<pages;cnt++) + { + name2page[page2name[cnt]]=cnt; + url2page[GURL::UTF8(page2name[cnt],baseURL)]=cnt; + } +} + +#ifndef NEED_DECODER_ONLY +void +DjVuNavDir::encode(ByteStream & str) +{ + GCriticalSectionLock lk(&lock); + + for(int i=0;i<page2name.size();i++) + { + GUTF8String & name=page2name[i]; + str.writall((const char*)name, name.length()); + str.writall("\n", 1); + }; +} +#endif //NEED_DECODER_ONLY + +int +DjVuNavDir::get_pages_num(void) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + return page2name.size(); +} + +int +DjVuNavDir::name_to_page(const char * name) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + if (!name2page.contains(name)) return -1; + return name2page[name]; +} + +int +DjVuNavDir::url_to_page(const GURL & url) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + if (!url2page.contains(url)) return -1; + return url2page[url]; +} + +GUTF8String +DjVuNavDir::page_to_name(int page) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + if (page<0) + G_THROW( ERR_MSG("DjVuNavDir.neg_page") ); + if (page>=page2name.size()) + G_THROW( ERR_MSG("DjVuNavDir.large_page") ); + return page2name[page]; +} + +GURL +DjVuNavDir::page_to_url(int page) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + return GURL::UTF8(page_to_name(page),baseURL); +} + +void +DjVuNavDir::insert_page(int where, const char * name) +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + int pages=page2name.size(); + if (where<0) where=pages; + + page2name.resize(pages); + for(int i=pages;i>where;i--) + page2name[i]=page2name[i-1]; + page2name[where]=name; + name2page[name]=where; + url2page[GURL::UTF8(name,baseURL)]=where; +} + +#ifndef NEED_DECODER_ONLY +void +DjVuNavDir::delete_page(int page_num) +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + int pages=page2name.size(); + + if (page_num<0 || page_num>=pages) + G_THROW( ERR_MSG("DjVuNavDir.bad_page") ); + + for(int i=page_num;i<pages-1;i++) + page2name[i]=page2name[i+1]; + page2name.resize(--pages-1); +} +#endif + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.h b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.h new file mode 100644 index 00000000..90b2b8db --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.h @@ -0,0 +1,192 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuNavDir.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUNAVDIR_H +#define _DJVUNAVDIR_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GString.h" +#include "GThreads.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVuNavDir.h + Files #"DjVuNavDir.h"# and #"DjVuNavDir.cpp"# contain implementation of the + multipage DjVu navigation directory. This directory lists all the pages, + that a given document is composed of. The navigation (switching from page + to page in the plugin) is not possible before this directory is decoded. + + Refer to the \Ref{DjVuNavDir} class description for greater details. + + @memo DjVu Navigation Directory + @author Andrei Erofeev <eaf@geocities.com> + @version #$Id: DjVuNavDir.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# +*/ + +//@{ + +//***************************************************************************** +//********************* Note: this class is thread-safe *********************** +//***************************************************************************** + +/** DjVu Navigation Directory. + + This class implements the {\em navigation directory} of a multipage + DjVu document - basically a list of pages that this document is composed + of. We would like to emphasize, that this is the list of namely + {\bf pages}, not {\bf files}. Any page may include any + number of additional files. When you've got an all-in-one-file multipage + DjVu document (DjVm archive) you may get the files list from \Ref{DjVmDir0} + class. + + The \Ref{DjVuNavDir} class can decode and encode the navigation directory + from {\bf NDIR} IFF chunk. It's normally created by the library during + decoding procedure and can be accessed like any other component of + the \Ref{DjVuImage} being decoded. + + In a typical multipage DjVu document the navigation directory is stored + in a separate IFF file containing only one chunk: {\bf NDIR} chunk. + This file should be included (by means of the {\bf INCL} chunk) into + every page of the document to enable the navigation. */ +class DjVuNavDir : public GPEnabled +{ +private: + GCriticalSection lock; + GURL baseURL; + GArray<GUTF8String> page2name; + GMap<GUTF8String, int> name2page; + GMap<GURL, int> url2page; +protected: + DjVuNavDir(const GURL &dir_url); + DjVuNavDir(ByteStream & str, const GURL &dir_url); + +public: + int get_memory_usage(void) const { return 1024; }; + + /** Creates a #DjVuNavDir# object. #dir_url# is the URL of the file + containing the directory source data. It will be used later + in translation by functions like \Ref{url_to_page}() and + \Ref{page_to_url}() */ + static GP<DjVuNavDir> create(const GURL &dir_url) + {return new DjVuNavDir(dir_url);} + + /** Creates #DjVuNavDir# object by decoding its contents from + the stream. #dir_url# is the URL of the file containing the + directory source data. */ + static GP<DjVuNavDir> create(ByteStream & str, const GURL &dir_url) + { return new DjVuNavDir(str,dir_url); } + + virtual ~DjVuNavDir(void) {}; + + /// Decodes the directory contents from the given \Ref{ByteStream} + void decode(ByteStream & str); + + /// Encodes the directory contents into the given \Ref{ByteStream} + void encode(ByteStream & str); + + /** Inserts a new page at position #where# pointing to a file + with name #name#. + + @param where The position where the page should be inserted. + #-1# means to append. + @param name The name of the file corresponding to this page. + The name may not contain slashes. The file may include + other files. */ + void insert_page(int where, const char * name); + + /// Deletes page with number #page_num# from the directory. + void delete_page(int page_num); + + /// Returns the number of pages in the directory. + int get_pages_num(void) const; + /** Converts the #url# to page number. Returns #-1# if the #url# + does not correspond to anything in the directory. */ + int url_to_page(const GURL & url) const; + /** Converts file name #name# to page number. Returns #-1# if file + with given name cannot be found. */ + int name_to_page(const char * name) const; + /** Converts given #page# to URL. Throws an exception if page number + is invalid. */ + GURL page_to_url(int page) const; + /** Converts given #page# to URL. Throws an exception if page number + is invalid. */ + GUTF8String page_to_name(int page) const; +}; + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPalette.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.cpp new file mode 100644 index 00000000..4e49fe12 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.cpp @@ -0,0 +1,590 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPalette.cpp,v 1.11 2004/03/18 15:03:50 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GException.h" +#include "ByteStream.h" +#include "BSByteStream.h" +#include "DjVuPalette.h" +#include <stdlib.h> +#include <math.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#define CUBEBITS 4 +#define CUBESIDE (1<<CUBEBITS) +#define CUBESIZE (CUBESIDE*CUBESIDE*CUBESIDE) + +#define RMUL 5 +#define GMUL 9 +#define BMUL 2 +#define SMUL (RMUL+GMUL+BMUL) + +#define MAXPALETTESIZE 65535 // Limit for a 16 bit unsigned read. + + +inline unsigned char +umax(unsigned char a, unsigned char b) +{ return (a>b) ? a : b; } + +inline unsigned char +umin(unsigned char a, unsigned char b) +{ return (a>b) ? b : a; } + +inline float +fmin(float a, float b) +{ return (a>b) ? b : a; } + + + +// ------- DJVUPALETTE + + +DjVuPalette::DjVuPalette() + : mask(0), hist(0), pmap(0) +{ +} + +DjVuPalette::~DjVuPalette() +{ + delete hist; + delete pmap; +} + +DjVuPalette& +DjVuPalette::operator=(const DjVuPalette &ref) +{ + if (this != &ref) + { + delete hist; + delete pmap; + mask = 0; + palette = ref.palette; + colordata = ref.colordata; + } + return *this; +} + +DjVuPalette::DjVuPalette(const DjVuPalette &ref) + : mask(0), hist(0), pmap(0) +{ + this->operator=(ref); +} + + + +// -------- HISTOGRAM ALLOCATION + +void +DjVuPalette::allocate_hist() +{ + if (! hist) + { + hist = new GMap<int,int>; + mask = 0; + } + else + { + GMap<int,int> *old = hist; + hist = new GMap<int,int>; + mask = (mask<<1)|(0x010101); + for (GPosition p = *old; p; ++p) + { + int k = old->key(p); + int w = (*old)[p]; + (*hist)[k | mask] += w; + } + delete old; + } +} + + +// -------- PALETTE COMPUTATION + + +#ifndef NEED_DECODER_ONLY + +struct PData +{ + unsigned char p[3]; + int w; +}; + +struct PBox +{ + PData *data; + int colors; + int boxsize; + int sum; +}; +int +DjVuPalette::bcomp (const void *a, const void *b) +{ + return ((PData*)a)->p[0] - ((PData*)b)->p[0]; +} + +int + +DjVuPalette::gcomp (const void *a, const void *b) +{ + return ((PData*)a)->p[1] - ((PData*)b)->p[1]; +} + +int +DjVuPalette::rcomp (const void *a, const void *b) +{ + return ((PData*)a)->p[2] - ((PData*)b)->p[2]; +} + +int +DjVuPalette::lcomp (const void *a, const void *b) +{ + unsigned char *aa = ((PColor*)a)->p; + unsigned char *bb = ((PColor*)b)->p; + if (aa[3] != bb[3]) + return aa[3]-bb[3]; + else if (aa[2] != bb[2]) + return aa[2]-bb[2]; + else if (aa[1] != bb[1]) + return aa[1]=bb[1]; + else + return aa[0]-bb[0]; +} + +int +DjVuPalette::compute_palette(int maxcolors, int minboxsize) +{ + if (!hist) + G_THROW( ERR_MSG("DjVuPalette.no_color") ); + if (maxcolors<1 || maxcolors>MAXPALETTESIZE) + G_THROW( ERR_MSG("DjVuPalette.many_colors") ); + + // Paul Heckbert: "Color Image Quantization for Frame Buffer Display", + // SIGGRAPH '82 Proceedings, page 297. (also in ppmquant) + + // Collect histogram colors + int sum = 0; + int ncolors = 0; + GTArray<PData> pdata; + for (GPosition p = *hist; p; ++p) + { + pdata.touch(ncolors); + PData &data = pdata[ncolors++]; + int k = hist->key(p); + data.p[0] = (k>>16) & 0xff; + data.p[1] = (k>>8) & 0xff; + data.p[2] = (k) & 0xff; + data.w = (*hist)[p]; + sum += data.w; + } + // Create first box + GList<PBox> boxes; + PBox newbox; + newbox.data = pdata; + newbox.colors = ncolors; + newbox.boxsize = 256; + newbox.sum = sum; + boxes.append(newbox); + // Repeat spliting boxes + while (boxes.size() < maxcolors) + { + // Find suitable box + GPosition p; + for (p=boxes; p; ++p) + if (boxes[p].colors>=2 && boxes[p].boxsize>minboxsize) + break; + if (! p) + break; + // Find box boundaries + PBox &splitbox = boxes[p]; + unsigned char pmax[3]; + unsigned char pmin[3]; + pmax[0] = pmin[0] = splitbox.data->p[0]; + pmax[1] = pmin[1] = splitbox.data->p[1]; + pmax[2] = pmin[2] = splitbox.data->p[2]; + for (int j=1; j<splitbox.colors; j++) + { + pmax[0] = umax(pmax[0], splitbox.data[j].p[0]); + pmax[1] = umax(pmax[1], splitbox.data[j].p[1]); + pmax[2] = umax(pmax[2], splitbox.data[j].p[2]); + pmin[0] = umin(pmin[0], splitbox.data[j].p[0]); + pmin[1] = umin(pmin[1], splitbox.data[j].p[1]); + pmin[2] = umin(pmin[2], splitbox.data[j].p[2]); + } + // Determine split direction and sort + int bl = pmax[0]-pmin[0]; + int gl = pmax[1]-pmin[1]; + int rl = pmax[2]-pmin[2]; + splitbox.boxsize = (bl>gl ? (rl>bl ? rl : bl) : (rl>gl ? rl : gl)); + if (splitbox.boxsize <= minboxsize) + continue; + if (gl == splitbox.boxsize) + qsort(splitbox.data, splitbox.colors, sizeof(PData), gcomp); + else if (rl == splitbox.boxsize) + qsort(splitbox.data, splitbox.colors, sizeof(PData), rcomp); + else + qsort(splitbox.data, splitbox.colors, sizeof(PData), bcomp); + // Find median + int lowercolors = 0; + int lowersum = 0; + while (lowercolors<splitbox.colors-1 && lowersum+lowersum<splitbox.sum) + lowersum += splitbox.data[lowercolors++].w; + // Compute new boxes + newbox.data = splitbox.data + lowercolors; + newbox.colors = splitbox.colors - lowercolors; + newbox.sum = splitbox.sum - lowersum; + splitbox.colors = lowercolors; + splitbox.sum = lowersum; + // Insert boxes at proper location + GPosition q; + for (q=p; q; ++q) + if (boxes[q].sum < newbox.sum) + break; + boxes.insert_before(q, newbox); + for (q=p; q; ++q) + if (boxes[q].sum < splitbox.sum) + break; + boxes.insert_before(q, boxes, p); + } + // Fill palette array + ncolors = 0; + palette.empty(); + palette.resize(0,boxes.size()-1); + for (GPosition p=boxes; p; ++p) + { + PBox &box = boxes[p]; + // Compute box representative color + float bsum = 0; + float gsum = 0; + float rsum = 0; + for (int j=0; j<box.colors; j++) + { + float w = (float)box.data[j].w; + bsum += box.data[j].p[0] * w; + gsum += box.data[j].p[1] * w; + rsum += box.data[j].p[2] * w; + } + PColor &color = palette[ncolors++]; + color.p[0] = (unsigned char) fmin(255, bsum/box.sum); + color.p[1] = (unsigned char) fmin(255, gsum/box.sum); + color.p[2] = (unsigned char) fmin(255, rsum/box.sum); + color.p[3] = ( color.p[0]*BMUL + color.p[1]*GMUL + color.p[2]*RMUL) / SMUL; + } + // Save dominant color + PColor dcolor = palette[0]; + // Sort palette colors in luminance order + qsort((PColor*)palette, ncolors, sizeof(PColor), lcomp); + // Clear invalid data + colordata.empty(); + delete pmap; + pmap = 0; + // Return dominant color + return color_to_index_slow(dcolor.p); +} + + + +int +DjVuPalette::compute_pixmap_palette(const GPixmap &pm, int ncolors, int minboxsize) +{ + // Prepare histogram + histogram_clear(); + for (int j=0; j<(int)pm.rows(); j++) + { + const GPixel *p = pm[j]; + for (int i=0; i<(int)pm.columns(); i++) + histogram_add(p[i], 1); + } + // Compute palette + return compute_palette(ncolors, minboxsize); +} + + +#endif + + + + +// -------- QUANTIZATION + + +void +DjVuPalette::allocate_pmap() +{ + if (! pmap) + pmap = new GMap<int,int>; +} + +int +DjVuPalette::color_to_index_slow(const unsigned char *bgr) +{ + PColor *pal = palette; + const int ncolors = palette.size(); + if (! ncolors) + G_THROW( ERR_MSG("DjVuPalette.not_init") ); + // Should be able to do better + int found = 0; + int founddist = 3*256*256; + for (int i=0; i<ncolors; i++) + { + int bd = bgr[0] - pal[i].p[0]; + int gd = bgr[1] - pal[i].p[1]; + int rd = bgr[2] - pal[i].p[2]; + int dist = (bd*bd)+(gd*gd)+(rd*rd); + if (dist < founddist) + { + found = i; + founddist = dist; + } + } + // Store in pmap + if (pmap && pmap->size()<0x8000) + { + int key = (bgr[0]<<16)|(bgr[1]<<8)|(bgr[2]); + (*pmap)[key] = found; + } + // Return + return found; +} + + +#ifndef NEED_DECODER_ONLY + +void +DjVuPalette::quantize(GPixmap &pm) +{ + for (int j=0; j<(int)pm.rows(); j++) + { + GPixel *p = pm[j]; + for (int i=0; i<(int)pm.columns(); i++) + index_to_color(color_to_index(p[i]), p[i]); + } +} + +int +DjVuPalette::compute_palette_and_quantize(GPixmap &pm, int maxcolors, int minboxsize) +{ + int result = compute_pixmap_palette(pm, maxcolors, minboxsize); + quantize(pm); + return result; +} + +void +DjVuPalette::color_correct(double corr) +{ + const int palettesize = palette.size(); + if (palettesize > 0) + { + // Copy colors + int i; + GTArray<GPixel> pix(0,palettesize-1); + GPixel *r = pix; + PColor *q = palette; + for (i=0; i<palettesize; i++) + { + r[i].b = q[i].p[0]; + r[i].g = q[i].p[1]; + r[i].r = q[i].p[2]; + } + // Apply color correction + GPixmap::color_correct(corr, r, palettesize); + // Restore colors + for (i=0; i<palettesize; i++) + { + q[i].p[0] = r[i].b; + q[i].p[1] = r[i].g; + q[i].p[2] = r[i].r; + } + } +} + +#endif + + +// -------- ENCODE AND DECODE + +#define DJVUPALETTEVERSION 0 + +void +DjVuPalette::encode_rgb_entries(ByteStream &bs) const +{ + const int palettesize = palette.size(); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + p[2] = palette[c].p[0]; + p[1] = palette[c].p[1]; + p[0] = palette[c].p[2]; + bs.writall((const void*)p, 3); + } +} + +void +DjVuPalette::encode(GP<ByteStream> gbs) const +{ + ByteStream &bs=*gbs; + const int palettesize = palette.size(); + const int datasize = colordata.size(); + // Code version number + int version = DJVUPALETTEVERSION; + if (datasize>0) version |= 0x80; + bs.write8(version); + // Code palette + bs.write16(palettesize); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + p[0] = palette[c].p[0]; + p[1] = palette[c].p[1]; + p[2] = palette[c].p[2]; + bs.writall((const void*)p, 3); + } + // Code colordata + if (datasize > 0) + { + bs.write24(datasize); + GP<ByteStream> gbsb=BSByteStream::create(gbs, 50); + ByteStream &bsb=*gbsb; + for (int d=0; d<datasize; d++) + bsb.write16(colordata[d]); + } +} + +void +DjVuPalette::decode_rgb_entries(ByteStream &bs, const int palettesize) +{ + palette.resize(0,palettesize-1); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + bs.readall((void*)p, 3); + palette[c].p[0] = p[2]; + palette[c].p[1] = p[1]; + palette[c].p[2] = p[0]; + palette[c].p[3] = (p[0]*BMUL+p[1]*GMUL+p[2]*RMUL)/SMUL; + } +} + +void +DjVuPalette::decode(GP<ByteStream> gbs) +{ + ByteStream &bs=*gbs; + // Make sure that everything is clear + delete hist; + delete pmap; + hist = 0; + pmap = 0; + mask = 0; + // Code version + int version = bs.read8(); + if ( (version & 0x7f) != DJVUPALETTEVERSION) + G_THROW( ERR_MSG("DjVuPalette.bad_version") ); + // Code palette + const int palettesize = bs.read16(); + if (palettesize<0 || palettesize>MAXPALETTESIZE) + G_THROW( ERR_MSG("DjVuPalette.bad_palette") ); + palette.resize(0,palettesize-1); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + bs.readall((void*)p, 3); + palette[c].p[0] = p[0]; + palette[c].p[1] = p[1]; + palette[c].p[2] = p[2]; + palette[c].p[3] = (p[0]*BMUL+p[1]*GMUL+p[2]*RMUL)/SMUL; + } + // Code data + if (version & 0x80) + { + int datasize = bs.read24(); + if (datasize<0) + G_THROW( ERR_MSG("DjVuPalette.bad_palette") ); + colordata.resize(0,datasize-1); + GP<ByteStream> gbsb=BSByteStream::create(gbs); + ByteStream &bsb=*gbsb; + for (int d=0; d<datasize; d++) + { + short s = bsb.read16(); + if (s<0 || s>=palettesize) + G_THROW( ERR_MSG("DjVuPalette.bad_palette") ); + colordata[d] = s; + } + } +} + + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPalette.h b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.h new file mode 100644 index 00000000..7f9884f1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.h @@ -0,0 +1,339 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPalette.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUPALETTE_H_ +#define _DJVUPALETTE_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GContainer.h" +#include "GPixmap.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name DjVuPalette.h + Files #"DjVuPalette.h"# and #"DjVuPalette.cpp"# implement a single class + \Ref{DjVuPalette} which provides facilities for computing optimal color + palettes, coding color palettes, and coding sequences of color indices. + @memo + DjVuPalette header file + @version + #$Id: DjVuPalette.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# + @author: + L\'eon Bottou <leonb@research.att.com> */ +//@{ + + +/** Computing and coding color palettes and index arrays. + This class provides facilities for computing optimal color palettes, + coding color palettes, and coding sequences of color indices. + + {\bf Creating a color palette} -- The recipe for creating a color palette + consists in (a) creating a DjVuPalette object, (b) constructing a color + histogram using \Ref{histogram_add}, and (c) calling function + \Ref{compute_palette}. + + {\bf Accessing the color palette} -- Conversion between colors and color + palette indices is easily achieved with \Ref{color_to_index} and + \Ref{index_to_color}. There are also functions for computing a palette + and quantizing a complete pixmap. + + {\bf Sequences of color indices} -- The DjVuPalette object also contains + an array \Ref{colordata} optionally containing a sequence of color + indices. This array will be encoded and decoded by functions \Ref{encode} + and \Ref{decode}. This feature simplifies the implementation of the ``one + color per symbol'' model in DjVu. + + {\bf Coding color palettes and color indices} -- Two functions + \Ref{encode} and \Ref{decode} are provided for coding the color palette + and the array of color indices when appropriate. */ +#ifdef _WIN32_WCE_EMULATION // Work around odd behavior under WCE Emulation +#define CALLINGCONVENTION __cdecl +#else +#define CALLINGCONVENTION /* */ +#endif + +class DjVuPalette : public GPEnabled +{ +protected: + DjVuPalette(void); +public: + /// Generic creator + static GP<DjVuPalette> create(void) {return new DjVuPalette();} + + /// Non-virtual destructor + ~DjVuPalette(); + // COPY + DjVuPalette(const DjVuPalette &ref); + DjVuPalette& operator=(const DjVuPalette &ref); + // PALETTE COMPUTATION + /** Resets the color histogram to zero. */ + void histogram_clear(); + /** Adds the color specified by #p# to the histogram. + Argument #weight# represent the number of pixels with this color. */ + void histogram_add(const GPixel &p, int weight); + /** Adds the color specified by the triple #bgr# to the histogram. + Argument #weight# represent the number of pixels with this color. */ + void histogram_add(const unsigned char *bgr, int weight); + /** Adds the color specified by the weighted triple #bgr# to the histogram. + Argument #weight# represent the number of pixels with this color. This + function will compute the actual color by dividing the elements of the + #bgr# array by #weight# and then use the unnormalized values to compute + the average color per bucket. This is all a way to avoid excessive loss + of accuracy. */ + void histogram_norm_and_add(const int *bgr, int weight); + /** Computes an optimal palette for representing an image where colors + appear according to the histogram. Argument #maxcolors# is the maximum + number of colors allowed in the palette (up to 1024). Argument + #minboxsize# controls the minimal size of the color cube area affected + to a color palette entry. Returns the index of the dominant color. */ + int compute_palette(int maxcolors, int minboxsize=0); + /** Computes the optimal palette for pixmap #pm#. This function builds the + histogram for pixmap #pm# and computes the optimal palette using + \Ref{compute_palette}. */ + int compute_pixmap_palette(const GPixmap &pm, int ncolors, int minboxsize=0); + // CONVERSION + /** Returns the number of colors in the palette. */ + int size() const; + /** Returns the best palette index for representing color #p#. */ + int color_to_index(const GPixel &p); + /** Returns the best palette index for representing color #bgr#. */ + int color_to_index(const unsigned char *bgr); + /** Overwrites #p# with the color located at position #index# in the palette. */ + void index_to_color(int index, GPixel &p) const; + /** Overwrites #rgb[0..3]# with the color located at + position #index# in the palette. */ + void index_to_color(int index, unsigned char *bgr) const; + /** Quantizes pixmap #pm#. All pixels are replaced by their closest + approximation available in the palette. */ + void quantize(GPixmap &pm); + /** Calls \Ref{compute_pixmap_palette} and \Ref{quantize}. */ + int compute_palette_and_quantize(GPixmap &pm, int maxcolors, int minboxsize=0); + // COLOR CORRECTION + /** Applies a luminance gamma correction factor of #corr# to the palette + entries. Values greater than #1.0# make the image brighter. Values + smaller than #1.0# make the image darker. The documentation of program + \Ref{ppmcoco} explains how to properly use this function. */ + void color_correct(double corr); + // COLOR INDEX DATA + /** Contains an optional sequence of color indices. + Function \Ref{encode} and \Ref{decode} also encode and decode this + sequence when such a sequence is provided. */ + GTArray<short> colordata; + /** Returns colors from the color index sequence. Pixel #out# is + overwritten with the color corresponding to the #nth# element of the + color sequence \Ref{colordata}. */ + void get_color(int nth, GPixel &out) const; + // CODING + /** Writes the palette colors. This function writes each palette color as a + RGB triple into bytestream #bs#. */ + void encode_rgb_entries(ByteStream &bs) const; + /** Reads palette colors. This function initializes the palette colors by + reading #palettesize# RGB triples from bytestream #bs#. */ + void decode_rgb_entries(ByteStream &bs, const int palettesize); + /** Encodes the palette and the color index sequence. This function encodes + the a version byte, the palette size, the palette colors and the color + index sequence into bytestream #bs#. Note that the color histogram is + never saved. */ + void encode(GP<ByteStream> bs) const; + /** Initializes the object by reading data from bytestream #bs#. This + function reads a version byte, the palette size, the palette and the + color index sequence from bytestream #bs#. Note that the color + histogram is never saved. */ + void decode(GP<ByteStream> bs); + +private: + // Histogram + int mask; + GMap<int,int> *hist; + // Quantization data + struct PColor { unsigned char p[4]; }; + GTArray<PColor> palette; + GMap<int,int> *pmap; + // Helpers + void allocate_hist(); + void allocate_pmap(); + static int CALLINGCONVENTION bcomp (const void*, const void*); + static int CALLINGCONVENTION gcomp (const void*, const void*); + static int CALLINGCONVENTION rcomp (const void*, const void*); + static int CALLINGCONVENTION lcomp (const void*, const void*); + int color_to_index_slow(const unsigned char *bgr); +private: // dummy functions + static void encode(ByteStream *); + static void decode(ByteStream *); +}; + + +//@} + +// ------------ INLINES + + +inline void +DjVuPalette::histogram_clear() +{ + delete hist; + hist = 0; + mask = 0; +} + +inline void +DjVuPalette::histogram_add(const unsigned char *bgr, int weight) +{ + if (weight > 0) + { + if (!hist || hist->size()>=0x4000) + allocate_hist(); + int key = (bgr[0]<<16)|(bgr[1]<<8)|(bgr[2])|(mask); + (*hist)[key] += weight; + } +} + +inline void +DjVuPalette::histogram_add(const GPixel &p, int weight) +{ + histogram_add(&p.b, weight); +} + +inline void +DjVuPalette::histogram_norm_and_add(const int *bgr, int weight) +{ + if (weight > 0) + { + int p0 = bgr[0]/weight; if (p0>255) p0=255; + int p1 = bgr[1]/weight; if (p1>255) p1=255; + int p2 = bgr[2]/weight; if (p2>255) p2=255; + if (!hist || hist->size()>=0x4000) + allocate_hist(); + int key = (p0<<16)|(p1<<8)|(p2)|(mask); + (*hist)[key] += weight; + } +} + +inline int +DjVuPalette::size() const +{ + return palette.size(); +} + +inline int +DjVuPalette::color_to_index(const unsigned char *bgr) +{ + if (! pmap) + allocate_pmap(); + int key = (bgr[0]<<16)|(bgr[1]<<8)|(bgr[2]); + GPosition p = pmap->contains(key); + if ( p) + return (*pmap)[p]; + return color_to_index_slow(bgr); +} + +inline int +DjVuPalette::color_to_index(const GPixel &p) +{ + return color_to_index(&p.b); +} + +inline void +DjVuPalette::index_to_color(int index, unsigned char *bgr) const +{ + const PColor &color = palette[index]; + bgr[0] = color.p[0]; + bgr[1] = color.p[1]; + bgr[2] = color.p[2]; +} + +inline void +DjVuPalette::index_to_color(int index, GPixel &p) const +{ + index_to_color(index, &p.b); +} + +inline void +DjVuPalette::get_color(int nth, GPixel &p) const +{ + index_to_color(colordata[nth], p); +} + + + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + + + + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPort.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuPort.cpp new file mode 100644 index 00000000..5e8a25c9 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPort.cpp @@ -0,0 +1,710 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPort.cpp,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuPort.h" +#include "GOS.h" +#include "DjVuImage.h" +#include "DjVuDocument.h" +#include "DjVuFile.h" +#include "DjVuMessageLite.h" +#include "DataPool.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +//**************************************************************************** +//******************************* Globals ************************************ +//**************************************************************************** + +static DjVuPortcaster *pcaster; + +DjVuPortcaster * +DjVuPort::get_portcaster(void) +{ + if (!pcaster) pcaster = new DjVuPortcaster(); + return pcaster; +} + +class DjVuPort::DjVuPortCorpse +{ +public: + DjVuPort * port; + DjVuPortCorpse * next; + + DjVuPortCorpse(DjVuPort * _port) : port(_port), next(0) {} +}; + +//**************************************************************************** +//******************************* DjVuPort *********************************** +//**************************************************************************** + +#define MAX_CORPSE_NUM 128 + +// Last MAX_CORPSE_NUM addresses of dead DjVuPorts. We want to maintain this +// list because of the way DjVuPort::is_port_alive() works: it accepts an +// address and runs it thru its internal maps. The problem will occur if +// a new DjVuPort is created exactly on place of another one, which just +// died. Here we attempt to remember the last MAX_CORPSE_NUM addresses +// of dead DjVuPorts, and take them into account in DjVuPort::operator new(); +GCriticalSection * DjVuPort::corpse_lock; +DjVuPort::DjVuPortCorpse * DjVuPort::corpse_head; +DjVuPort::DjVuPortCorpse * DjVuPort::corpse_tail; +int DjVuPort::corpse_num; + +void * +DjVuPort::operator new (size_t sz) +{ + if (!corpse_lock) corpse_lock=new GCriticalSection(); + + // Loop until we manage to allocate smth, which is not mentioned in + // the 'corpse' list. Thus we will avoid allocating a new DjVuPort + // on place of a dead one. Not *absolutely* secure (only 64 items + // in the list) but is still better than nothing. + void * addr=0; + { + GCriticalSectionLock lock(corpse_lock); + + // Store here addresses, which were found in 'corpse' list. + // We will free then in the end + int addr_num=0; + static void * addr_arr[MAX_CORPSE_NUM]; + + // Make at most MAX_CORPSE_NUM attempts. During each attempt + // we try to allocate a block of memory for DjVuPort. If + // the address of this block is not in the corpse list, we break + // All addresses will be recorder, so that we can delete them + // after we're done. + for(int attempt=0;attempt<MAX_CORPSE_NUM;attempt++) + { + void * test_addr=::operator new (sz); + addr_arr[addr_num++]=test_addr; + + // See if 'test_addr' is in the 'corpse' list (was recently used) + DjVuPortCorpse * corpse; + for(corpse=corpse_head;corpse;corpse=corpse->next) + if (test_addr==corpse->port) break; + if (!corpse) + { + addr=test_addr; + addr_num--; + break; + } + } + // If all attempts failed (all addresses generated are already + // in the list of corpses, allocate a new one and proceed + // w/o additional checks + if (!addr) addr=::operator new(sz); + + // Here 'addr_arr[0<=i<addr_num]' contains addresses, that we + // tried to allocate, and which need to be freed now + // 'addr' contains address we want to use. + addr_num--; + while(addr_num>=0) ::operator delete(addr_arr[addr_num--]); + } + + DjVuPortcaster * pcaster=get_portcaster(); + GCriticalSectionLock lock(&pcaster->map_lock); + pcaster->cont_map[addr]=0; + return addr; +} + +void +DjVuPort::operator delete(void * addr) +{ + if (corpse_lock) + { + GCriticalSectionLock lock(corpse_lock); + + // Add 'addr' to the list of corpses + if (corpse_tail) + { + corpse_tail->next=new DjVuPortCorpse((DjVuPort *) addr); + corpse_tail=corpse_tail->next; + corpse_tail->next=0; + } else + { + corpse_head=corpse_tail=new DjVuPortCorpse((DjVuPort *) addr); + corpse_tail->next=0; + } + corpse_num++; + if (corpse_num>=MAX_CORPSE_NUM) + { + DjVuPortCorpse * corpse=corpse_head; + corpse_head=corpse_head->next; + delete corpse; + corpse_num--; + } + } + ::operator delete(addr); +} + +DjVuPort::DjVuPort() +{ + DjVuPortcaster *pcaster = get_portcaster(); + GCriticalSectionLock lock(& pcaster->map_lock ); + GPosition p = pcaster->cont_map.contains(this); + if (!p) G_THROW( ERR_MSG("DjVuPort.not_alloc") ); + pcaster->cont_map[p] = (void*)this; +} + +DjVuPort::DjVuPort(const DjVuPort & port) +{ + DjVuPortcaster *pcaster = get_portcaster(); + GCriticalSectionLock lock(& pcaster->map_lock ); + GPosition p = pcaster->cont_map.contains(this); + if (!p) G_THROW( ERR_MSG("DjVuPort.not_alloc") ); + pcaster->cont_map[p] = (void*)this; + pcaster->copy_routes(this, &port); +} + +DjVuPort & +DjVuPort::operator=(const DjVuPort & port) +{ + if (this != &port) + get_portcaster()->copy_routes(this, &port); + return *this; +} + +DjVuPort::~DjVuPort(void) +{ + get_portcaster()->del_port(this); +} + + +//**************************************************************************** +//**************************** DjVuPortcaster ******************************** +//**************************************************************************** + + + +DjVuPortcaster::DjVuPortcaster(void) +{ +} + +DjVuPortcaster::~DjVuPortcaster(void) +{ + GCriticalSectionLock lock(&map_lock); + for(GPosition pos=route_map;pos;++pos) + delete (GList<void *> *) route_map[pos]; +} + +GP<DjVuPort> +DjVuPortcaster::is_port_alive(DjVuPort *port) +{ + GP<DjVuPort> gp_port; + GCriticalSectionLock lock(&map_lock); + GPosition pos=cont_map.contains(port); + if (pos && cont_map[pos] && ((DjVuPort *) port)->get_count()>0) + gp_port=port; + return gp_port; +} + +void +DjVuPortcaster::add_alias(const DjVuPort * port, const GUTF8String &alias) +{ + GCriticalSectionLock lock(&map_lock); + a2p_map[alias]=port; +} + +void +DjVuPortcaster::clear_all_aliases(void) +{ + DjVuPortcaster *p=get_portcaster(); + GCriticalSectionLock lock(&(p->map_lock)); + GPosition pos; + while((pos=p->a2p_map)) + { + p->a2p_map.del(pos); + } +} + +void +DjVuPortcaster::clear_aliases(const DjVuPort * port) +{ + GCriticalSectionLock lock(&map_lock); + for(GPosition pos=a2p_map;pos;) + if (a2p_map[pos]==port) + { + GPosition this_pos=pos; + ++pos; + a2p_map.del(this_pos); + } else ++pos; +} + +GP<DjVuPort> +DjVuPortcaster::alias_to_port(const GUTF8String &alias) +{ + GCriticalSectionLock lock(&map_lock); + GPosition pos; + if (a2p_map.contains(alias, pos)) + { + DjVuPort * port=(DjVuPort *) a2p_map[pos]; + GP<DjVuPort> gp_port=is_port_alive(port); + if (gp_port) return gp_port; + else a2p_map.del(pos); + } + return 0; +} + +GPList<DjVuPort> +DjVuPortcaster::prefix_to_ports(const GUTF8String &prefix) +{ + GPList<DjVuPort> list; + { + int length=prefix.length(); + if (length) + { + GCriticalSectionLock lock(&map_lock); + for(GPosition pos=a2p_map;pos;++pos) + if (!prefix.cmp(a2p_map.key(pos), length)) + { + DjVuPort * port=(DjVuPort *) a2p_map[pos]; + GP<DjVuPort> gp_port=is_port_alive(port); + if (gp_port) list.append(gp_port); + } + } + } + return list; +} + +void +DjVuPortcaster::del_port(const DjVuPort * port) +{ + GCriticalSectionLock lock(&map_lock); + + GPosition pos; + + // Update the "aliases map" + clear_aliases(port); + + // Update "contents map" + if (cont_map.contains(port, pos)) cont_map.del(pos); + + // Update "route map" + if (route_map.contains(port, pos)) + { + delete (GList<void *> *) route_map[pos]; + route_map.del(pos); + } + for(pos=route_map;pos;) + { + GList<void *> & list=*(GList<void *> *) route_map[pos]; + GPosition list_pos; + if (list.search((void *) port, list_pos)) list.del(list_pos); + if (!list.size()) + { + delete &list; + GPosition tmp_pos=pos; + ++pos; + route_map.del(tmp_pos); + } else ++pos; + } +} + +void +DjVuPortcaster::add_route(const DjVuPort * src, DjVuPort * dst) + // Adds route src->dst +{ + GCriticalSectionLock lock(&map_lock); + if (cont_map.contains(src) && src->get_count()>0 && + cont_map.contains(dst) && dst->get_count()>0) + { + if (!route_map.contains(src)) route_map[src]=new GList<void *>(); + GList<void *> & list=*(GList<void *> *) route_map[src]; + if (!list.contains(dst)) list.append(dst); + } +} + +void +DjVuPortcaster::del_route(const DjVuPort * src, DjVuPort * dst) +// Deletes route src->dst +{ + GCriticalSectionLock lock(&map_lock); + + if (route_map.contains(src)) + { + GList<void *> & list=*(GList<void *> *) route_map[src]; + GPosition pos; + if (list.search(dst, pos)) list.del(pos); + if (!list.size()) + { + delete &list; + route_map.del(src); + } + } +} + +void +DjVuPortcaster::copy_routes(DjVuPort * dst, const DjVuPort * src) + // For every route src->x or x->src, it creates a new one: + // dst->x or x->dst respectively. It's useful when you create a copy + // of a port and you want the copy to stay connected. +{ + GCriticalSectionLock lock(&map_lock); + + if (!cont_map.contains(src) || src->get_count()<=0 || + !cont_map.contains(dst) || dst->get_count()<=0) return; + + for(GPosition pos=route_map;pos;++pos) + { + GList<void *> & list=*(GList<void *> *) route_map[pos]; + if (route_map.key(pos) == src) + for(GPosition pos=list;pos;++pos) + add_route(dst, (DjVuPort *) list[pos]); + for(GPosition pos=list;pos;++pos) + if ((DjVuPort*)(list[pos]) == src) + add_route((DjVuPort *) route_map.key(pos), dst); + } +} + +void +DjVuPortcaster::add_to_closure(GMap<const void *, void *> & set, + const DjVuPort * dst, int distance) +{ + // Assuming that the map's already locked + // GCriticalSectionLock lock(&map_lock); + set[dst]= (void*) (unsigned long) distance; + if (route_map.contains(dst)) + { + GList<void *> & list=*(GList<void *> *) route_map[dst]; + for(GPosition pos=list;pos;++pos) + { + DjVuPort * new_dst=(DjVuPort *) list[pos]; + if (!set.contains(new_dst)) + add_to_closure(set, new_dst, distance+1); + } + } +} + +void +DjVuPortcaster::compute_closure(const DjVuPort * src, GPList<DjVuPort> &list, bool sorted) +{ + GCriticalSectionLock lock(&map_lock); + GMap<const void*, void*> set; + if (route_map.contains(src)) + { + GList<void *> & list=*(GList<void *> *) route_map[src]; + for(GPosition pos=list;pos;++pos) + { + DjVuPort * dst=(DjVuPort *) list[pos]; + if (dst==src) add_to_closure(set, src, 0); + else add_to_closure(set, dst, 1); + } + } + + // Compute list + GPosition pos; + if (sorted) + { + // Sort in depth order + int max_dist=0; + for(pos=set;pos;++pos) + if (max_dist < (int)(long)set[pos]) + max_dist = (int)(long)set[pos]; + GArray<GList<const void*> > lists(0,max_dist); + for(pos=set;pos;++pos) + lists[(int)(long)set[pos]].append(set.key(pos)); + for(int dist=0;dist<=max_dist;dist++) + for(pos=lists[dist];pos;++pos) + { + GP<DjVuPort> p = is_port_alive((DjVuPort*) lists[dist][pos]); + if (p) list.append(p); + } + } + else + { + // Gather ports without order + for(pos=set;pos;++pos) + { + GP<DjVuPort> p = is_port_alive((DjVuPort*) set.key(pos)); + if (p) list.append(p); + } + } +} + +GURL +DjVuPortcaster::id_to_url(const DjVuPort * source, const GUTF8String &id) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + GURL url; + for(GPosition pos=list;pos;++pos) + { + url=list[pos]->id_to_url(source, id); + if (!url.is_empty()) break; + } + return url; +} + +GP<DjVuFile> +DjVuPortcaster::id_to_file(const DjVuPort * source, const GUTF8String &id) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + GP<DjVuFile> file; + for(GPosition pos=list;pos;++pos) + if ((file=list[pos]->id_to_file(source, id))) break; + return file; +} + +GP<DataPool> +DjVuPortcaster::request_data(const DjVuPort * source, const GURL & url) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + GP<DataPool> data; + for(GPosition pos=list;pos;++pos) + if ((data = list[pos]->request_data(source, url))) + break; + return data; +} + +bool +DjVuPortcaster::notify_error(const DjVuPort * source, const GUTF8String &msg) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + for(GPosition pos=list;pos;++pos) + if (list[pos]->notify_error(source, msg)) + return 1; + return 0; +} + +bool +DjVuPortcaster::notify_status(const DjVuPort * source, const GUTF8String &msg) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + for(GPosition pos=list;pos;++pos) + if (list[pos]->notify_status(source, msg)) + return 1; + return 0; +} + +void +DjVuPortcaster::notify_redisplay(const DjVuImage * source) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_redisplay(source); +} + +void +DjVuPortcaster::notify_relayout(const DjVuImage * source) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_relayout(source); +} + +void +DjVuPortcaster::notify_chunk_done(const DjVuPort * source, const GUTF8String &name) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_chunk_done(source, name); +} + +void +DjVuPortcaster::notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_file_flags_changed(source, set_mask, clr_mask); +} + +void +DjVuPortcaster::notify_doc_flags_changed(const DjVuDocument * source, + long set_mask, long clr_mask) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_doc_flags_changed(source, set_mask, clr_mask); +} + +void +DjVuPortcaster::notify_decode_progress(const DjVuPort * source, float done) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_decode_progress(source, done); +} + +//**************************************************************************** +//******************************* DjVuPort *********************************** +//**************************************************************************** + +GURL +DjVuPort::id_to_url(const DjVuPort *, const GUTF8String &) { return GURL(); } + +GP<DjVuFile> +DjVuPort::id_to_file(const DjVuPort *, const GUTF8String &) { return 0; } + +GP<DataPool> +DjVuPort::request_data(const DjVuPort *, const GURL &) { return 0; } + +bool +DjVuPort::notify_error(const DjVuPort *, const GUTF8String &) { return 0; } + +bool +DjVuPort::notify_status(const DjVuPort *, const GUTF8String &) { return 0; } + +void +DjVuPort::notify_redisplay(const DjVuImage *) {} + +void +DjVuPort::notify_relayout(const DjVuImage *) {} + +void +DjVuPort::notify_chunk_done(const DjVuPort *, const GUTF8String &) {} + +void +DjVuPort::notify_file_flags_changed(const DjVuFile *, long, long) {} + +void +DjVuPort::notify_doc_flags_changed(const DjVuDocument *, long, long) {} + +void +DjVuPort::notify_decode_progress(const DjVuPort *, float) {} + +//**************************************************************************** +//*************************** DjVuSimplePort ********************************* +//**************************************************************************** + +GP<DataPool> +DjVuSimplePort::request_data(const DjVuPort * source, const GURL & url) +{ + G_TRY { + if (url.is_local_file_url()) + { +// GUTF8String fname=GOS::url_to_filename(url); +// if (GOS::basename(fname)=="-") fname="-"; + return DataPool::create(url); + } + } G_CATCH_ALL {} G_ENDCATCH; + return 0; +} + +bool +DjVuSimplePort::notify_error(const DjVuPort * source, const GUTF8String &msg) +{ + DjVuMessageLite::perror(msg); + return 1; +} + +bool +DjVuSimplePort::notify_status(const DjVuPort * source, const GUTF8String &msg) +{ + DjVuMessageLite::perror(msg); + return 1; +} + + + + + +//**************************************************************************** +//*************************** DjVuMemoryPort ********************************* +//**************************************************************************** + + + +GP<DataPool> +DjVuMemoryPort::request_data(const DjVuPort * source, const GURL & url) +{ + GCriticalSectionLock lk(&lock); + GP<DataPool> pool; + GPosition pos; + if (map.contains(url, pos)) + pool=map[pos]; + return pool; +} + +void +DjVuMemoryPort::add_data(const GURL & url, const GP<DataPool> & pool) +{ + GCriticalSectionLock lk(&lock); + map[url]=pool; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPort.h b/kviewshell/plugins/djvu/libdjvu/DjVuPort.h new file mode 100644 index 00000000..99c165fb --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPort.h @@ -0,0 +1,518 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPort.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUPORT_H +#define _DJVUPORT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GThreads.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DataPool; + +/** @name DjVuPort.h + Files #"DjVuPort.h"# and #"DjVuPort.cpp"# implement a communication + mechanism between different parties involved in decoding DjVu files. + It should be pretty clear that the creator of \Ref{DjVuDocument} and + \Ref{DjVuFile} would like to receive some information about the progress + of decoding, errors occurred, etc. It may also want to provide source data + for decoders (like it's done in the plugin where the real data is downloaded + from the net and is fed into DjVu decoders). + + Normally this functionality is implemented by means of callbacks which are + run when a given condition comes true. Unfortunately it's not quite easy + to implement this strategy in our case. The reason is that there may be + more than one "client" working with the same document, and the document + should send the information to each of the clients. This could be done by + means of callback {\em lists}, of course, but we want to achieve more + bulletproof results: we want to be sure that the client that we're about + to contact is still alive, and is not being destroyed by another thread. + Besides, we are going to call these "callbacks" from many places, from + many different classes. Maintaining multi-thread safe callback lists is + very difficult. + + Finally, we want to provide some default implementation of these + "callbacks" in the library, which should attempt to process the requests + themselves if they can, and contact the client only if they're unable to + do it (like in the case of \Ref{DjVuPort::request_data}() with local URL + where \Ref{DjVuDocument} can get the data from the hard drive itself not + disturbing the document's creator. + + Two classes implement a general communication mechanism: \Ref{DjVuPort} and + \Ref{DjVuPortcaster}. Any sender and recipient of requests should be a + subclass of \Ref{DjVuPort}. \Ref{DjVuPortcaster} maintains a map of + routes between \Ref{DjVuPort}s, which should be configured by somebody + else. Whenever a port wants to send a request, it calls the corresponding + function of \Ref{DjVuPortcaster}, and the portcaster relays the request to + all the destinations that it sees in the internal map. + + The \Ref{DjVuPortcaster} is responsible for keeping the map up to date by + getting rid of destinations that have been destroyed. Map updates are + performed from a single place and are serialized by a global monitor. + + @memo DjVu decoder communication mechanism. + @author Andrei Erofeev <eaf@geocities.com>\\ + L\'eon Bottou <leonb@research.att.com> + @version #$Id: DjVuPort.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + +class DjVuPort; +class DjVuPortcaster; +class DjVuFile; + +/** Base class for notification targets. + #DjVuPort# provides base functionality for classes willing to take part in + sending and receiving messages generated during decoding process. You + need to derive your class from #DjVuPort# if you want it to be able to + send or receive requests. In addition, for receiving requests you should + override one or more virtual function. + + {\bf Important remark} --- All ports should be allocated on the heap using + #operator new# and immediately secured using a \Ref{GP} smart pointer. + Ports which are not secured by a smart-pointer are not considered + ``alive'' and never receive notifications! */ + +class DjVuPort : public GPEnabled +{ +public: + DjVuPort(); + virtual ~DjVuPort(); + static void *operator new (size_t sz); + static void operator delete(void *addr); + + /** Use this function to get a copy of the global \Ref{DjVuPortcaster}. */ + static DjVuPortcaster *get_portcaster(void); + + /** Copy constructor. When #DjVuPort#s are copied, the portcaster + copies all incoming and outgoing routes of the original. */ + DjVuPort(const DjVuPort & port); + + /** Copy operator. Similarly to the copy constructor, the portcaster + copies all incoming and outgoing coming routes of the original. */ + DjVuPort & operator=(const DjVuPort & port); + + /** Should return 1 if the called class inherits class #class_name#. + When a destination receives a request, it can retrieve the pointer + to the source #DjVuPort#. This virtual function should be able + to help to identify the source of the request. For example, + \Ref{DjVuFile} is also derived from #DjVuPort#. In order for + the receiver to recognize the sender, the \Ref{DjVuFile} should + override this function to return #TRUE# when the #class_name# + is either #DjVuPort# or #DjVuFile# */ + virtual bool inherits(const GUTF8String &class_name) const; + + /** @name Notifications. + These virtual functions may be overridden by the subclasses + of #DjVuPort#. They are called by the \Ref{DjVuPortcaster} + when the port is alive and when there is a route between the + source of the notification and this port. */ + //@{ + + /** This request is issued to request translation of the ID, used + in an DjVu INCL chunk to a URL, which may be used to request + data associated with included file. \Ref{DjVuDocument} usually + intercepts all such requests, and the user doesn't have to + worry about the translation */ + virtual GURL id_to_url(const DjVuPort * source, const GUTF8String &id); + + /** This request is used to get a file corresponding to the + given ID. \Ref{DjVuDocument} is supposed to intercept it + and either create a new instance of \Ref{DjVuFile} or reuse + an existing one from the cache. */ + virtual GP<DjVuFile> id_to_file(const DjVuPort * source, const GUTF8String &id); + + /** This request is issued when decoder needs additional data + for decoding. Both \Ref{DjVuFile} and \Ref{DjVuDocument} are + initialized with a URL, not the document data. As soon as + they need the data, they call this function, whose responsibility + is to locate the source of the data basing on the #URL# passed + and return it back in the form of the \Ref{DataPool}. If this + particular receiver is unable to fullfil the request, it should + return #0#. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /** This notification is sent when an error occurs and the error message + should be shown to the user. The receiver should return #0# if it is + unable to process the request. Otherwise the receiver should return 1. */ + virtual bool notify_error(const DjVuPort * source, const GUTF8String &msg); + + /** This notification is sent to update the decoding status. The + receiver should return #0# if it is unable to process the + request. Otherwise the receiver should return 1. */ + virtual bool notify_status(const DjVuPort * source, const GUTF8String &msg); + + /** This notification is sent by \Ref{DjVuImage} when it should be + redrawn. It may be used to implement progressive redisplay. + + @param source The sender of the request */ + virtual void notify_redisplay(const class DjVuImage * source); + + /** This notification is sent by \ref{DjVuImage} when its geometry + has been changed as a result of decoding. It may be used to + implement progressive redisplay. */ + virtual void notify_relayout(const class DjVuImage * source); + + /** This notification is sent when a new chunk has been decoded. */ + virtual void notify_chunk_done(const DjVuPort * source, const GUTF8String &name); + + /** This notification is sent after the \Ref{DjVuFile} flags have + been changed. This happens, for example, when: + \begin{itemize} + \item Decoding succeeded, failed or just stopped + \item All data has been received + \item All included files have been created + \end{itemize} + + @param source \Ref{DjVuFile}, which flags have been changed + @param set_mask bits, which have been set + @param clr_mask bits, which have been cleared */ + virtual void notify_file_flags_changed(const class DjVuFile * source, + long set_mask, long clr_mask); + + /** This notification is sent after the \Ref{DjVuDocument} flags have + been changed. This happens, for example, after it receives enough + data and can determine its structure (#BUNDLED#, #OLD_INDEXED#, etc.). + + @param source \Ref{DjVuDocument}, which flags have been changed + @param set_mask bits, which have been set + @param clr_mask bits, which have been cleared */ + virtual void notify_doc_flags_changed(const class DjVuDocument * source, + long set_mask, long clr_mask); + + /** This notification is sent from time to time while decoding is in + progress. The purpose is obvious: to provide a way to know how much + is done and how long the decoding will continue. Argument #done# is + a number from 0 to 1 reflecting the progress. */ + virtual void notify_decode_progress(const DjVuPort * source, float done); + + /** This is the standard types for defining what to do in case of errors. + This is only used by some of the subclasses, but it needs to be + defined here to guarantee all subclasses use the same enum types. + In general, many errors are non recoverable. Using a setting + other than ABORT may just result in even more errors. */ + enum ErrorRecoveryAction {ABORT=0,SKIP_PAGES=1,SKIP_CHUNKS=2,KEEP_ALL=3 }; + //@} +public: + class DjVuPortCorpse; +private: + static GCriticalSection * corpse_lock; + static DjVuPortCorpse * corpse_head, * corpse_tail; + static int corpse_num; +}; + +/** Simple port. + An instance of #DjVuSimplePort# is automatically created when you create a + \Ref{DjVuFile} or a \Ref{DjVuDocument} without specifying a port. This + simple port can retrieve data for local urls (i.e. urls referring to local + files) and display error messages on #stderr#. All other notifications + are ignored. */ + +class DjVuSimplePort : public DjVuPort +{ +public: + /// Returns 1 if #class_name# is #"DjVuPort"# or #"DjVuSimplePort"#. + virtual bool inherits(const GUTF8String &class_name) const; + + /** If #url# is local, it created a \Ref{DataPool}, connects it to the + file with the given name and returns. Otherwise returns #0#. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /// Displays error on #stderr#. Always returns 1. + virtual bool notify_error(const DjVuPort * source, const GUTF8String &msg); + + /// Displays status on #stderr#. Always returns 1. + virtual bool notify_status(const DjVuPort * source, const GUTF8String &msg); +}; + + +/** Memory based port. + This \Ref{DjVuPort} maintains a map associating pseudo urls with data + segments. It processes the #request_data# notifications according to this + map. After initializing the port, you should add as many pairs #<url, + pool># as needed need and add a route from a \Ref{DjVuDocument} or + \Ref{DjVuFile} to this port. */ + +class DjVuMemoryPort : public DjVuPort +{ +public: + /// Returns 1 if #class_name# is #"DjVuPort"# or #"DjVuMemoryPort"# + virtual bool inherits(const GUTF8String &class_name) const; + + /** If #url# is one of those, that have been added before by means + of \Ref{add_data}() function, it will return the associated + \Ref{DataPool}. #ZERO# otherwize. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /** Adds #<url, pool># pair to the internal map. From now on, if + somebody asks for data corresponding to the #url#, it will + be returning the #pool# */ + void add_data(const GURL & url, const GP<DataPool> & pool); +private: + GCriticalSection lock; + GPMap<GURL, DataPool>map; +}; + + + +/** Maintains associations between ports. + It monitors the status of all ports (have they been destructed yet?), + accepts requests and notifications from them and forwards them to + destinations according to internally maintained map of routes. + + The caller can modify the route map any way he likes (see + \Ref{add_route}(), \Ref{del_route}(), \Ref{copy_routes}(), + etc. functions). Any port can be either a sender of a message, an + intermediary receiver or a final destination. + + When a request is sent, the #DjVuPortcaster# computes the list of + destinations by consulting with the route map. Notifications are only + sent to ``alive'' ports. A port is alive if it is referenced by a valid + \Ref{GP} smartpointer. As a consequence, a port usually becomes alive + after running the constructor (since the returned pointer is then assigned + to a smartpointer) and is no longer alive when the port is destroyed + (because it would not be destroyed if a smartpointer was referencing it). + + Destination ports are sorted according to their distance from the source. + For example, if port {\bf A} is connected to ports {\bf B} and {\bf C} + directly, and port {\bf B} is connected to {\bf D}, then {\bf B} and {\bf + C} are assumed to be one hop away from {\bf A}, while {\bf D} is two hops + away from {\bf A}. + + In some cases the requests and notifications are sent to every possible + destination, and the order is not significant (like it is for + \Ref{notify_file_flags_changed}() request). Others should be sent to the closest + destinations first, and only then to the farthest, in case if they have + not been processed by the closest. The examples are \Ref{request_data}(), + \Ref{notify_error}() and \Ref{notify_status}(). + + The user is not expected to create the #DjVuPortcaster# itself. He should + use \Ref{get_portcaster}() global function instead. */ +class DjVuPortcaster +{ +public: + /** Use this function to get a copy of the global \Ref{DjVuPortcaster}. */ + static DjVuPortcaster *get_portcaster(void) + { return DjVuPort::get_portcaster(); } ; + + /** The default constructor. */ + DjVuPortcaster(void); + + virtual ~DjVuPortcaster(void); + + /** Removes the specified port from all routes. It will no longer + be able to receive or generate messages and will be considered + {\bf "dead"} by \Ref{is_port_alive}() function. */ + void del_port(const DjVuPort * port); + + /** Adds route from #src# to #dst#. Whenever a request is + sent or received by #src#, it will be forwarded to #dst# as well. + @param src The source + @param dst The destination */ + void add_route(const DjVuPort *src, DjVuPort *dst); + + /** The opposite of \Ref{add_route}(). Removes the association + between #src# and #dst# */ + void del_route(const DjVuPort *src, DjVuPort *dst); + + /** Copies all incoming and outgoing routes from #src# to + #dst#. This function should be called when a \Ref{DjVuPort} is + copied, if you want to preserve the connectivity. */ + void copy_routes(DjVuPort *dst, const DjVuPort *src); + + /** Returns a smart pointer to the port if #port# is a valid pointer + to an existing #DjVuPort#. Returns a null pointer otherwise. */ + GP<DjVuPort> is_port_alive(DjVuPort *port); + + /** Assigns one more {\em alias} for the specified \Ref{DjVuPort}. + {\em Aliases} are names, which can be used later to retrieve this + \Ref{DjVuPort}, if it still exists. Any \Ref{DjVuPort} may have + more than one {\em alias}. But every {\em alias} must correspond + to only one \Ref{DjVuPort}. Thus, if the specified alias is + already associated with another port, this association will be + removed. */ + void add_alias(const DjVuPort * port, const GUTF8String &alias); + + /** Removes all the aliases */ + static void clear_all_aliases(void); + + /** Removes all aliases associated with the given \Ref{DjVuPort}. */ + void clear_aliases(const DjVuPort * port); + + /** Returns \Ref{DjVuPort} associated with the given #alias#. If nothing + is known about name #alias#, or the port associated with it has + already been destroyed #ZERO# pointer will be returned. */ + GP<DjVuPort> alias_to_port(const GUTF8String &name); + + /** Returns a list of \Ref{DjVuPort}s with aliases starting with + #prefix#. If no \Ref{DjVuPort}s have been found, empty + list is returned. */ + GPList<DjVuPort> prefix_to_ports(const GUTF8String &prefix); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns non-empty \Ref{GURL}. */ + virtual GURL id_to_url(const DjVuPort * source, const GUTF8String &id); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns non-zero pointer to + \Ref{DjVuFile}. */ + virtual GP<DjVuFile> id_to_file(const DjVuPort * source, const GUTF8String &id); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns non-zero \Ref{DataPool}. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /** Computes destination list for #source# and calls the corresponding. + function in each of the ports from the destination starting from + the closest until one of them returns 1. */ + virtual bool notify_error(const DjVuPort * source, const GUTF8String &msg); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns 1. */ + virtual bool notify_status(const DjVuPort * source, const GUTF8String &msg); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_redisplay(const class DjVuImage * source); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_relayout(const class DjVuImage * source); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_chunk_done(const DjVuPort * source, const GUTF8String &name); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_file_flags_changed(const class DjVuFile * source, + long set_mask, long clr_mask); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_doc_flags_changed(const class DjVuDocument * source, + long set_mask, long clr_mask); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_decode_progress(const DjVuPort * source, float done); + +private: + // We use these 'void *' to minimize template instantiations. + friend class DjVuPort; + GCriticalSection map_lock; + GMap<const void *, void *> route_map; // GMap<DjVuPort *, GList<DjVuPort *> *> + GMap<const void *, void *> cont_map; // GMap<DjVuPort *, DjVuPort *> + GMap<GUTF8String, const void *> a2p_map; // GMap<GUTF8String, DjVuPort *> + void add_to_closure(GMap<const void*, void*> & set, + const DjVuPort *dst, int distance); + void compute_closure(const DjVuPort *src, GPList<DjVuPort> &list, + bool sorted=false); +}; + + +inline bool +DjVuPort::inherits(const GUTF8String &class_name) const +{ + return (class_name == "DjVuPort"); +} + +inline bool +DjVuSimplePort::inherits(const GUTF8String &class_name) const +{ + return + (class_name == "DjVuSimplePort") || DjVuPort::inherits(class_name); +} + +inline bool +DjVuMemoryPort::inherits(const GUTF8String &class_name) const +{ + return + (class_name == "DjVuMemoryPort") || DjVuPort::inherits(class_name); +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuText.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuText.cpp new file mode 100644 index 00000000..b359df41 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuText.cpp @@ -0,0 +1,971 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuText.cpp,v 1.10 2004/07/07 19:23:36 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuText.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "debug.h" +#include <ctype.h> + + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +#ifdef min +#undef min +#endif +template<class TYPE> +static inline TYPE min(TYPE a,TYPE b) { return (a<b)?a:b; } + +//*************************************************************************** +//******************************** DjVuTXT ********************************** +//*************************************************************************** + +const char DjVuTXT::end_of_column = 013; // VT: Vertical Tab +const char DjVuTXT::end_of_region = 035; // GS: Group Separator +const char DjVuTXT::end_of_paragraph = 037; // US: Unit Separator +const char DjVuTXT::end_of_line = 012; // LF: Line Feed + +const int DjVuTXT::Zone::version = 1; + +DjVuTXT::Zone::Zone() + : ztype(DjVuTXT::PAGE), text_start(0), text_length(0), zone_parent(0) +{ +} + +DjVuTXT::Zone * +DjVuTXT::Zone::append_child() +{ + Zone empty; + empty.ztype = ztype; + empty.text_start = 0; + empty.text_length = 0; + empty.zone_parent=this; + children.append(empty); + return & children[children.lastpos()]; +} + +void +DjVuTXT::Zone::cleartext() +{ + text_start = 0; + text_length = 0; + for (GPosition i=children; i; ++i) + children[i].cleartext(); +} + +void +DjVuTXT::Zone::normtext(const char *instr, GUTF8String &outstr) +{ + if (text_length == 0) + { + // Descend collecting text below + text_start = outstr.length(); + for (GPosition i=children; i; ++i) + children[i].normtext(instr, outstr); + text_length = outstr.length() - text_start; + // Ignore empty zones + if (text_length == 0) + return; + } + else + { + // Collect text at this level + int new_start = outstr.length(); + outstr = outstr + GUTF8String(instr+text_start, text_length); + text_start = new_start; + // Clear textual information on lower level nodes + for (GPosition i=children; i; ++i) + children[i].cleartext(); + } + // Determine standard separator + char sep; + switch (ztype) + { + case COLUMN: + sep = end_of_column; break; + case REGION: + sep = end_of_region; break; + case PARAGRAPH: + sep = end_of_paragraph; break; + case LINE: + sep = end_of_line; break; + case WORD: + sep = ' '; break; + default: + return; + } + // Add separator if not present yet. + if (outstr[text_start+text_length-1] != sep) + { + outstr = outstr + GUTF8String(&sep, 1); + text_length += 1; + } +} + +unsigned int +DjVuTXT::Zone::memuse() const +{ + int memuse = sizeof(*this); + for (GPosition i=children; i; ++i) + memuse += children[i].memuse(); + return memuse; +} + + +#ifndef NEED_DECODER_ONLY +void +DjVuTXT::Zone::encode( + const GP<ByteStream> &gbs, const Zone * parent, const Zone * prev) const +{ + ByteStream &bs=*gbs; + // Encode type + bs.write8(ztype); + + // Modify text_start and bounding rectangle based on the context + // (whether there is a previous non-zero same-level-child or parent) + int start=text_start; + int x=rect.xmin, y=rect.ymin; + int width=rect.width(), height=rect.height(); + if (prev) + { + if (ztype==PAGE || ztype==PARAGRAPH || ztype==LINE) + { + // Encode offset from the lower left corner of the previous + // child in the coord system in that corner with x to the + // right and y down + x=x-prev->rect.xmin; + y=prev->rect.ymin-(y+height); + } else // Either COLUMN or WORD or CHARACTER + { + // Encode offset from the lower right corner of the previous + // child in the coord system in that corner with x to the + // right and y up + x=x-prev->rect.xmax; + y=y-prev->rect.ymin; + } + start-=prev->text_start+prev->text_length; + } else if (parent) + { + // Encode offset from the upper left corner of the parent + // in the coord system in that corner with x to the right and y down + x=x-parent->rect.xmin; + y=parent->rect.ymax-(y+height); + start-=parent->text_start; + } + // Encode rectangle + bs.write16(0x8000+x); + bs.write16(0x8000+y); + bs.write16(0x8000+width); + bs.write16(0x8000+height); + // Encode text info + bs.write16(0x8000+start); + bs.write24(text_length); + // Encode number of children + bs.write24(children.size()); + + const Zone * prev_child=0; + // Encode all children + for (GPosition i=children; i; ++i) + { + children[i].encode(gbs, this, prev_child); + prev_child=&children[i]; + } +} +#endif + +void +DjVuTXT::Zone::decode(const GP<ByteStream> &gbs, int maxtext, + const Zone * parent, const Zone * prev) +{ + ByteStream &bs=*gbs; + // Decode type + ztype = (ZoneType) bs.read8(); + if ( ztype<PAGE || ztype>CHARACTER ) + G_THROW( ERR_MSG("DjVuText.corrupt_text") ); + + // Decode coordinates + int x=(int) bs.read16()-0x8000; + int y=(int) bs.read16()-0x8000; + int width=(int) bs.read16()-0x8000; + int height=(int) bs.read16()-0x8000; + + // Decode text info + text_start = (int) bs.read16()-0x8000; +// int start=text_start; + text_length = bs.read24(); + if (prev) + { + if (ztype==PAGE || ztype==PARAGRAPH || ztype==LINE) + { + x=x+prev->rect.xmin; + y=prev->rect.ymin-(y+height); + } else // Either COLUMN or WORD or CHARACTER + { + x=x+prev->rect.xmax; + y=y+prev->rect.ymin; + } + text_start+=prev->text_start+prev->text_length; + } else if (parent) + { + x=x+parent->rect.xmin; + y=parent->rect.ymax-(y+height); + text_start+=parent->text_start; + } + rect=GRect(x, y, width, height); + // Get children size + int size = bs.read24(); + + // Checks + if (rect.isempty() || text_start<0 || text_start+text_length>maxtext ) + G_THROW( ERR_MSG("DjVuText.corrupt_text") ); + + // Process children + const Zone * prev_child=0; + children.empty(); + while (size-- > 0) + { + Zone *z = append_child(); + z->decode(gbs, maxtext, this, prev_child); + prev_child=z; + } +} + +void +DjVuTXT::normalize_text() +{ + GUTF8String newtextUTF8; + page_zone.normtext( (const char*)textUTF8, newtextUTF8 ); + textUTF8 = newtextUTF8; +} + +int +DjVuTXT::has_valid_zones() const +{ + if (!textUTF8) + return false; + if (page_zone.children.isempty() || page_zone.rect.isempty()) + return false; + return true; +} + + +#ifndef NEED_DECODER_ONLY +void +DjVuTXT::encode(const GP<ByteStream> &gbs) const +{ + ByteStream &bs=*gbs; + if (! textUTF8 ) + G_THROW( ERR_MSG("DjVuText.no_text") ); + // Encode text + int textsize = textUTF8.length(); + bs.write24( textsize ); + bs.writall( (void*)(const char*)textUTF8, textsize ); + // Encode zones + if (has_valid_zones()) + { + bs.write8(Zone::version); + page_zone.encode(gbs); + } +} +#endif + +void +DjVuTXT::decode(const GP<ByteStream> &gbs) +{ + ByteStream &bs=*gbs; + // Read text + textUTF8.empty(); + int textsize = bs.read24(); + char *buffer = textUTF8.getbuf(textsize); + int readsize = bs.read(buffer,textsize); + buffer[readsize] = 0; + if (readsize < textsize) + G_THROW( ERR_MSG("DjVuText.corrupt_chunk") ); + // Try reading zones + unsigned char version; + if ( bs.read( (void*) &version, 1 ) == 1) + { + if (version != Zone::version) + G_THROW( ERR_MSG("DjVuText.bad_version") "\t" + GUTF8String(version) ); + page_zone.decode(gbs, textsize); + } +} + +GP<DjVuTXT> +DjVuTXT::copy(void) const +{ + return new DjVuTXT(*this); +} + + +static inline bool +intersects_zone(GRect box, const GRect &zone) +{ + return + ((box.xmin < zone.xmin) + ?(box.xmax >= zone.xmin) + :(box.xmin <= zone.xmax)) + &&((box.ymin < zone.ymin) + ?(box.ymax >= zone.ymin) + :(box.ymin <= zone.ymax)); +} + +void +DjVuTXT::Zone::get_text_with_rect(const GRect &box, + int &string_start, int &string_end) const +{ + GPosition pos=children; + if(pos?box.contains(rect):intersects_zone(box,rect)) + { + const int text_end=text_start+text_length; + if(string_start == string_end) + { + string_start=text_start; + string_end=text_end; + }else + { + if (string_end < text_end) + string_end=text_end; + if(text_start < string_start) + string_start=text_start; + } + }else if(pos&&intersects_zone(box,rect)) + { + do + { + children[pos].get_text_with_rect(box,string_start,string_end); + } while(++pos); + } +} + +void +DjVuTXT::Zone::find_zones(GList<Zone *> &list, + const int string_start, const int string_end) const +{ + const int text_end=text_start+text_length; + if(text_start >= string_start) + { + if(text_end <= string_end) + { + list.append(const_cast<Zone *>(this)); + } + else if(text_start < string_end) + { + if (children.size()) + for (GPosition pos=children; pos; ++pos) + children[pos].find_zones(list,string_start,string_end); + else + list.append(const_cast<Zone *>(this)); + } + } + else if( text_end > string_start) + { + if (children.size()) + for (GPosition pos=children; pos; ++pos) + children[pos].find_zones(list,string_start,string_end); + else + list.append(const_cast<Zone *>(this)); + } +} + +void +DjVuTXT::Zone::get_smallest(GList<GRect> &list) const +{ + GPosition pos=children; + if(pos) + { + do { + children[pos].get_smallest(list); + } while (++pos); + } + else + { + list.append(rect); + } +} + +void +DjVuTXT::Zone::get_smallest(GList<GRect> &list, const int padding) const +{ + GPosition pos=children; + if(pos) + { + do { + children[pos].get_smallest(list,padding); + } while (++pos); + } + else if(zone_parent && zone_parent->ztype >= PARAGRAPH) + { + const GRect &xrect=zone_parent->rect; + if(xrect.height() < xrect.width()) + { + list.append(GRect(rect.xmin-padding,xrect.ymin-padding,rect.width() + +2*padding,xrect.height()+2*padding)); + } + else + { + list.append(GRect(xrect.xmin-padding,rect.ymin-padding,xrect.width() + +2*padding,rect.height()+2*padding)); + } + } + else + { + list.append(GRect(rect.xmin-padding,rect.ymin-padding,rect.width() + +2*padding,rect.height()+2*padding)); + } +} + +void +DjVuTXT::get_zones(int zone_type, const Zone *parent, + GList<Zone *> & zone_list) const + // get all the zones of type zone_type under zone node parent +{ + // search all branches under parent + const Zone *zone=parent; + for( int cur_ztype=zone->ztype; cur_ztype<zone_type; ++cur_ztype ) + { + GPosition pos; + for(pos=zone->children; pos; ++pos) + { + Zone *zcur=(Zone *)&zone->children[pos]; + if ( zcur->ztype == zone_type ) + { + GPosition zpos=zone_list; + if ( !zone_list.search(zcur,zpos) ) + zone_list.append(zcur); + } + else if ( zone->children[pos].ztype < zone_type ) + get_zones(zone_type, &zone->children[pos], zone_list); + } + } +} + +GList<GRect> +DjVuTXT::find_text_with_rect(const GRect &box, GUTF8String &text, + const int padding) const +{ + GList<GRect> retval; + int text_start=0; + int text_end=0; + page_zone.get_text_with_rect(box,text_start,text_end); + if(text_start != text_end) + { + GList<Zone *> zones; + page_zone.find_zones(zones,text_start,text_end); + GPosition pos=zones; + if(pos) + { + do + { + if(padding >= 0) + { + zones[pos]->get_smallest(retval,padding); + }else + { + zones[pos]->get_smallest(retval); + } + } while(++pos); + } + } + text=textUTF8.substr(text_start,text_end-text_start); + return retval; +} + + +GList<DjVuTXT::Zone *> +DjVuTXT::find_text_in_rect(GRect target_rect, GUTF8String &text) const + // returns a list of zones of type WORD in the nearest/selected paragraph +{ + GList<Zone *> zone_list; + GList<Zone *> lines; + + get_zones((int)PARAGRAPH, &page_zone, zone_list); + // it's possible that no paragraph structure exists for reasons that + // 1) ocr engine is not capable 2) file was modified by user. In such case, + // we can only make a rough guess, i.e., select all the lines intersected with + // target_rect + if (zone_list.isempty()) + { + get_zones((int)LINE, &page_zone, zone_list); + GPosition pos; + for(pos=zone_list; pos; ++pos) + { + GRect rect=zone_list[pos]->rect; + int h0=rect.height()/2; + if(rect.intersect(rect,target_rect) && rect.height()>h0) + lines.append(zone_list[pos]); + } + } else + { + GPosition pos, pos_sel=zone_list; + float ar=0; + for(pos=zone_list; pos; ++pos) + { + GRect rect=zone_list[pos]->rect; + int area=rect.area(); + if (rect.intersect(rect, target_rect)) + { + float ftmp=rect.area()/(float)area; + if ( !ar || ar<ftmp ) + { + ar=ftmp; + pos_sel=pos; + } + } + } + Zone *parag = 0; + if ( ar>0 ) parag=zone_list[pos_sel]; + zone_list.empty(); + if ( ar>0 ) + { + get_zones((int)LINE, parag, zone_list); + if ( !zone_list.isempty() ) + { + for(GPosition pos=zone_list; pos; ++pos) + { + GRect rect=zone_list[pos]->rect; + int h0=rect.height()/2; + if(rect.intersect(rect,target_rect) && rect.height()>h0) + lines.append(zone_list[pos]); + } + } + } + } + + zone_list.empty(); + if (!lines.isempty()) + { + int i=1, lsize=lines.size(); + + GList<Zone *> words; + for (GPosition pos=lines; pos; ++pos, ++i) + { + words.empty(); + get_zones((int)WORD, lines[pos], words); + + if ( lsize==1 ) + { + for(GPosition p=words;p;++p) + { + GRect rect=words[p]->rect; + if(rect.intersect(rect,target_rect)) + //if (target_rect.contains(words[p]->rect)) + zone_list.append(words[p]); + } + } else + { + if (i==1) + { + bool start=true; + for(GPosition p=words; p; ++p) + { + if ( start ) + { + GRect rect=words[p]->rect; + if(rect.intersect(rect,target_rect)) + //if (target_rect.contains(words[p]->rect)) + { + start=false; + zone_list.append(words[p]); + } + } else + zone_list.append(words[p]); + } + } else if (i==lsize) + { + bool end=true; + for(GPosition p=words.lastpos();p;--p) + { + if ( end ) + { + GRect rect=words[p]->rect; + if(rect.intersect(rect,target_rect)) + //if(target_rect.contains(words[p]->rect) ) + { + end=false; + zone_list.append(words[p]); + } + } else + zone_list.append(words[p]); + } + } + + if (i!=1 && i!=lsize ) + { + for(GPosition p=words;p;++p) + zone_list.append(words[p]); + } + } + } + } + + return zone_list; +} + +unsigned int +DjVuTXT::get_memory_usage() const +{ + return sizeof(*this) + textUTF8.length() + page_zone.memuse() - sizeof(page_zone); +} + + + +//*************************************************************************** +//******************************** DjVuText ********************************* +//*************************************************************************** + +void +DjVuText::decode(const GP<ByteStream> &gbs) +{ + GUTF8String chkid; + GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + while( iff.get_chunk(chkid) ) + { + if (chkid == "TXTa") + { + if (txt) + G_THROW( ERR_MSG("DjVuText.dupl_text") ); + txt = DjVuTXT::create(); + txt->decode(iff.get_bytestream()); + } + else if (chkid == "TXTz") + { + if (txt) + G_THROW( ERR_MSG("DjVuText.dupl_text") ); + txt = DjVuTXT::create(); + const GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream()); + txt->decode(gbsiff); + } + // Add decoding of other chunks here + iff.close_chunk(); + } +} + +void +DjVuText::encode(const GP<ByteStream> &gbs) +{ + if (txt) + { + const GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + iff.put_chunk("TXTz"); + { + GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50); + txt->encode(gbsiff); + } + iff.close_chunk(); + } + // Add encoding of other chunks here +} + + +GP<DjVuText> +DjVuText::copy(void) const +{ + GP<DjVuText> text= new DjVuText; + // Copy any primitives (if any) + *text=*this; + // Copy each substructure + if (txt) + text->txt = txt->copy(); + return text; +} + +static GUTF8String +indent ( int spaces) +{ + GUTF8String ret; + for( int i = 0 ; i < spaces ; i++ ) + ret += ' '; + return ret; +} + +static const char *tags[8]= +{ 0, + "HIDDENTEXT", + "PAGECOLUMN", + "REGION", + "PARAGRAPH", + "LINE", + "WORD", + "CHARACTER" }; +static const int tags_size=sizeof(tags)/sizeof(const char *); + +static GUTF8String +start_tag(const DjVuTXT::ZoneType zone) +{ + GUTF8String retval; + if((tags_size > (int)zone)&&((int)zone > 0)) + { + switch (zone) + { + case DjVuTXT::CHARACTER: + retval="<"+GUTF8String(tags[zone])+">"; + break; + case DjVuTXT::WORD: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+">"; + break; + default: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+">\n"; + break; + } + } + return retval; +} + +static GUTF8String +start_tag(const DjVuTXT::ZoneType zone, const GUTF8String &attributes) +{ + GUTF8String retval; + if((tags_size > (int)zone)&&((int)zone > 0)) + { + switch (zone) + { + case DjVuTXT::CHARACTER: + retval="<"+GUTF8String(tags[zone])+" "+attributes+">"; + break; + case DjVuTXT::WORD: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+" "+attributes+">"; + break; + default: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+" "+attributes+">\n"; + break; + } + } + return retval; +} + +static inline GUTF8String +start_tag(const int layer) +{ + return start_tag((const DjVuTXT::ZoneType)layer); +} + + +static GUTF8String +end_tag(const DjVuTXT::ZoneType zone) +{ + GUTF8String retval; + if((tags_size > (int)zone)&&((int)zone >= 0)) + { + switch (zone) + { + case DjVuTXT::CHARACTER: + retval="</"+GUTF8String(tags[zone])+">"; + break; + case DjVuTXT::WORD: + retval="</"+GUTF8String(tags[zone])+">\n"; + break; + default: + retval=indent(2*(int)zone+2)+"</"+tags[zone]+">\n"; + break; + } + } + return retval; +} + +static inline GUTF8String +end_tag(const int layer) +{ + return end_tag((const DjVuTXT::ZoneType)layer); +} + +static GUTF8String +tolayer(int &layer, const DjVuTXT::ZoneType next_layer) +{ + GUTF8String retval; + for( ;layer < (int)next_layer;layer++ ) + { + retval+=start_tag(layer); + } + while (layer > (int)next_layer ) + { + retval+=end_tag(--layer); + } + return retval; +} + +static void +writeText( ByteStream & str_out, + const GUTF8String &textUTF8, + const DjVuTXT::Zone &zone, + const int WindowHeight ); + +static void +writeText( ByteStream & str_out, + const GUTF8String &textUTF8, + const DjVuTXT::ZoneType zlayer, + const GList<DjVuTXT::Zone> &children, + const int WindowHeight ) +{ +// assert( txt->has_valid_zones() ); +// DEBUG_MSG( "--zonetype=" << txt->page_zone.ztype << "\n" ); + + // Beginning tags for missing layers + int layer=(int)zlayer; + // Output the next layer + for(GPosition pos=children ; pos ; ++pos ) + { + str_out.writestring(tolayer(layer,children[pos].ztype)); + writeText( str_out, + textUTF8, + children[pos], + WindowHeight ); + } + str_out.writestring(tolayer(layer,zlayer)); +} + +static void +writeText( ByteStream & str_out, + const GUTF8String &textUTF8, + const DjVuTXT::Zone &zone, + const int WindowHeight ) +{ +// DEBUG_MSG( "--zonetype=" << zone.ztype << "\n" ); + + const GUTF8String xindent(indent( 2 * zone.ztype + 2 )); + GPosition pos=zone.children; + // Build attribute string + if( ! pos ) + { + GUTF8String coords; + coords.format("coords=\"%d,%d,%d,%d\"", + zone.rect.xmin, WindowHeight - 1 - zone.rect.ymin, + zone.rect.xmax, WindowHeight - 1 - zone.rect.ymax); + const int start=zone.text_start; + const int end=textUTF8.firstEndSpace(start,zone.text_length); + str_out.writestring(start_tag(zone.ztype,coords)); + str_out.writestring(textUTF8.substr(start,end-start).toEscaped()); + str_out.writestring(end_tag(zone.ztype)); + } else + { + writeText(str_out,textUTF8,zone.ztype,zone.children,WindowHeight); + } +} + +void +DjVuTXT::writeText(ByteStream &str_out,const int height) const +{ + if(has_valid_zones()) + { + ::writeText(str_out,textUTF8,DjVuTXT::PAGE,page_zone.children,height); + }else + { + str_out.writestring(start_tag(DjVuTXT::PAGE)); + str_out.writestring(end_tag(DjVuTXT::PAGE)); + } +} + +void +DjVuText::writeText(ByteStream &str_out,const int height) const +{ + if(txt) + { + txt->writeText(str_out,height); + }else + { + str_out.writestring("<"+GUTF8String(tags[DjVuTXT::PAGE])+"/>\n"); + } + +} +GUTF8String +DjVuTXT::get_xmlText(const int height) const +{ + GP<ByteStream> gbs(ByteStream::create()); + ByteStream &bs=*gbs; + writeText(bs,height); + bs.seek(0L); + return bs.getAsUTF8(); +} + +GUTF8String +DjVuText::get_xmlText(const int height) const +{ + GUTF8String retval; + if(txt) + { + retval=txt->get_xmlText(height); + }else + { + retval="<"+GUTF8String(tags[DjVuTXT::PAGE])+"/>\n"; + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuText.h b/kviewshell/plugins/djvu/libdjvu/DjVuText.h new file mode 100644 index 00000000..61ee3667 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuText.h @@ -0,0 +1,281 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuText.h,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUTEXT_H +#define _DJVUTEXT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + + +/** @name DjVuText.h + + Files #"DjVuText.h"# and #"DjVuText.cpp"# implement the mechanism for + text in DjVuImages. + + This file implements annotations understood by the DjVu plugins + and encoders. + + + using: contents of #TXT*# chunks. + + Contents of the #FORM:TEXT# should be passed to \Ref{DjVuText::decode}() + for parsing, which initializes \Ref{DjVuText::TXT} + and fills them with decoded data. + @memo Implements support for DjVuImage hidden text. + @author Andrei Erofeev <eaf@geocities.com> + @version + #$Id: DjVuText.h,v 1.10 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "GMapAreas.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class ByteStream; + +// -------- DJVUTXT -------- + +/** Description of the text contained in a DjVu page. This class contains the + textual data for the page. It describes the text as a hierarchy of zones + corresponding to page, column, region, paragraph, lines, words, etc... + The piece of text associated with each zone is represented by an offset + and a length describing a segment of a global UTF8 encoded string. */ + +class DjVuTXT : public GPEnabled +{ +protected: + DjVuTXT(void) {} +public: + /// Default creator + static GP<DjVuTXT> create(void) {return new DjVuTXT();} + + /** These constants are used to tell what a zone describes. + This can be useful for a copy/paste application. + The deeper we go into the hierarchy, the higher the constant. */ + enum ZoneType { PAGE=1, COLUMN=2, REGION=3, PARAGRAPH=4, + LINE=5, WORD=6, CHARACTER=7 }; + /** Data structure representing document textual components. + The text structure is represented by a hierarchy of rectangular zones. */ + class Zone + { + public: + Zone(); + /** Type of the zone. */ + enum ZoneType ztype; + /** Rectangle spanned by the zone */ + GRect rect; + /** Position of the zone text in string #textUTF8#. */ + int text_start; + /** Length of the zone text in string #textUTF8#. */ + int text_length; + /** List of children zone. */ + GList<Zone> children; + /** Appends another subzone inside this zone. The new zone is initialized + with an empty rectangle, empty text, and has the same type as this + zone. */ + Zone *append_child(); + /** Find the text_start and text_end indicated by the given box. */ + void get_text_with_rect(const GRect &box, + int &string_start,int &string_end ) const; + /** Find the zones used by the specified string and append them to the list. */ + void find_zones(GList<Zone *> &list, + const int string_start, const int string_end) const; + /** Finds the smallest rectangles and appends them to the list. */ + void get_smallest(GList<GRect> &list) const; + /** Finds the smallest rectangles and appends them to the list after + padding the smallest unit to fit width or height for the parent rectangle + and adding the number of specified pixels. */ + void get_smallest(GList<GRect> &list,const int padding) const; + /// Find out this Zone's parent. + const Zone *get_parent(void) const; + private: + friend class DjVuTXT; + const Zone *zone_parent; + void cleartext(); + void normtext(const char *instr, GUTF8String &outstr); + unsigned int memuse() const; + static const int version; + void encode(const GP<ByteStream> &bs, + const Zone * parent=0, const Zone * prev=0) const; + void decode(const GP<ByteStream> &bs, int maxtext, + const Zone * parent=0, const Zone * prev=0); + }; + /** Textual data for this page. + The content of this string is encoded using the UTF8 code. + This code corresponds to ASCII for the first 127 characters. + Columns, regions, paragraph and lines are delimited by the following + control character: + \begin{tabular}{lll} + {\bf Name} & {\bf Octal} & {\bf Ascii name} \\\hline\\ + {\tt DjVuText::end_of_column} & 013 & VT, Vertical Tab \\ + {\tt DjVuText::end_of_region} & 035 & GS, Group Separator \\ + {\tt DjVuText::end_of_paragraph} & 037 & US, Unit Separator \\ + {\tt DjVuText::end_of_line} & 012 & LF: Line Feed + \end{tabular} */ + GUTF8String textUTF8; + static const char end_of_column ; // VT: Vertical Tab + static const char end_of_region ; // GS: Group Separator + static const char end_of_paragraph ; // US: Unit Separator + static const char end_of_line ; // LF: Line Feed + /** Main zone in the document. + This zone represent the page. */ + Zone page_zone; + /** Tests whether there is a meaningful zone hierarchy. */ + int has_valid_zones() const; + /** Normalize textual data. Assuming that a zone hierarchy has been built + and represents the reading order. This function reorganizes the string + #textUTF8# by gathering the highest level text available in the zone + hierarchy. The text offsets and lengths are recomputed for all the + zones in the hierarchy. Separators are inserted where appropriate. */ + void normalize_text(); + /** Encode data for a TXT chunk. */ + void encode(const GP<ByteStream> &bs) const; + /** Decode data from a TXT chunk. */ + void decode(const GP<ByteStream> &bs); + /** Returns a copy of this object. */ + GP<DjVuTXT> copy(void) const; + /// Write XML formated text. + void writeText(ByteStream &bs,const int height) const; + /// Get XML formatted text. + GUTF8String get_xmlText(const int height) const; + /** Find the text specified by the rectangle. */ + GList<Zone*> find_text_in_rect(GRect target_rect, GUTF8String &text) const; + /** Find the text specified by the rectangle. */ + GList<GRect> find_text_with_rect(const GRect &box, GUTF8String &text, const int padding=0) const; + /** Get all zones of zone type zone_type under node parent. + zone_list contains the return value. */ + void get_zones(int zone_type, const Zone *parent, GList<Zone *> & zone_list) const; + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + unsigned int get_memory_usage() const; +}; + +inline const DjVuTXT::Zone * +DjVuTXT::Zone::get_parent(void) const +{ + return zone_parent; +} + + +class DjVuText : public GPEnabled +{ +protected: + DjVuText(void) {} +public: + /// Default creator. + static GP<DjVuText> create(void) {return new DjVuText();} + + /** Decodes a sequence of annotation chunks and merges contents of every + chunk with previously decoded information. This function + should be called right after applying \Ref{IFFByteStream::get_chunk}() + to data from #FORM:TEXT#. */ + void decode(const GP<ByteStream> &bs); + + /** Encodes all annotations back into a sequence of chunks to be put + inside a #FORM:TEXT#. */ + void encode(const GP<ByteStream> &bs); + + /// Returns a copy of this object + GP<DjVuText> copy(void) const; + + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + inline unsigned int get_memory_usage() const; + + /// Write XML formated text. + void writeText(ByteStream &bs,const int height) const; + + /// Get XML formatted text. + GUTF8String get_xmlText(const int height) const; + + GP<DjVuTXT> txt; +private: // dummy stuff + static void decode(ByteStream *); + static void encode(ByteStream *); +}; + +//@} + +inline unsigned int +DjVuText::get_memory_usage() const +{ + return (txt)?(txt->get_memory_usage()):0; +} + + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp new file mode 100644 index 00000000..beaa01bc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp @@ -0,0 +1,2582 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002-2003 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuToPS.cpp,v 1.23 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuToPS.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "DjVuImage.h" +#include "DjVuText.h" +#include "DataPool.h" +#include "IW44Image.h" +#include "JB2Image.h" +#include "GBitmap.h" +#include "GPixmap.h" +#include "debug.h" +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <math.h> +#ifdef UNIX +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const size_t ps_string_size=15000; + +// *************************************************************************** +// ****************************** Options ************************************ +// *************************************************************************** + +DjVuToPS::Options:: +Options(void) +: format(PS), + level(2), + orientation(AUTO), + mode(COLOR), + zoom(0), + color(true), + calibrate(true), + text(false), + gamma((double)2.2), + copies(1), + frame(false), + cropmarks(false), + bookletmode(OFF), + bookletmax(0), + bookletalign(0), + bookletfold(18), + bookletxfold(200) +{} + +void +DjVuToPS::Options:: +set_format(Format xformat) +{ + if (xformat != EPS && xformat != PS) + G_THROW(ERR_MSG("DjVuToPS.bad_format")); + format=xformat; +} + +void +DjVuToPS::Options:: +set_level(int xlevel) +{ + if (xlevel<1 || xlevel>3) + G_THROW(ERR_MSG("DjVuToPS.bad_level") + + GUTF8String("\t") + GUTF8String(xlevel)); + level=xlevel; +} + +void +DjVuToPS::Options:: +set_orientation(Orientation xorientation) +{ + if (xorientation!=PORTRAIT && + xorientation!=LANDSCAPE && + xorientation!=AUTO ) + G_THROW(ERR_MSG("DjVuToPS.bad_orient")); + orientation=xorientation; +} + +void +DjVuToPS::Options:: +set_mode(Mode xmode) +{ + if (xmode!=COLOR && xmode!=FORE && xmode!=BACK && xmode!=BW) + G_THROW(ERR_MSG("DjVuToPS.bad_mode")); + mode=xmode; +} + +void +DjVuToPS::Options:: +set_zoom(int xzoom) +{ + if (xzoom!=0 && !(xzoom>=5 && xzoom<=999)) + G_THROW(ERR_MSG("DjVuToPS.bad_zoom")); + zoom=xzoom; +} + +void +DjVuToPS::Options:: +set_color(bool xcolor) +{ + color=xcolor; +} + +void +DjVuToPS::Options:: +set_sRGB(bool xcalibrate) +{ + calibrate=xcalibrate; +} + +void +DjVuToPS::Options:: +set_gamma(double xgamma) +{ + if (xgamma<(double)(0.3-0.0001) || xgamma>(double)(5.0+0.0001)) + G_THROW(ERR_MSG("DjVuToPS.bad_gamma")); + gamma=xgamma; +} + +void +DjVuToPS::Options:: +set_copies(int xcopies) +{ + if (xcopies<=0) + G_THROW(ERR_MSG("DjVuToPS.bad_number")); + copies=xcopies; +} + +void +DjVuToPS::Options:: +set_frame(bool xframe) +{ + frame=xframe; +} + +void +DjVuToPS::Options:: +set_cropmarks(bool xmarks) +{ + cropmarks=xmarks; +} + +void +DjVuToPS::Options:: +set_text(bool xtext) +{ + text=xtext; +} + +void +DjVuToPS::Options:: +set_bookletmode(BookletMode m) +{ + bookletmode = m; +} + +void +DjVuToPS::Options:: +set_bookletmax(int m) +{ + bookletmax = 0; + if (m > 0) + bookletmax = (m+3)/4; + bookletmax *= 4; +} + +void +DjVuToPS::Options:: +set_bookletalign(int m) +{ + bookletalign = m; +} + +void +DjVuToPS::Options:: +set_bookletfold(int fold, int xfold) +{ + if (fold >= 0) + bookletfold = fold; + if (xfold >= 0) + bookletxfold = xfold; +} + + +// *************************************************************************** +// ******************************* DjVuToPS ********************************** +// *************************************************************************** + +static char bin2hex[256][2]; + +DjVuToPS::DjVuToPS(void) +{ + DEBUG_MSG("DjVuToPS::DjVuToPS(): initializing...\n"); + DEBUG_MAKE_INDENT(3); + DEBUG_MSG("Initializing dig2hex[]\n"); + // Creating tables for bin=>text translation + static char * dig2hex="0123456789ABCDEF"; + int i; + for(i=0;i<256;i++) + { + bin2hex[i][0]=dig2hex[i/16]; + bin2hex[i][1]=dig2hex[i%16]; + } + refresh_cb=0; + refresh_cl_data=0; + prn_progress_cb=0; + prn_progress_cl_data=0; + dec_progress_cb=0; + dec_progress_cl_data=0; + info_cb=0; + info_cl_data=0; +} + +#ifdef __GNUC__ +static void +write(ByteStream &str, const char *format, ...) +__attribute__((format (printf, 2, 3))); +#endif + +static void +write(ByteStream &str, const char *format, ...) +{ + /* Will output the formated string to the specified \Ref{ByteStream} + like #fprintf# would do it for a #FILE#. */ + va_list args; + va_start(args, format); + GUTF8String tmp; + tmp.vformat(format, args); + str.writall((const char *) tmp, tmp.length()); +} + +// ************************* DOCUMENT LEVEL ********************************* + +void +DjVuToPS:: +store_doc_prolog(ByteStream &str, int pages, int dpi, GRect *grect) +{ + /* Will store the {\em document prolog}, which is basically a + block of document-level comments in PS DSC 3.0 format. + @param str Stream where PostScript data should be written + @param pages Total number of pages + @param dpi (EPS mode only) + @param grect (EPS mode only) */ + DEBUG_MSG("storing the document prolog\n"); + DEBUG_MAKE_INDENT(3); + if (options.get_format()==Options::EPS) + write(str, + "%%!PS-Adobe-3.0 EPSF 3.0\n" + "%%%%BoundingBox: 0 0 %d %d\n", + (grect->width()*100+dpi-1)/dpi, + (grect->height()*100+dpi-1)/dpi ); + else + write(str, "%%!PS-Adobe-3.0\n"); + write(str, + "%%%%Title: DjVu PostScript document\n" + "%%%%Copyright: Copyright (c) 1998-1999 AT&T\n" + "%%%%Creator: DjVu (code by Andrei Erofeev)\n" + "%%%%DocumentData: Clean7Bit\n"); + // Date + time_t tm=time(0); + write(str, "%%%%CreationDate: %s", ctime(&tm)); + // For +#ifdef UNIX + passwd *pswd = getpwuid(getuid()); + if (pswd) + { + char *s = strchr(pswd->pw_gecos, ','); + if (s) + *s = 0; + s = 0; + if (pswd->pw_gecos && strlen(pswd->pw_gecos)) + s = pswd->pw_gecos; + else if (pswd->pw_name && strlen(pswd->pw_name)) + s = pswd->pw_name; + if (s) + write(str, "%%%%For: %s\n", s); + } +#endif + // Language + write(str, "%%%%LanguageLevel: %d\n", options.get_level()); + if (options.get_level()<2 && options.get_color()) + write(str, "%%%%Extensions: CMYK\n"); + // Pages + write(str, "%%%%Pages: %d\n",pages ); + write(str, "%%%%PageOrder: Ascend\n"); + // Orientation + if (options.get_orientation() != Options::AUTO) + write(str, "%%%%Orientation: %s\n", + options.get_orientation()==Options::PORTRAIT ? + "Portrait" : "Landscape" ); + // Requirements + if (options.get_format() == Options::PS) + { + write(str, "%%%%Requirements:"); + if (options.get_color()) + write(str, " color"); + if (options.get_copies()>1) + write(str, " numcopies(%d)", options.get_copies()); + if (options.get_level()>=2) + { + if (options.get_copies()>1) + write(str, " collate"); + if (options.get_bookletmode() == Options::RECTOVERSO) + write(str, " duplex(tumble)"); + } + write(str, "\n"); + } + // End + write(str, + "%%%%EndComments\n" + "%%%%EndProlog\n" + "\n"); +} + +void +DjVuToPS:: +store_doc_setup(ByteStream &str) +{ + /* Will store the {\em document setup}, which is a set of + PostScript commands and functions used to inspect and prepare + the PostScript interpreter environment before displaying images. */ + write(str, + "%%%%BeginSetup\n" + "/doc-origstate save def\n"); + if (options.get_level()>=2) + { + if (options.get_format() == Options::PS) + { + if (options.get_copies()>1) + write(str, + "[{\n" + "%%%%BeginFeature: NumCopies %d\n" + "<< /NumCopies %d >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n" + "[{\n" + "%%%%BeginFeature: Collate\n" + "<< /Collate true >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n", + options.get_copies(), + options.get_copies() ); + if (options.get_bookletmode()==Options::RECTOVERSO) + write(str, + "[{\n" + "%%%%BeginFeature: Duplex DuplexTumble\n" + "<< /Duplex true /Tumble true >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n"); + } + if (options.get_color()) + write(str, + "%% -- procs for reading color image\n" + "/readR () def\n" + "/readG () def\n" + "/readB () def\n" + "/ReadData {\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferR readstring pop /readR exch def\n" + " dup status { flushfile } { pop } ifelse\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferG readstring pop /readG exch def\n" + " dup status { flushfile } { pop } ifelse\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferB readstring pop /readB exch def\n" + " dup status { flushfile } { pop } ifelse\n" + "} bind def\n" + "/ReadR {\n" + " readR length 0 eq { ReadData } if\n" + " readR /readR () def\n" + "} bind def\n" + "/ReadG {\n" + " readG length 0 eq { ReadData } if\n" + " readG /readG () def\n" + "} bind def\n" + "/ReadB {\n" + " readB length 0 eq { ReadData } if\n" + " readB /readB () def\n" + "} bind def\n"); + write(str, + "%% -- procs for foreground layer\n" + "/g {gsave 0 0 0 0 5 index 5 index setcachedevice\n" + " true [1 0 0 1 0 0] 5 4 roll imagemask grestore\n" + "} bind def\n" + "/gn {gsave 0 0 0 0 6 index 6 index setcachedevice\n" + " true [1 0 0 1 0 0] 3 2 roll 5 1 roll \n" + " { 1 sub 0 index 2 add 1 index 1 add roll\n" + " } imagemask grestore pop \n" + "} bind def\n" + "/c {setcolor rmoveto glyphshow} bind def\n" + "/s {rmoveto glyphshow} bind def\n" + "/S {rmoveto gsave show grestore} bind def\n" + "/F {(Helvetica) findfont exch scalefont setfont} bind def\n" + "%% -- emulations\n" + "systemdict /rectstroke known not {\n" + " /rectstroke %% stack : x y width height \n" + " { newpath 4 2 roll moveto 1 index 0 rlineto\n" + " 0 exch rlineto neg 0 rlineto closepath stroke\n" + " } bind def } if\n" + "systemdict /rectclip known not {\n" + " /rectclip %% stack : x y width height \n" + " { newpath 4 2 roll moveto 1 index 0 rlineto\n" + " 0 exch rlineto neg 0 rlineto closepath clip\n" + " } bind def } if\n" + "%% -- color space\n" ); + if (options.get_sRGB()) + write(str, + "/DjVuColorSpace [ %s\n" + "<< /DecodeLMN [ { dup 0.03928 le {\n" + " 12.92321 div\n" + " } {\n" + " 0.055 add 1.055 div 2.4 exp\n" + " } ifelse } bind dup dup ]\n" + " /MatrixLMN [\n" + " 0.412457 0.212673 0.019334\n" + " 0.357576 0.715152 0.119192\n" + " 0.180437 0.072175 0.950301 ]\n" + " /WhitePoint [ 0.9505 1 1.0890 ] %% D65 \n" + " /BlackPoint[0 0 0] >> ] def\n", + (options.get_color()) ? "/CIEBasedABC" : "/CIEBasedA" ); + else if (options.get_color()) + write(str,"/DjVuColorSpace /DeviceRGB def\n"); + else + write(str,"/DjVuColorSpace /DeviceGray def\n"); + } + else + { + // level<2 + if (options.get_format() == Options::PS) + if (options.get_copies() > 1) + write(str,"/#copies %d def\n", options.get_copies()); + if (options.get_color()) + write(str, + "%% -- buffers for reading image\n" + "/buffer8 () def\n" + "/buffer24 () def\n" + "%% -- colorimage emulation\n" + "systemdict /colorimage known {\n" + " /ColorProc {\n" + " currentfile buffer24 readhexstring pop\n" + " } bind def\n" + " /ColorImage {\n" + " colorimage\n" + " } bind def\n" + "} {\n" + " /ColorProc {\n" + " currentfile buffer24 readhexstring pop\n" + " /data exch def /datalen data length def\n" + " /cnt 0 def\n" + " 0 1 datalen 3 idiv 1 sub {\n" + " buffer8 exch\n" + " data cnt get 20 mul /cnt cnt 1 add def\n" + " data cnt get 32 mul /cnt cnt 1 add def\n" + " data cnt get 12 mul /cnt cnt 1 add def\n" + " add add 64 idiv put\n" + " } for\n" + " buffer8 0 datalen 3 idiv getinterval\n" + " } bind def\n" + " /ColorImage {\n" + " pop pop image\n" + " } bind def\n" + "} ifelse\n"); + } // level<2 + write(str, "%%%%EndSetup\n\n"); +} + +void +DjVuToPS:: +store_doc_trailer(ByteStream &str) +{ + /* Will store the {\em document trailer}, which is a clean-up code + used to return the PostScript interpeter back to the state, in which + it was before displaying this document. */ + write(str, + "%%%%Trailer\n" + "doc-origstate restore\n" + "%%%%EOF\n"); +} + +// *********************************************************************** +// ***************************** PAGE LEVEL ****************************** +// *********************************************************************** + +static unsigned char * +ASCII85_encode(unsigned char * dst, + const unsigned char * src_start, + const unsigned char * src_end) +{ + /* Will read data between #src_start# and #src_end# pointers (excluding byte + pointed by #src_end#), encode it using {\bf ASCII85} algorithm, and + output the result into the destination buffer pointed by #dst#. The + function returns pointer to the first unused byte in the destination + buffer. */ + int symbols=0; + const unsigned char * ptr; + for(ptr=src_start;ptr<src_end;ptr+=4) + { + unsigned int num=0; + if (ptr+3<src_end) + { + num |= ptr[0] << 24; + num |= ptr[1] << 16; + num |= ptr[2] << 8; + num |= ptr[3]; + } + else + { + num |= ptr[0] << 24; + if (ptr+1<src_end) + num |= ptr[1] << 16; + if (ptr+2<src_end) + num |= ptr[2] << 8; + } + int a1, a2, a3, a4, a5; + a5=num % 85; num/=85; + a4=num % 85; num/=85; + a3=num % 85; num/=85; + a2=num % 85; + a1=num / 85; + *dst++ = a1+33; + *dst++ = a2+33; + if (ptr+1<src_end) + *dst++ = a3+33; + if (ptr+2<src_end) + *dst++ = a4+33; + if (ptr+3<src_end) + *dst++ = a5+33; + symbols += 5; + if (symbols > 70 && ptr+4<src_end) + { + *dst++='\n'; + symbols=0; + } + } + return dst; +} + +static unsigned char * +RLE_encode(unsigned char * dst, + const unsigned char * src_start, + const unsigned char * src_end) +{ + /* Will read data between #src_start# and #src_end# pointers (excluding byte + pointed by #src_end#), RLE encode it, and output the result into the + destination buffer pointed by #dst#. #counter# is used to count the + number of output bytes. The function returns pointer to the first unused + byte in the destination buffer. */ + const unsigned char * ptr; + for(ptr=src_start;ptr<src_end;ptr++) + { + if (ptr==src_end-1) + { + *dst++=0; *dst++=*ptr; + } + else if (ptr[0]!=ptr[1]) + { + // Guess how many non repeating bytes we have + const unsigned char * ptr1; + for(ptr1=ptr+1;ptr1<src_end-1;ptr1++) + if (ptr1[0]==ptr1[1] || ptr1-ptr>=128) break; + int pixels=ptr1-ptr; + *dst++=pixels-1; + for(int cnt=0;cnt<pixels;cnt++) + *dst++=*ptr++; + ptr--; + } + else + { + // Get the number of repeating bytes + const unsigned char * ptr1; + for(ptr1=ptr+1;ptr1<src_end-1;ptr1++) + if (ptr1[0]!=ptr1[1] || ptr1-ptr+1>=128) break; + int pixels=ptr1-ptr+1; + *dst++=257-pixels; + *dst++=*ptr; + ptr=ptr1; + } + } + return dst; +} + +#define GRAY(r,g,b) (((r)*20+(g)*32+(b)*12)/64) + +void +DjVuToPS:: +store_page_setup(ByteStream &str, + int dpi, + const GRect &grect, + int align ) +{ + /* Will store PostScript code necessary to prepare page for + the coming \Ref{DjVuImage}. This is basically a scaling + code plus initialization of some buffers. */ + if (options.get_format() == Options::EPS) + write(str, + "/page-origstate save def\n" + "%% -- coordinate system\n" + "/image-dpi %d def\n" + "/image-x 0 def\n" + "/image-y 0 def\n" + "/image-width %d def\n" + "/image-height %d def\n" + "/coeff 100 image-dpi div def\n" + "/a11 coeff def\n" + "/a12 0 def\n" + "/a13 0 def\n" + "/a21 0 def\n" + "/a22 coeff def\n" + "/a23 0 def\n" + "[a11 a21 a12 a22 a13 a23] concat\n" + "gsave 0 0 image-width image-height rectclip\n" + "%% -- begin printing\n", + dpi, grect.width(), grect.height() ); + else + { + int margin = 0; + const char *xauto = "false"; + const char *xportrait = "false"; + const char *xfit = "false"; + if (options.get_orientation()==Options::AUTO) + xauto = "true"; + if (options.get_orientation()==Options::PORTRAIT) + xportrait = "true"; + if (options.get_zoom()<=0) + xfit = "true"; + if (options.get_cropmarks()) + margin = 36; + else if (options.get_frame()) + margin = 6; + write(str, + "/page-origstate save def\n" + "%% -- coordinate system\n" + "/auto-orient %s def\n" + "/portrait %s def\n" + "/fit-page %s def\n" + "/zoom %d def\n" + "/image-dpi %d def\n" + "clippath pathbbox newpath\n" + "2 index sub exch 3 index sub\n" + "/page-width exch def\n" + "/page-height exch def\n" + "/page-y exch def\n" + "/page-x exch def\n" + "/image-x 0 def\n" + "/image-y 0 def\n" + "/image-width %d def\n" + "/image-height %d def\n" + "/margin %d def\n" + "/halign %d def\n" + "/valign 0 def\n", + xauto, xportrait, xfit, options.get_zoom(), + dpi, grect.width(), grect.height(), + margin, align ); + write(str, + "%% -- position page\n" + "auto-orient {\n" + " image-height image-width sub\n" + " page-height page-width sub\n" + " mul 0 ge /portrait exch def\n" + "} if\n" + "fit-page {\n" + " /page-width page-width margin sub\n" + " halign 0 eq { margin sub } if def\n" + " /page-height page-height margin sub\n" + " valign 0 eq { margin sub } if def\n" + " /page-x page-x halign 0 ge { margin add } if def\n" + " /page-y page-y valign 0 ge { margin add } if def\n" + "} if\n" + "portrait {\n" + " fit-page {\n" + " image-height page-height div\n" + " image-width page-width div\n" + " gt {\n" + " page-height image-height div /coeff exch def\n" + " } {\n" + " page-width image-width div /coeff exch def\n" + " } ifelse\n" + " } {\n" + " /coeff 72 image-dpi div zoom mul 100 div def\n" + " } ifelse\n" + " /start-x page-x page-width image-width\n" + " coeff mul sub 2 div halign 1 add mul add def\n" + " /start-y page-y page-height image-height\n" + " coeff mul sub 2 div valign 1 add mul add def\n" + " /a11 coeff def\n" + " /a12 0 def\n" + " /a13 start-x def\n" + " /a21 0 def\n" + " /a22 coeff def\n" + " /a23 start-y def\n" + "} { %% landscape\n" + " fit-page {\n" + " image-height page-width div\n" + " image-width page-height div\n" + " gt {\n" + " page-width image-height div /coeff exch def\n" + " } {\n" + " page-height image-width div /coeff exch def\n" + " } ifelse\n" + " } {\n" + " /coeff 72 image-dpi div zoom mul 100 div def\n" + " } ifelse\n" + " /start-x page-x page-width add page-width image-height\n" + " coeff mul sub 2 div valign 1 add mul sub def\n" + " /start-y page-y page-height image-width\n" + " coeff mul sub 2 div halign 1 add mul add def\n" + " /a11 0 def\n" + " /a12 coeff neg def\n" + " /a13 start-x image-y coeff neg mul sub def\n" + " /a21 coeff def\n" + " /a22 0 def\n" + " /a23 start-y image-x coeff mul add def \n" + "} ifelse\n" + "[a11 a21 a12 a22 a13 a23] concat\n" + "gsave 0 0 image-width image-height rectclip\n" + "%% -- begin print\n"); + } +} + +void +DjVuToPS:: +store_page_trailer(ByteStream &str) +{ + write(str, + "%% -- end print\n" + "grestore\n"); + if (options.get_frame()) + write(str, + "%% Drawing frame\n" + "gsave 0.7 setgray 0.5 coeff div setlinewidth 0 0\n" + "image-width image-height rectstroke\n" + "grestore\n"); + if (options.get_cropmarks() && + options.get_format() != Options::EPS ) + write(str, + "%% Drawing crop marks\n" + "/cm { gsave translate rotate 1 coeff div dup scale\n" + " 0 setgray 0.5 setlinewidth -36 0 moveto 0 0 lineto\n" + " 0 -36 lineto stroke grestore } bind def\n" + "0 0 0 cm 180 image-width image-height cm\n" + "90 image-width 0 cm 270 0 image-height cm\n"); + write(str, + "page-origstate restore\n"); +} + +static int +compute_red(int w, int h, int rw, int rh) +{ + for (int red=1; red<16; red++) + if (((w+red-1)/red==rw) && ((h+red-1)/red==rh)) + return red; + return 16; +} + +static int +get_bg_red(GP<DjVuImage> dimg) +{ + GP<GPixmap> pm = 0; + // Access image size + int width = dimg->get_width(); + int height = dimg->get_height(); + if (width<=0 || height<=0) return 0; + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = dimg->get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + return compute_red(width,height,w,h); + } + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = dimg->get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + return compute_red(width,height,w,h); + } + return 0; +} + +static GP<GPixmap> +get_bg_pixmap(GP<DjVuImage> dimg, const GRect &rect) +{ + GP<GPixmap> pm = 0; + // Access image size + int width = dimg->get_width(); + int height = dimg->get_height(); + GP<DjVuInfo> info = dimg->get_info(); + if (width<=0 || height<=0 || !info) return 0; + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = dimg->get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + pm = bg44->get_pixmap(1,rect); + return pm; + } + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = dimg->get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + pm->init(*bgpm, rect); + return pm; + } + // FAILURE + return 0; +} + +void +DjVuToPS:: +make_gamma_ramp(GP<DjVuImage> dimg) +{ + double targetgamma = options.get_gamma(); + double whitepoint = (options.get_sRGB() ? 255 : 280); + for (int i=0; i<256; i++) + ramp[i] = i; + if (! dimg->get_info()) + return; + if (targetgamma < 0.1) + return; + double filegamma = dimg->get_info()->gamma; + double correction = filegamma / targetgamma; + if (correction<0.1 || correction>10) + return; + { + for (int i=0; i<256; i++) + { + double x = (double)(i)/255.0; + if (correction != 1.0) + x = pow(x, correction); + int j = (int) floor(whitepoint * x + 0.5); + ramp[i] = (j>255) ? 255 : (j<0) ? 0 : j; + } + } +} + +void +DjVuToPS:: +print_fg_2layer(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect, + unsigned char *blit_list) +{ + // Pure-jb2 or color-jb2 case. + GPixel p; + int currentx=0; + int currenty=0; + GP<DjVuPalette> pal = dimg->get_fgbc(); + GP<JB2Image> jb2 = dimg->get_fgjb(); + if (! jb2) return; + int num_blits = jb2->get_blit_count(); + int current_blit; + for(current_blit=0; current_blit<num_blits; current_blit++) + { + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + if ((pal) && !(options.get_mode()==Options::BW)) + { + pal->index_to_color(pal->colordata[current_blit], p); + if (options.get_color()) + { + write(str,"/%d %d %d %f %f %f c\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty, + ramp[p.r]/255.0, ramp[p.g]/255.0, ramp[p.b]/255.0); + } + else + { + write(str,"/%d %d %d %f c\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty, + ramp[GRAY(p.r, p.g, p.b)]/255.0); + } + } + else + { + write(str,"/%d %d %d s\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty); + } + currentx = blit->left; + currenty = blit->bottom; + } + } +} + +void +DjVuToPS:: +print_fg_3layer(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &cprn_rect, + unsigned char *blit_list ) +{ + GRect prn_rect; + GP<GPixmap> brush = dimg->get_fgpm(); + if (! brush) return; + int br = brush->rows(); + int bc = brush->columns(); + int red = compute_red(dimg->get_width(),dimg->get_height(),bc,br); + prn_rect.ymin = (cprn_rect.ymin)/red; + prn_rect.xmin = (cprn_rect.xmin)/red; + prn_rect.ymax = (cprn_rect.ymax+red-1)/red; + prn_rect.xmax = (cprn_rect.xmax+red-1)/red; + int color_nb = ((options.get_color()) ? 3 : 1); + GP<JB2Image> jb2 = dimg->get_fgjb(); + if (! jb2) return; + int pw = bc; + int ph = 2; + + write(str, + "/P {\n" + " 11 dict dup begin 4 1 roll\n" + " /PatternType 1 def\n" + " /PaintType 1 def\n" + " /TilingType 1 def\n" + " /H exch def\n" + " /W exch def\n" + " /Red %d def\n" + " /PatternString exch def\n" + " /XStep W Red mul def\n" + " /YStep H Red mul def\n" + " /BBox [0 0 XStep YStep] def\n" + " /PaintProc { begin\n" + " Red dup scale\n" + " << /ImageType 1 /Width W /Height H\n" + " /BitsPerComponent 8 /Interpolate false\n" + " /Decode [%s] /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource PatternString >> image\n" + " end } bind def\n" + " 0 0 XStep YStep rectclip\n" + " end matrix makepattern\n" + " /Pattern setcolorspace setpattern\n" + " 0 0 moveto\n" + "} def\n", red, (color_nb == 1) ? "0 1" : "0 1 0 1 0 1" ); + + unsigned char *s; + GPBuffer<unsigned char> gs(s,pw*ph*color_nb); + unsigned char *s_ascii_encoded; + GPBuffer<unsigned char> gs_ascii_encoded(s_ascii_encoded,pw*ph*2*color_nb); + { + for (int y=prn_rect.ymin; y<prn_rect.ymax; y+=ph) + for (int x=prn_rect.xmin; x<prn_rect.xmax; x+=pw) + { + int w = ((x+pw > prn_rect.xmax) ? prn_rect.xmax-x : pw); + int h = ((y+ph > prn_rect.ymax) ? prn_rect.ymax-y : ph); + int currentx = x * red; + int currenty = y * red; + // Find first intersecting blit + int current_blit; + int num_blits = jb2->get_blit_count(); + GRect rect1(currentx,currenty, w*red, h*red); + for(current_blit=0; current_blit<num_blits; current_blit++) + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + GRect rect2(blit->left, blit->bottom, + jb2->get_shape(blit->shapeno).bits->columns(), + jb2->get_shape(blit->shapeno).bits->rows()); + if (rect2.intersect(rect1,rect2)) + break; + } + if (current_blit >= num_blits) + continue; + // Setup pattern + write(str,"gsave %d %d translate\n", currentx, currenty); + write(str,"<~"); + unsigned char *q = s; + for(int current_row = y; current_row<y+h; current_row++) + { + GPixel *row_pix = (*brush)[current_row]; + for(int current_col = x; current_col<x+w; current_col++) + { + GPixel &p = row_pix[current_col]; + if (color_nb>1) + { + *q++ = ramp[p.r]; + *q++ = ramp[p.g]; + *q++ = ramp[p.b]; + } + else + { + *q++ = ramp[GRAY(p.r,p.g,p.b)]; + } + } + } + unsigned char *stop_ascii = + ASCII85_encode(s_ascii_encoded,s,s+w*h*color_nb); + *stop_ascii++='\0'; + write(str,"%s",s_ascii_encoded); + write(str,"~> %d %d P\n", w, h); + // Keep performing blits + for(; current_blit<num_blits; current_blit++) + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + GRect rect2(blit->left, blit->bottom, + jb2->get_shape(blit->shapeno).bits->columns(), + jb2->get_shape(blit->shapeno).bits->rows()); + if (rect2.intersect(rect1,rect2)) + { + write(str,"/%d %d %d s\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty); + currentx = blit->left; + currenty = blit->bottom; + } + } + write(str,"grestore\n"); + } + // Cleanup + } +} + +void +DjVuToPS:: +print_fg(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect ) +{ + GP<JB2Image> jb2=dimg->get_fgjb(); + if (! jb2) return; + int num_blits = jb2->get_blit_count(); + int num_shapes = jb2->get_shape_count(); + unsigned char *dict_shapes = 0; + unsigned char *blit_list = 0; + GPBuffer<unsigned char> gdict_shapes(dict_shapes,num_shapes); + GPBuffer<unsigned char> gblit_list(blit_list,num_blits); + for(int i=0; i<num_shapes; i++) + { + dict_shapes[i]=0; + } + for(int current_blit=0; current_blit<num_blits; current_blit++) + { + JB2Blit *blit = jb2->get_blit(current_blit); + JB2Shape *shape = & jb2->get_shape(blit->shapeno); + blit_list[current_blit] = 0; + if (! shape->bits) + continue; + GRect rect2(blit->left, blit->bottom, + shape->bits->columns(), shape->bits->rows()); + if (rect2.intersect(rect2, prn_rect)) + { + dict_shapes[blit->shapeno] = 1; + blit_list[current_blit] = 1; + } + } + write(str, + "%% --- now doing the foreground\n" + "gsave DjVuColorSpace setcolorspace\n" ); + // Define font + write(str, + "/$DjVuLocalFont 7 dict def\n" + "$DjVuLocalFont begin\n" + "/FontType 3 def \n" + "/FontMatrix [1 0 0 1 0 0] def\n" + "/FontBBox [0 0 1 .5] def\n" + "/CharStrings %d dict def\n" + "/Encoding 2 array def\n" + "0 1 1 {Encoding exch /.notdef put} for \n" + "CharStrings begin\n" + "/.notdef {} def\n", + num_shapes+1); + for(int current_shape=0; current_shape<num_shapes; current_shape++) + { + if (dict_shapes[current_shape]) + { + JB2Shape *shape = & jb2->get_shape(current_shape); + GP<GBitmap> bitmap = shape->bits; + int rows = bitmap->rows(); + int columns = bitmap->columns(); + int nbytes = (columns+7)/8*rows+1; + int nrows = rows; + int nstrings=0; + if (nbytes>(int)ps_string_size) //max string length + { + nrows=ps_string_size/((columns+7)/8); + nbytes=(columns+7)/8*nrows+1; + } + unsigned char *s_start; + GPBuffer<unsigned char> gs_start(s_start,nbytes); + unsigned char *s_ascii; + GPBuffer<unsigned char> gs_ascii(s_ascii,nbytes*2); + write(str,"/%d {",current_shape); + + unsigned char *s = s_start; + for(int current_row=0; current_row<rows; current_row++) + { + unsigned char * row_bits = (*bitmap)[current_row]; + unsigned char acc = 0; + unsigned char mask = 0; + for(int current_col=0; current_col<columns; current_col++) + { + if (mask == 0) + mask = 0x80; + if (row_bits[current_col]) + acc |= mask; + mask >>= 1; + if (mask == 0) + { + *s=acc; + s++; + acc = mask = 0; + } + } + if (mask != 0) + { + *s=acc; + s++; + } + if (!((current_row+1)%nrows)) + { + unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s); + *stop_ascii++='\0'; + write(str,"<~%s~> ",s_ascii); + s=s_start; + nstrings++; + } + } + if (s!=s_start) + { + unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s); + *stop_ascii++='\0'; + write(str,"<~%s~> ",s_ascii); + nstrings++; + } + if (nstrings==1) + write(str," %d %d g} def\n", columns, rows); + else + write(str," %d %d %d gn} def\n", columns, rows,nstrings); + } + } + write(str, + "end\n" + "/BuildGlyph {\n" + " exch /CharStrings get exch\n" + " 2 copy known not\n" + " {pop /.notdef} if\n" + " get exec \n" + "} bind def\n" + "end\n" + "/LocalDjVuFont $DjVuLocalFont definefont pop\n" + "/LocalDjVuFont findfont setfont\n" ); + write(str, + "-%d -%d translate\n" + "0 0 moveto\n", + prn_rect.xmin, prn_rect.ymin); + // Print the foreground layer + if (dimg->get_fgpm() && !(options.get_mode()==Options::BW)) + print_fg_3layer(str, dimg, prn_rect, blit_list); + else + print_fg_2layer(str, dimg, prn_rect, blit_list); + write(str, "/LocalDjVuFont undefinefont grestore\n"); +} + + +void +DjVuToPS:: +print_bg(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &cprn_rect) +{ + GP<GPixmap> pm; + GRect prn_rect; + double print_done = 0; + int red = 0; + write(str, "%% --- now doing the background\n"); + if (! (red = get_bg_red(dimg))) + return; + write(str, + "gsave -%d -%d translate\n" + "/bgred %d def bgred bgred scale\n", + cprn_rect.xmin % red, + cprn_rect.ymin % red, + red); + prn_rect.ymin = (cprn_rect.ymin)/red; + prn_rect.ymax = (cprn_rect.ymax+red-1)/red; + prn_rect.xmin = (cprn_rect.xmin)/red; + prn_rect.xmax = (cprn_rect.xmax+red-1)/red; + // Display image + int band_bytes = 125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + int ps_chunk_height = 30960/prn_rect.width()+1; + buffer_size = buffer_size*23/10; + bool do_color = options.get_color(); + if (!dimg->is_legal_photo() && + !dimg->is_legal_compound() || + options.get_mode()==Options::BW) + do_color = false; + if (do_color) + buffer_size *= 3; + if (do_color) + write(str, + "/bufferR %d string def\n" + "/bufferG %d string def\n" + "/bufferB %d string def\n" + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1 0 1 0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /MultipleDataSources true\n" + " /DataSource [ { ReadR } { ReadG } { ReadB } ]\n" + " /Interpolate false >> image\n", + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + prn_rect.width(), prn_rect.height()); + else + write(str, + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource currentfile /ASCII85Decode\n" + " filter /RunLengthDecode filter\n" + " /Interpolate false >> image\n", + prn_rect.width(), prn_rect.height()); + + unsigned char *buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + unsigned char *rle_in; + GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width()); + unsigned char *rle_out; + GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width()); + { + // Start storing image in bands + unsigned char * rle_out_end = rle_out; + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + GP<GPixmap> pm = 0; + // Compute next band + grectBand.ymin=grectBand.ymax; + grectBand.ymax=grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax>prn_rect.ymax) + grectBand.ymax=prn_rect.ymax; + pm = get_bg_pixmap(dimg, grectBand); + unsigned char *buf_ptr = buffer; + if (pm) + { + if (do_color) + { + int y=0; + while(y<grectBand.height()) + { + int row, y1; + unsigned char *ptr, *ptr1; + // Doing R component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->r]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n'; + // Doing G component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->g]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + // Doing B component of current chunk + for (row=0, ptr=rle_in, y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->b]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + y=y1; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } //while (y>=0) + } + else + { + // Don't use color + int y=0; + while(y<grectBand.height()) + { + unsigned char *ptr = rle_in; + for(int row=0; + row<ps_chunk_height && y<grectBand.height(); + row++,y++) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)]; + } + rle_out_end = RLE_encode(rle_out_end, rle_in, ptr); + unsigned char *encode_to + = rle_out+(rle_out_end-rle_out)/4*4; + int bytes_left = rle_out_end-encode_to; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to); + *buf_ptr++ = '\n'; + memcpy(rle_out, encode_to, bytes_left); + rle_out_end = rle_out+bytes_left; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + } + } // if (pm) + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double)(grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } // while(grectBand.yax<grect.ymax) + if (! do_color) + { + unsigned char * buf_ptr = buffer; + *rle_out_end++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end); + *buf_ptr++='~'; + *buf_ptr++='>'; + *buf_ptr++='\n'; + str.writall(buffer, buf_ptr-buffer); + } + } + //restore the scaling + write(str, "grestore\n"); +} + +void +DjVuToPS:: +print_image_lev1(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect) +{ + double print_done=0; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + GP<GPixmap> pm; + GP<GBitmap> bm; + GRect test(0,0,1,1); + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(test, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(test, all); + else if (options.get_mode() != Options::BW) + pm = dimg->get_pixmap(test, all); + if (! pm) + bm = dimg->get_bitmap(test,all); + if (! pm && ! bm) + return; + write(str, + "%% --- now doing a level 1 image\n" + "gsave\n"); + // Display image + int band_bytes=125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + buffer_size = buffer_size*21/10; + bool do_color = false; + bool do_color_or_gray = false; + if (pm && (options.get_mode() != Options::BW)) + do_color_or_gray = true; + if (do_color_or_gray && options.get_color()) + do_color = true; + if (do_color) + buffer_size *= 3; + if (do_color) + write(str, "/buffer24 %d string def\n", 3*prn_rect.width()); + if (do_color_or_gray) + write(str, "/buffer8 %d string def\n", prn_rect.width()); + else + write(str, "/buffer8 %d string def\n", (prn_rect.width()+7)/8); + if (do_color) + { + write(str, + "%d %d 8 [ 1 0 0 1 0 0 ]\n" + "{ ColorProc } false 3 ColorImage\n", + prn_rect.width(), prn_rect.height()); + } + else if (do_color_or_gray) + { + write(str, + "%d %d 8 [ 1 0 0 1 0 0 ]\n" + "{ currentfile buffer8 readhexstring pop } image\n", + prn_rect.width(), prn_rect.height()); + } + else + { + write(str, + "%d %d 1 [ 1 0 0 1 0 0 ]\n" + "{ currentfile buffer8 readhexstring pop } image\n", + prn_rect.width(), prn_rect.height()); + } + unsigned char * buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + { + // Start storing image in bands + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + // Compute next band + grectBand.ymin = grectBand.ymax; + grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax > prn_rect.ymax) + grectBand.ymax = prn_rect.ymax; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + pm = 0; + bm = 0; + if (do_color_or_gray) + { + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(grectBand, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(grectBand, all); + else + pm = dimg->get_pixmap(grectBand, all); + } + else + { + bm = dimg->get_bitmap(grectBand, all); + } + // Store next band + unsigned char *buf_ptr = buffer; + int symbols=0; + for (int y=0; y<grectBand.height(); y++) + { + if (pm && do_color_or_gray) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--, pix++) + { + if (do_color) + { + char *data; + data = bin2hex[ramp[pix->r]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + data = bin2hex[ramp[pix->g]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + data = bin2hex[ramp[pix->b]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 6; + } + else + { + char *data; + data = bin2hex[ramp[GRAY(pix->r,pix->g,pix->b)]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + } + if (symbols>70) + { + *buf_ptr++ = '\n'; + symbols=0; + } + } + } + else if (bm) + { + unsigned char *pix = (*bm)[y]; + unsigned char acc = 0; + unsigned char mask = 0; + char *data; + for (int x=grectBand.width(); x>0; x--, pix++) + { + if (mask == 0) + mask = 0x80; + if (! *pix) + acc |= mask; + mask >>= 1; + if (mask == 0) + { + data = bin2hex[acc]; + acc = 0; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + if (symbols>70) + { + *buf_ptr++ = '\n'; + symbols = 0; + } + } + } + if (mask != 0) + { + data = bin2hex[acc]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + } + } + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double) (grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } + write(str, "\n"); + } + write(str, "grestore\n"); +} + +void +DjVuToPS:: +print_image_lev2(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect) +{ + double print_done=0; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + GP<GPixmap> pm; + GRect test(0,0,1,1); + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(test, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(test, all); + else if (options.get_mode() != Options::BW) + pm = dimg->get_pixmap(test, all); + if (! pm) + return; + write(str, + "%% --- now doing a level 2 image\n" + "gsave\n"); + // Display image + int band_bytes=125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + int ps_chunk_height = 30960/prn_rect.width()+1; + buffer_size = buffer_size*21/10 + 32; + bool do_color = options.get_color(); + if (do_color) + { + buffer_size *= 3; + write(str, + "/bufferR %d string def\n" + "/bufferG %d string def\n" + "/bufferB %d string def\n" + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1 0 1 0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /MultipleDataSources true\n" + " /DataSource [ { ReadR } { ReadG } { ReadB } ]\n" + " /Interpolate false >> image\n", + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + prn_rect.width(), prn_rect.height()); + } + else + { + write(str, + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource currentfile /ASCII85Decode\n" + " filter /RunLengthDecode filter\n" + " /Interpolate false >> image\n", + prn_rect.width(), prn_rect.height()); + } + unsigned char *buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + unsigned char *rle_in; + GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width()); + unsigned char *rle_out; + GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width()); + { + // Start storing image in bands + unsigned char * rle_out_end = rle_out; + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + // Compute next band + grectBand.ymin = grectBand.ymax; + grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax > prn_rect.ymax) + grectBand.ymax = prn_rect.ymax; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + pm = 0; + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(grectBand, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(grectBand, all); + else + pm = dimg->get_pixmap(grectBand, all); + // Store next band + unsigned char *buf_ptr = buffer; + if (do_color && pm) + { + int y=0; + while(y<grectBand.height()) + { + int row, y1; + unsigned char *ptr, *ptr1; + // Doing R component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->r]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n'; + // Doing G component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->g]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + // Doing B component of current chunk + for (row=0, ptr=rle_in, y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->b]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + y=y1; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } //while (y>=0) + } + else if (pm) + { + // Don't use color + int y=0; + while(y<grectBand.height()) + { + unsigned char *ptr = rle_in; + for(int row=0; + row<ps_chunk_height && y<grectBand.height(); + row++,y++) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)]; + } + rle_out_end = RLE_encode(rle_out_end, rle_in, ptr); + unsigned char *encode_to = rle_out + + (rle_out_end-rle_out)/4*4; + int bytes_left = rle_out_end-encode_to; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to); + *buf_ptr++ = '\n'; + memcpy(rle_out, encode_to, bytes_left); + rle_out_end = rle_out+bytes_left; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + if (grectBand.ymax >= prn_rect.ymax) + { + *rle_out_end++ = 0x80; // Add EOF marker + buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + } + } + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double) (grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } + write(str, "\n"); + } + write(str, "grestore\n"); +} + +static void +get_anno_sub(IFFByteStream &iff, IFFByteStream &out) +{ + GUTF8String chkid; + while (iff.get_chunk(chkid)) + { + if (iff.composite()) + get_anno_sub(iff, out); + else if (chkid == "ANTa" || chkid == "ANTz" || + chkid == "TXTa" || chkid == "TXTz" ) + { + out.put_chunk(chkid); + out.copy(*iff.get_bytestream()); + out.close_chunk(); + } + iff.close_chunk(); + } +} + +static GP<ByteStream> +get_anno(GP<DjVuFile> f) +{ + if (! f->anno) + { + GP<ByteStream> bs = f->get_init_data_pool()->get_stream(); + GP<ByteStream> anno = ByteStream::create(); + GP<IFFByteStream> in = IFFByteStream::create(bs); + GP<IFFByteStream> out = IFFByteStream::create(anno); + get_anno_sub(*in, *out); + f->anno = anno; + } + f->anno->seek(0); + return f->anno; +} + +static GP<DjVuTXT> +get_text(GP<DjVuFile> file) +{ + GUTF8String chkid; + GP<IFFByteStream> iff = IFFByteStream::create(get_anno(file)); + while (iff->get_chunk(chkid)) + { + if (chkid == "TXTa") + { + GP<DjVuTXT> txt = DjVuTXT::create(); + txt->decode(iff->get_bytestream()); + return txt; + } + else if (chkid == "TXTz") + { + GP<DjVuTXT> txt = DjVuTXT::create(); + GP<ByteStream> bsiff = BSByteStream::create(iff->get_bytestream()); + txt->decode(bsiff); + return txt; + } + iff->close_chunk(); + } + return 0; +} + +static void +print_ps_string(const char *data, int length, ByteStream &out) +{ + while (*data && length>0) + { + int span = 0; + while (span<length && data[span]>=0x20 && data[span]<0x7f + && data[span]!='(' && data[span]!=')' && data[span]!='\\' ) + span++; + if (span > 0) + { + out.write(data, span); + data += span; + length -= span; + } + else + { + char buffer[5]; + sprintf(buffer,"\\%03o", *data); + out.write(buffer,4); + data += 1; + length -= 1; + } + } +} + +static void +print_txt_sub(DjVuTXT &txt, DjVuTXT::Zone &zone, + ByteStream &out,int &lastx,int &lasty) +{ + // Get separator + char separator = 0; + switch(zone.ztype) + { + case DjVuTXT::COLUMN: + separator = DjVuTXT::end_of_column; break; + case DjVuTXT::REGION: + separator = DjVuTXT::end_of_region; break; + case DjVuTXT::PARAGRAPH: + separator = DjVuTXT::end_of_paragraph; break; + case DjVuTXT::LINE: + separator = DjVuTXT::end_of_line; break; + case DjVuTXT::WORD: + separator = ' '; break; + default: + separator = 0; break; + } + // Zone children + if (zone.children.isempty()) + { + const char *data = (const char*)txt.textUTF8 + zone.text_start; + int length = zone.text_length; + if (data[length-1] == separator) + length -= 1; + out.write("( ",2); + print_ps_string(data,length,out); + out.write(")",1); + GUTF8String message; + int tmpx= zone.rect.xmin-lastx; + int tmpy= zone.rect.ymin-lasty; + message.format(" %d %d S \n", tmpx, tmpy); + lastx=zone.rect.xmin; + lasty=zone.rect.ymin; + out.write((const char*)message, message.length()); + } + else + { + if (zone.ztype==DjVuTXT::LINE) + { + GUTF8String message; + message.format("%d F\n",zone.rect.ymax-zone.rect.ymin); + out.write((const char*)message,message.length()); + } + for (GPosition pos=zone.children; pos; ++pos) + print_txt_sub(txt, zone.children[pos], out,lastx,lasty); + } +} + +static void +print_txt(GP<DjVuTXT> txt, + ByteStream &out ) +{ + if (txt) + { + int lastx=0; + int lasty=0; + GUTF8String message = + "%% -- now doing hidden text\n" + "gsave -1 -1 0 0 clip 0 0 moveto\n"; + out.write((const char*)message,message.length()); + print_txt_sub(*txt, txt->page_zone, out,lastx,lasty); + message = + "grestore \n"; + out.write((const char*)message,message.length()); + } +} + +void +DjVuToPS:: +print_image(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect, + GP<DjVuTXT> txt) +{ + /* Just outputs the specified image. The function assumes, that + all add-ons (like {\em document setup}, {\em page setup}) are + already there. It will just output the image. Since + output of this function will generate PostScript errors when + used without output of auxiliary functions, it should be + used carefully. */ + DEBUG_MSG("DjVuToPS::print_image(): Printing DjVuImage to a stream\n"); + DEBUG_MAKE_INDENT(3); + if (!dimg) + G_THROW(ERR_MSG("DjVuToPS.empty_image")); + if (prn_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.empty_rect")); + if (prn_progress_cb) + prn_progress_cb(0, prn_progress_cl_data); + // Compute information for chosen display mode + print_txt(txt, str); + make_gamma_ramp(dimg); + if (options.get_level() < 2) + { + print_image_lev1(str, dimg, prn_rect); + } + else if (options.get_level() < 3 && dimg->get_fgpm()) + { + switch(options.get_mode()) + { + case Options::COLOR: + case Options::FORE: + print_image_lev2(str, dimg, prn_rect); + break; + case Options::BW: + print_fg(str, dimg, prn_rect); + break; + case Options::BACK: + print_bg(str, dimg, prn_rect); + break; + } + } + else + { + switch(options.get_mode()) + { + case Options::COLOR: + print_bg(str, dimg, prn_rect); + print_fg(str, dimg, prn_rect); + break; + case Options::FORE: + case Options::BW: + print_fg(str, dimg, prn_rect); + break; + case Options::BACK: + print_bg(str, dimg, prn_rect); + break; + } + } + if (prn_progress_cb) + prn_progress_cb(1, prn_progress_cl_data); +} + + + + +// *********************************************************************** +// ******* PUBLIC FUNCTION FOR PRINTING A SINGLE PAGE ******************** +// *********************************************************************** + + + + +void +DjVuToPS:: +print(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect_in, + const GRect &img_rect, + int override_dpi) +{ + DEBUG_MSG("DjVuToPS::print(): Printing DjVu page to a stream\n"); + DEBUG_MAKE_INDENT(3); + GRect prn_rect; + prn_rect.intersect(prn_rect_in, img_rect); + DEBUG_MSG("prn_rect=(" << prn_rect.xmin << ", " << prn_rect.ymin << ", " << + prn_rect.width() << ", " << prn_rect.height() << ")\n"); + DEBUG_MSG("img_rect=(" << img_rect.xmin << ", " << img_rect.ymin << ", " << + img_rect.width() << ", " << img_rect.height() << ")\n"); + if (!dimg) + G_THROW(ERR_MSG("DjVuToPS.empty_image")); + if (prn_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.empty_rect")); + if (img_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.bad_scale")); + GRectMapper mapper; + mapper.set_input(img_rect); + GRect full_rect(0, 0, dimg->get_width(), dimg->get_height()); + mapper.set_output(full_rect); + mapper.map(prn_rect); + int image_dpi = dimg->get_dpi(); + if (override_dpi>0) + image_dpi = override_dpi; + if (image_dpi <= 0) + image_dpi = 300; + store_doc_prolog(str, 1, (int)(image_dpi), &prn_rect); + store_doc_setup(str); + write(str,"%%%%Page: 1 1\n"); + store_page_setup(str, (int)(image_dpi), prn_rect); + print_image(str, dimg, prn_rect, 0); + store_page_trailer(str); + write(str,"showpage\n"); + store_doc_trailer(str); +} + + + + +// *********************************************************************** +// *************************** DOCUMENT LEVEL **************************** +// *********************************************************************** + + +void +DjVuToPS:: +parse_range(GP<DjVuDocument> doc, + GUTF8String page_range, + GList<int> &pages_todo) +{ + int doc_pages = doc->get_pages_num(); + if (!page_range.length()) + page_range.format("1-%d", doc_pages); + DEBUG_MSG("page_range='" << (const char *)page_range << "'\n"); + int spec = 0; + int both = 1; + int start_page = 1; + int end_page = doc_pages; + const char *q = (const char*)page_range; + char *p = (char*)q; + while (*p) + { + while (*p==' ') + p += 1; + if (! *p) + break; + if (*p>='0' && *p<='9') + { + end_page = strtol(p, &p, 10); + spec = 1; + } + else if (*p=='$') + { + spec = 1; + end_page = doc_pages; + p += 1; + } + else if (both) + { + end_page = 1; + } + else + { + end_page = doc_pages; + } + while (*p==' ') + p += 1; + if (both) + { + start_page = end_page; + if (*p == '-') + { + p += 1; + both = 0; + continue; + } + } + both = 1; + while (*p==' ') + p += 1; + if (*p && *p != ',') + G_THROW(ERR_MSG("DjVuToPS.bad_range") + + GUTF8String("\t") + GUTF8String(p) ); + if (*p == ',') + p += 1; + if (! spec) + G_THROW(ERR_MSG("DjVuToPS.bad_range") + + GUTF8String("\t") + page_range ); + spec = 0; + if (end_page < 0) + end_page = 0; + if (start_page < 0) + start_page = 0; + if (end_page > doc_pages) + end_page = doc_pages; + if (start_page > doc_pages) + start_page = doc_pages; + if (start_page <= end_page) + for(int page_num=start_page; page_num<=end_page; page_num++) + pages_todo.append(page_num-1); + else + for(int page_num=start_page; page_num>=end_page; page_num--) + pages_todo.append(page_num-1); + } +} + +class DjVuToPS::DecodePort : public DjVuPort +{ +protected: + DecodePort(void); +public: + static GP<DecodePort> create(void); + GEvent decode_event; + bool decode_event_received; + double decode_done; + GURL decode_page_url; + virtual void notify_file_flags_changed(const DjVuFile*,long,long); + virtual void notify_decode_progress(const DjVuPort*,double); +}; + +DjVuToPS::DecodePort:: +DecodePort(void) + : decode_event_received(false), + decode_done((double)0) +{ +} + +GP<DjVuToPS::DecodePort> +DjVuToPS::DecodePort:: +create(void) +{ + return new DecodePort; +} + +void +DjVuToPS::DecodePort:: +notify_file_flags_changed(const DjVuFile *source, + long set_mask, long clr_mask) +{ + // WARNING! This function is called from another thread + if (set_mask & (DjVuFile::DECODE_OK | + DjVuFile::DECODE_FAILED | + DjVuFile::DECODE_STOPPED )) + { + if (source->get_url() == decode_page_url) + { + decode_event_received=true; + decode_event.set(); + } + } +} + +void +DjVuToPS::DecodePort:: +notify_decode_progress(const DjVuPort *source, double done) +{ + // WARNING! This function is called from another thread + if (source->inherits("DjVuFile")) + { + DjVuFile * file=(DjVuFile *) source; + if (file->get_url()==decode_page_url) + if ((int) (decode_done*20)!=(int) (done*20)) + { + decode_done=done; + decode_event_received=true; + decode_event.set(); + } + } +} + +void +DjVuToPS:: +set_refresh_cb(void (*_refresh_cb)(void*), void *_refresh_cl_data) +{ + refresh_cb = _refresh_cb; + refresh_cl_data = _refresh_cl_data; +} + +void +DjVuToPS:: +set_prn_progress_cb(void (*_prn_progress_cb)(double, void *), + void *_prn_progress_cl_data) +{ + prn_progress_cb=_prn_progress_cb; + prn_progress_cl_data=_prn_progress_cl_data; +} + +void +DjVuToPS:: +set_dec_progress_cb(void (*_dec_progress_cb)(double, void *), + void *_dec_progress_cl_data) +{ + dec_progress_cb=_dec_progress_cb; + dec_progress_cl_data=_dec_progress_cl_data; +} + +void +DjVuToPS:: +set_info_cb(void (*_info_cb)(int, int, int, Stage, void*), + void *_info_cl_data) +{ + info_cb=_info_cb; + info_cl_data=_info_cl_data; +} + +GP<DjVuImage> +DjVuToPS:: +decode_page(GP<DjVuDocument> doc, + int page_num, int cnt, int todo) +{ + DEBUG_MSG("processing page #" << page_num << "\n"); + if (! port) + { + port = DecodePort::create(); + DjVuPort::get_portcaster()->add_route((DjVuDocument*)doc, port); + } + port->decode_event_received = false; + port->decode_done = 0; + GP<DjVuFile> djvu_file; + GP<DjVuImage> dimg; + if (page_num >= 0 && page_num < doc->get_pages_num()) + djvu_file = doc->get_djvu_file(page_num); + if (! djvu_file ) + return 0; + if (djvu_file->is_decode_ok()) + return doc->get_page(page_num, false); + // This is the best place to call info_cb(). Note, that + // get_page() will start decoding if necessary, and will not + // return until the decoding is over in a single threaded + // environment. That's why we call get_djvu_file() first. + if (info_cb) + info_cb(page_num, cnt, todo, DECODING, info_cl_data); + // Do NOT decode the page synchronously here!!! + // The plugin will deadlock otherwise. + dimg = doc->get_page(page_num, false); + djvu_file = dimg->get_djvu_file(); + port->decode_page_url = djvu_file->get_url(); + if (djvu_file->is_decode_ok()) + return dimg; + DEBUG_MSG("decoding\n"); + if (dec_progress_cb) + dec_progress_cb(0, dec_progress_cl_data); + while(! djvu_file->is_decode_ok()) + { + while(!port->decode_event_received && + !djvu_file->is_decode_ok()) + { + port->decode_event.wait(250); + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + port->decode_event_received = false; + if (djvu_file->is_decode_failed() || + djvu_file->is_decode_stopped()) + G_THROW(ERR_MSG("DjVuToPS.no_image") + + GUTF8String("\t") + + GUTF8String(page_num)); + if (dec_progress_cb) + dec_progress_cb(port->decode_done, dec_progress_cl_data); + } + if (dec_progress_cb) + dec_progress_cb(1, dec_progress_cl_data); + return dimg; +} + +void +DjVuToPS:: +process_single_page(ByteStream &str, + GP<DjVuDocument> doc, + int page_num, int cnt, int todo, + int magic) +{ + GP<DjVuTXT> txt; + GP<DjVuImage> dimg; + dimg = decode_page(doc, page_num, cnt, todo); + if (options.get_text()) + txt = get_text(dimg->get_djvu_file()); + if (info_cb) + info_cb(page_num, cnt, todo, PRINTING, info_cl_data); + if (!magic) + write(str, "%%%%Page: %d %d\n", page_num+1, cnt+1); + if (dimg) + { + int dpi = dimg->get_dpi(); + dpi = ((dpi <= 0) ? 300 : dpi); + GRect img_rect(0, 0, dimg->get_width(), dimg->get_height()); + store_page_setup(str, dpi, img_rect, magic); + print_image(str, dimg, img_rect,txt); + store_page_trailer(str); + } + if (!magic) + write(str,"showpage\n"); +} + + +struct pdata { + int page1, page2; + int smax, spos; + int offset; +}; + +void +DjVuToPS:: +process_double_page(ByteStream &str, + GP<DjVuDocument> doc, + void *v, int cnt, int todo) +{ + const pdata *inf = (const pdata*)v; + int off = abs(inf->offset); + write(str, + "%%%%Page: (%d,%d) %d\n" + "gsave\n" + "/fold-dict 8 dict dup 3 1 roll def begin\n" + " clippath pathbbox newpath pop pop translate\n" + " clippath pathbbox newpath 4 2 roll pop pop\n" + " /ph exch def\n" + " /pw exch def\n" + " /w ph %d sub 2 div def\n" + " /m1 %d def\n" + " /m2 %d def\n" + "end\n", + inf->page1 + 1, inf->page2 + 1, cnt, + 2 * (off + options.get_bookletfold(inf->smax-1)), + inf->offset + options.get_bookletfold(inf->spos), + inf->offset - options.get_bookletfold(inf->spos)); + if (options.get_cropmarks()) + write(str, + "%% -- folding marks\n" + "fold-dict begin\n" + " 0 setgray 0.5 setlinewidth\n" + " ph m1 m2 add add 2 div dup\n" + " 0 exch moveto 36 0 rlineto stroke\n" + " pw exch moveto -36 0 rlineto stroke\n" + "end\n"); + write(str, + "%% -- first page\n" + "gsave fold-dict begin\n" + " 0 ph 2 div w add m1 add translate 270 rotate\n" + " 0 0 w pw rectclip end\n"); + if (inf->page1 >= 0) + process_single_page(str, doc, inf->page1, cnt*2, todo*2, +1); + write(str, + "grestore\n" + "%% -- second page\n" + "gsave fold-dict begin\n" + " 0 ph 2 div m2 add translate 270 rotate\n" + " 0 0 w pw rectclip end\n"); + if (inf->page2 >= 0) + process_single_page(str, doc, inf->page2, cnt*2+1, todo*2, -1); + write(str, + "grestore\n" + "grestore\n" + "showpage\n"); +} + +static void +booklet_order(GList<int>& pages, int smax) +{ + // -- make a multiple of four + while (pages.size() & 0x3) + pages.append(-1); + // -- copy to array + int i = 0; + int n = pages.size(); + GTArray<int> p(0,n-1); + for (GPosition pos=pages; pos; ++pos) + p[i++] = pages[pos]; + // -- rebuild + pages.empty(); + for (i=0; i<n; i+=smax) + { + int lo = i; + int hi = i+smax-1; + if (hi >= n) + hi = n-1; + while (lo < hi) + { + pages.append(p[hi--]); + pages.append(p[lo++]); + pages.append(p[lo++]); + pages.append(p[hi--]); + } + } +} + + +// *********************************************************************** +// ******* PUBLIC FUNCTIONS FOR PRINTING MULTIPLE PAGES ****************** +// *********************************************************************** + + + +void +DjVuToPS:: +print(ByteStream &str, + GP<DjVuDocument> doc, + GUTF8String page_range) +{ + DEBUG_MSG("DjVuToPS::print(): Printing DjVu document\n"); + DEBUG_MAKE_INDENT(3); + // Get page range + GList<int> pages_todo; + parse_range(doc, page_range, pages_todo); + int todo = pages_todo.size(); + if (options.get_format()==Options::EPS) + { + /* Encapsulated Postscript mode */ + if (todo != 1) + G_THROW(ERR_MSG("DjVuToPS.only_one_page")); + GPosition pos = pages_todo; + int page_num = pages_todo[pos]; + GP<DjVuImage> dimg = decode_page(doc,page_num,0,todo); + if (! dimg) + G_THROW(ERR_MSG("DjVuToPS.no_image") + GUTF8String("\t1")); + GRect bbox(0, 0, dimg->get_width(), dimg->get_height()); + store_doc_prolog(str, 1, dimg->get_dpi(), &bbox); + store_doc_setup(str); + process_single_page(str, doc, page_num, 0, todo, 0); + } + else if (options.get_bookletmode()==Options::OFF) + { + /* Normal mode */ + int cnt = 0; + store_doc_prolog(str, todo, 0, 0); + store_doc_setup(str); + for(GPosition pos = pages_todo; pos; ++pos) + process_single_page(str,doc,pages_todo[pos],cnt++,todo,0); + store_doc_trailer(str); + } + else + { + /* Booklet mode */ + int sheets_left = (todo+3)/4; + int sides_todo = sheets_left; + if (options.get_bookletmode() == Options::RECTOVERSO) + sides_todo *= 2; + int sheets_max = (options.get_bookletmax()+3)/4; + if (! sheets_max) + sheets_max = sheets_left; + // -- reorder pages + booklet_order(pages_todo, sheets_max*4); + // -- print + int sides = 0; + int sheetpos = sheets_max; + store_doc_prolog(str, sides_todo, 0, 0); + store_doc_setup(str); + for (GPosition p=pages_todo; p; ++p) + { + struct pdata inf; + inf.page1 = pages_todo[p]; + inf.page2 = pages_todo[++p]; + inf.smax = sheets_max; + inf.spos = --sheetpos; + inf.offset = options.get_bookletalign(); + if (options.get_bookletmode() != Options::VERSO) + process_double_page(str,doc,(void*)&inf,sides++,sides_todo); + inf.page1 = pages_todo[++p]; + inf.page2 = pages_todo[++p]; + inf.offset = -inf.offset; + if (options.get_bookletmode() != Options::RECTO) + process_double_page(str,doc,(void*)&inf,sides++,sides_todo); + sheets_left -= 1; + if (sheetpos <= 0) + sheetpos = ((sheets_max<sheets_left) ? sheets_max : sheets_left); + } + store_doc_trailer(str); + } +} + + +void +DjVuToPS:: +print(ByteStream &str, GP<DjVuDocument> doc) +{ + GUTF8String dummy; + print(str,doc,dummy); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuToPS.h b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.h new file mode 100644 index 00000000..95d547bb --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.h @@ -0,0 +1,425 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002-2003 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuToPS.h,v 1.14 2004/03/05 16:48:53 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVU_TO_PS_H_ +#define _DJVU_TO_PS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name DjVuToPS.h + Files #"DjVuToPS.h"# and #"DjVuToPS.cpp"# implement code that can be + used to convert a \Ref{DjVuImage} or \Ref{DjVuDocument} to PostScript + format. The conversion is carried out by the \Ref{DjVuToPS} class. + + @memo PostScript file generator + @author Andrei Erofeev <eaf@geocities.com> \\ + Florin Nicsa <Florin.Nicsa@insa-lyon.fr> + @version + #$Id: DjVuToPS.h,v 1.14 2004/03/05 16:48:53 leonb Exp $# +*/ +//@{ + +#include "DjVuGlobal.h" +#include "GRect.h" +#include "DjVuDocument.h" +#include "DjVuText.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** DjVuImage to PostScript converter. + Use this class to print \Ref{DjVuImage}s and \Ref{DjVuDocument}s. + The behavior is customizable. See \Ref{DjVuToPS::Options} for the + description of available options.*/ +class DjVuToPS +{ +public: + class DecodePort; + + /** DjVuToPS options. Use this class to customize the way + in which DjVu to PS conversion will be done. You can adjust + the following things: + \begin{description} + \item[Format] ({\em EPS} or {\em PS}). Use the {\em EPS} + format if you plan to embed the output image into another + document. Print {\em PS} otherwise. + \item[Language level] ({\em 1} or {\em 2}). Any PostScript + printer or interpreter should understand PostScript Level 1 + files. Unfortunately we cannot efficiently compress and encode + data when generating Level 1 files. PostScript Level 2 allows + to employ an RLE compression and ASCII85 encoding scheme, + which makes output files significantly smaller. Most of + the printers and word processors nowadays support PostScript + Level 2. + \item[Orientation] ({\em PORTRAIT} or {\em LANDSCAPE}) + \item[Zoom factor] ({\em FIT_PAGE} or {\em ONE_TO_ONE}). + {\em ONE_TO_ONE} mode is useful, if you want the output to + be of the same size as the original image (before compression). + This requires that the #dpi# setting inside the \Ref{DjVuImage} + is correct. In most of the cases the {\em FIT_PAGE} zoom is + would be your best choice. + \item[Mode] ({\em COLOR}, {\em FORE}, {\em BACK}, or {\em BW}) + Specifies how the \Ref{DjVuImage}s will be rendered (all layers, + foreground layer, background layer, and the mask respectively) + \item[Color] ({\em TRUE} or {\em FALSE}). Choosing {\em FALSE} + converts color images to gray scale. + \item[Gamma] Printer color correction. + This parameter ranges from #0.3# to #5.0#. + \item[sRGB] ({\em TRUE} or {\em FALSE}). Choosing {\em TRUE} + enables accurate sRGB color calibration. This option + only works with language level 2. When this is set, + gamma correction is clamped to #2.2#. + \item[Number of copies] Specifies how many copies should be + printed. This does {\bf not} affect the size of the output file. + \end{description} + */ + class Options + { + public: + /** Specifies the rendering mode */ + enum Mode { COLOR, FORE, BACK, BW }; + /** Selects the output format */ + enum Format { PS, EPS }; + /** Specifies the orientation of the output image */ + enum Orientation { PORTRAIT, LANDSCAPE, AUTO }; + /** Specifies the booklet mode */ + enum BookletMode { OFF, RECTO, VERSO, RECTOVERSO }; + private: + Format format; + int level; + Orientation orientation; + Mode mode; + int zoom; + bool color; + bool calibrate; + bool text; + double gamma; + int copies; + bool frame; + bool cropmarks; + BookletMode bookletmode; + int bookletmax; + int bookletalign; + int bookletfold; + int bookletxfold; + public: + /** Sets output image format to #PS# or #EPS# */ + void set_format(Format format); + /** Sets PostScript level (#1#, #2#, or #3#) */ + void set_level(int level); + /** Sets orientation (#LANDSCAPE# or #PORTRAIT#) */ + void set_orientation(Orientation orientation); + /** Sets \Ref{DjVuImage} rendering mode (#COLOR#, #BW#, #FORE#, #BACK#) */ + void set_mode(Mode mode); + /** Sets zoom factor. Zoom 0 means fit page. */ + void set_zoom(int zoom); + /** Affects automatic conversion to GreyScale mode. */ + void set_color(bool color); + /** Sets gamma correction factor. Ranges from #0.3# to #5.0#. */ + void set_gamma(double gamma); + /** Sets sRGB color calibration flag. */ + void set_sRGB(bool calibrate); + /** Specifies the number of copies to be printed. */ + void set_copies(int copies); + /** Specifies if a gray frame around the image should be printed. */ + void set_frame(bool on); + /** Specifies if crop marks should be printed. */ + void set_cropmarks(bool on); + /** Specifies if a shadow text should be printed. */ + void set_text(bool on); + /** Specifies the bookletmode */ + void set_bookletmode(BookletMode m); + /** Specifies the maximal number of pages in a booklet */ + void set_bookletmax(int m); + /** Specifies an offset (points) between + booklet recto(s) and verso(s). */ + void set_bookletalign(int m); + /** Specifies the margin (points) required to fold + the booklet (#fold# in points) and the margin + increase required for each sheet (#xfold# in millipoints). */ + void set_bookletfold(int fold, int xfold=0); + + /** Returns output image format (#PS# or #EPS#) */ + Format get_format(void) const { + return format; } + /** Returns PostScript level (#1# or #2#) */ + int get_level(void) const { + return level; } + /** Returns output image orientation (#PORTRAIT# or #LANDSCAPE#) */ + Orientation get_orientation(void) const { + return orientation; } + /** Returns \Ref{DjVuImage} rendering mode + (#COLOR#, #FORE#, #BACK#, #BW#) */ + Mode get_mode(void) const { + return mode; } + /** Returns output zoom factor (#FIT_PAGE# or #ONE_TO_ONE#) */ + int get_zoom(void) const { + return zoom; } + /** Returns color printing flag. */ + bool get_color(void) const { + return color; } + /** Returns sRGB color calibration flag. */ + bool get_sRGB(void) const { + return calibrate; } + /** Returns printer gamma correction factor */ + double get_gamma(void) const { + return ((calibrate) ? ((double)2.2) : gamma); } + /** Returns the number of copies, which will be printed by printer + This parameter does {\bf not} affect the size of output file */ + int get_copies(void) const { + return copies; } + /** Returns #TRUE# if there will be a gray frame */ + bool get_frame(void) const { + return frame; } + /** Returns #TRUE# if there will be a gray frame */ + bool get_cropmarks(void) const { + return cropmarks; } + /** Returns #TRUE# if there will be a shadow text printed */ + bool get_text(void) const { + return text; } + /** Returns the booklet mode */ + BookletMode get_bookletmode(void) const { + return bookletmode; } + /** Returns the booklet max size */ + int get_bookletmax(void) { + return bookletmax; } + /** Returns the booklet recto/verso offset */ + int get_bookletalign(void) { + return bookletalign; } + /** Returns the folding margin for sheet number #n#. */ + int get_bookletfold(int n=0) { + return bookletfold + (n*bookletxfold+500)/1000; } + /* Constructor */ + Options(void); + }; + + /** Describes current page processing stage. This is passed to + the #info_cb()# callback. See \Ref{set_info_cb}() for details. */ + enum Stage { DECODING, PRINTING }; + +private: + void (*refresh_cb)(void*); + void *refresh_cl_data; + void (*prn_progress_cb)(double, void*); + void *prn_progress_cl_data; + void (*dec_progress_cb)(double, void*); + void *dec_progress_cl_data; + void (*info_cb)(int,int,int,Stage,void*); + void *info_cl_data; + unsigned char ramp[256]; + GP<DecodePort> port; +protected: + void store_doc_prolog(ByteStream&,int,int,GRect*); + void store_doc_setup(ByteStream&); + void store_doc_trailer(ByteStream&); + void store_page_setup(ByteStream&,int,const GRect&,int align=0); + void store_page_trailer(ByteStream&); + void make_gamma_ramp(GP<DjVuImage>); + void print_image_lev1(ByteStream&,GP<DjVuImage>,const GRect&); + void print_image_lev2(ByteStream&,GP<DjVuImage>,const GRect&); + void print_bg(ByteStream&,GP<DjVuImage>,const GRect&); + void print_fg_3layer(ByteStream&,GP<DjVuImage>,const GRect&,unsigned char*); + void print_fg_2layer(ByteStream&,GP<DjVuImage>,const GRect&,unsigned char*); + void print_fg(ByteStream&,GP<DjVuImage>,const GRect&); + void print_image(ByteStream&,GP<DjVuImage>,const GRect&,GP<DjVuTXT>); + void parse_range(GP<DjVuDocument>,GUTF8String,GList<int>&); + GP<DjVuImage> decode_page(GP<DjVuDocument>,int,int,int); + void process_single_page(ByteStream&,GP<DjVuDocument>,int,int,int,int); + void process_double_page(ByteStream&,GP<DjVuDocument>,void*,int,int); + +public: + /** Options affecting the print result. Please refer to + \Ref{DjVuToPS::Options} for details. */ + Options options; + + /** @name Callbacks */ + //@{ + /** Refresh callback is a function, which will be called fairly + often while the image (document) is being printed. It can + be used to refresh a GUI, if necessary. + + @param refresh_cb Callback function to be called periodically + @param refresh_cl_data Pointer passed to #refresh_cb()# */ + void set_refresh_cb(void (*refresh_cb)(void*), void *refresh_cl_data); + /** Callback used to report the progress of printing. The progress is a + double number from 0 to 1. If an existing \Ref{DjVuImage} is printed, + this callback will be called at least twice: in the beginning and at the + end of printing. If a \Ref{DjVuDocument} is being printed, this + callback will be used to report printing progress of every page. To + learn the number of the page being printed you can use + \Ref{set_info_cb}() function. See \Ref{set_dec_progress_cb}() to find + out how to learn the decoding progress. + + @param cb Callback function to be called + @param data Pointer passed to #cb()#. */ + void set_prn_progress_cb(void (*cb)(double, void*), void *data); + /** Callback used to report the progress of decoding. The progress is a + double number from 0 to 1. This callback is only used when printing a + \Ref{DjVuDocument} in a multithreaded environment. In all other cases it + will not be called. Whenever you \Ref{print}() a page range from a + \Ref{DjVuDocument}, the #DjVuToPS# has to decode the mentioned pages + before writing them to the output \Ref{ByteStream}. This callback can be + helpful to find out the status of the decoding. See + \Ref{set_prn_progress_cb}() to find out how to learn the printing + progress. See \Ref{set_info_cb}() to learn how to find out the number + of the page being processed, the total number of pages and the number of + processed pages. + + @param cb Callback function to be called + @param data Pointer passed to #cb()#. */ + void set_dec_progress_cb(void (*cb)(double, void*), void *data); + /** Callback used to report the current printing stage of a + \Ref{DjVuDocument}. When printing a \Ref{DjVuDocument} ({\bf not} a + \Ref{DjVuImage}), the #DjVuToPS# class will decode and output every page + mentioned in the {\em page range}. Before decoding and outputing, it + will call this #info_cb()# callback in order to let you know about what + is going on. This can be quite useful in a GUI program to keep the user + informed. This function is not called when you print a \Ref{DjVuImage}. + + Description of the arguments passed to #info_cb#: + \begin{description} + \item[page_num] The number of the page being processed + \item[page_cnt] Counts how many pages have already been processed. + \item[tot_pages] Counts how many pages will be output enventually. + \item[stage] Describes the current processing stage + (#DECODING# or #PRINTING#). + \end{description} + @param cb Callback function to be called + @param data Pointer, which will be passed to #cb()#. */ + void set_info_cb(void (*cb)(int,int,int,Stage,void*), void *data); + //@} + + /** Prints the specified \Ref{DjVuImage} #dimg# into the + \Ref{ByteStream} #str#. The function will first scale + the image to fit the #img_rect#, then extract #prn_rect# + from the obtained bitmap and will output it in the + PostScript format. The function generates a legal PostScript + (or Encapsulated PostScript) file taking care of all comments + conforming to Document Structure Conventions v. 3.0. + + {\bf Warning:} The zoom factor specified in \Ref{Options} does + not affect the amount of data stored into the PostScript file. + It will be used by the PostScript code to additionally scale + the image. We cannot pre-scale it here, because we do not know + the future resolution of the printer. The #img_rect# and + #prn_rect# alone define how much data will be sent to printer. + + Using #img_rect# one can upsample or downsample the image prior + to sending it to the printer. + + @param str \Ref{ByteStream} where PostScript output will be sent + @param dimg \Ref{DjVuImage} to print + @param img_rect Rectangle to which the \Ref{DjVuImage} will be scaled. + Note that this parameters defines the amount of data + that will actually be sent to the printer. The PostScript + code can futher resize the image according to the + #zoom# parameter from the \Ref{Options} structure. + @param prn_rect Part of img_rect to send to printer. + @param override_dpi Optional parameter allowing you to override + dpi setting that would otherwise be extracted from #dimg# */ + void print(ByteStream&, GP<DjVuImage> dimg, + const GRect &prn_rect, const GRect &img_rect, + int override_dpi=-1 ); + + /** Outputs the specifies pages from the \Ref{DjVuDocument} into the + \Ref{ByteStream} in PostScript format. The function will generate a + multipage PostScript document conforming to PS DSC 3.0 by storing into + it every page mentioned in the #page_range#. + + If #page_range# is empty, all pages from the \Ref{DjVuDocument} #doc# + will be printed. The #page_range# is a set of ranges separated by + commas. Every range has this form: {\em start_page}[-{\em + end_page}]. {\em end_page} is optional and can be less than the {\em + start_page}, in which case the pages will be printed in the reverse + order. + + Examples: + \begin{itemize} + \item {\bf 1-10} - Will print pages 1 to 10. + \item {\bf 10-1} - Will print pages 1 to 10 in reverse order. + \item {\bf 1-10,12-20} - Will print pages 1 to 20 with page 11 skipped. + \end{itemize} */ + void print(ByteStream&, GP<DjVuDocument> doc, GUTF8String page_range); + void print(ByteStream&, GP<DjVuDocument> doc); + + + /** Default constructor. Initializes the class. */ + DjVuToPS(void); +}; + + +//**************************************************************************** +//******************************** DjVuToPS ********************************** +//**************************************************************************** + +//@} +// ------------ + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GBitmap.cpp b/kviewshell/plugins/djvu/libdjvu/GBitmap.cpp new file mode 100644 index 00000000..696367e7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GBitmap.cpp @@ -0,0 +1,1658 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GBitmap.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GBitmap.h" +#include "ByteStream.h" +#include "GRect.h" +#include "GString.h" +#include "GThreads.h" +#include "GException.h" +#include <string.h> + +// File "$Id: GBitmap.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $" +// - Author: Leon Bottou, 05/1997 + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +// ----- constructor and destructor + +GBitmap::~GBitmap() +{ +} + +void +GBitmap::destroy(void) +{ + gbytes_data.resize(0); + bytes = 0; + grle.resize(0); + grlerows.resize(0); + rlelength = 0; +} + +GBitmap::GBitmap() + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ +} + +GBitmap::GBitmap(int nrows, int ncolumns, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(nrows, ncolumns, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GBitmap::GBitmap(ByteStream &ref, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GBitmap::GBitmap(const GBitmap &ref) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, ref.border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GBitmap::GBitmap(const GBitmap &ref, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + + +GBitmap::GBitmap(const GBitmap &ref, const GRect &rect, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, rect, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + + + + + + +// ----- initialization + +void +GBitmap::init(int arows, int acolumns, int aborder) +{ + GMonitorLock lock(monitor()); + destroy(); + grays = 2; + nrows = arows; + ncolumns = acolumns; + border = aborder; + bytes_per_row = ncolumns + border; + int npixels = nrows * bytes_per_row + border; + gzerobuffer=zeroes(bytes_per_row + border); + if (npixels > 0) + { + gbytes_data.resize(npixels); + gbytes_data.clear(); + bytes = bytes_data; + } +} + + +void +GBitmap::init(const GBitmap &ref, int aborder) +{ + GMonitorLock lock(monitor()); + if (this != &ref) + { + GMonitorLock lock(ref.monitor()); + init(ref.nrows, ref.ncolumns, aborder); + grays = ref.grays; + unsigned char *row = bytes_data+border; + for (int n=0; n<nrows; n++, row+=bytes_per_row) + memcpy( (void*)row, (void*)ref[n], ncolumns ); + } + else if (aborder > border) + { + minborder(aborder); + } +} + + +void +GBitmap::init(const GBitmap &ref, const GRect &rect, int border) +{ + GMonitorLock lock(monitor()); + // test bitmap physical equality + if (this == &ref) + { + GBitmap tmp; + tmp.grays = grays; + tmp.border = border; + tmp.bytes_per_row = bytes_per_row; + tmp.ncolumns = ncolumns; + tmp.nrows = nrows; + tmp.bytes = bytes; + tmp.gbytes_data.swap(gbytes_data); + tmp.grle.swap(grle); + bytes = 0 ; + init(tmp, rect, border); + } + else + { + GMonitorLock lock(ref.monitor()); + // create empty bitmap + init(rect.height(), rect.width(), border); + grays = ref.grays; + // compute destination rectangle + GRect rect2(0, 0, ref.columns(), ref.rows() ); + rect2.intersect(rect2, rect); + rect2.translate(-rect.xmin, -rect.ymin); + // copy bits + if (! rect2.isempty()) + { + for (int y=rect2.ymin; y<rect2.ymax; y++) + { + unsigned char *dst = (*this)[y]; + const unsigned char *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = src[x]; + } + } + } +} + + +void +GBitmap::init(ByteStream &ref, int aborder) +{ + GMonitorLock lock(monitor()); + // Get magic number + char magic[2]; + magic[0] = magic[1] = 0; + ref.readall((void*)magic, sizeof(magic)); + char lookahead = '\n'; + int acolumns = read_integer(lookahead, ref); + int arows = read_integer(lookahead, ref); + init(arows, acolumns, aborder); + // go reading file + if (magic[0]=='P') + { + switch(magic[1]) + { + case '1': + grays = 2; + read_pbm_text(ref); + return; + case '2': + grays = 1 + read_integer(lookahead, ref); + if (grays > 256) + G_THROW("Cannot read PGM with depth greater than 8 bits."); + read_pgm_text(ref); + return; + case '4': + grays = 2; + read_pbm_raw(ref); + return; + case '5': + grays = 1 + read_integer(lookahead, ref); + if (grays > 256) + grays = 256; + read_pgm_raw(ref); + return; + } + } + else if (magic[0]=='R') + { + switch(magic[1]) + { + case '4': + grays = 2; + read_rle_raw(ref); + return; + } + } + G_THROW( ERR_MSG("GBitmap.bad_format") ); +} + +void +GBitmap::donate_data(unsigned char *data, int w, int h) +{ + destroy(); + grays = 2; + nrows = h; + ncolumns = w; + border = 0; + bytes_per_row = w; + gbytes_data.replace(data,w*h); + bytes = bytes_data; + rlelength = 0; +} + +void +GBitmap::donate_rle(unsigned char *rledata, unsigned int rledatalen, int w, int h) +{ + destroy(); + grays = 2; + nrows = h; + ncolumns = w; + border = 0; + bytes_per_row = w; +// rle = rledata; + grle.replace(rledata,rledatalen); + rlelength = rledatalen; +} + + +unsigned char * +GBitmap::take_data(size_t &offset) +{ + GMonitorLock lock(monitor()); + unsigned char *ret = bytes_data; + if (ret) offset = (size_t)border; + bytes_data=0; + return ret; +} + +const unsigned char * +GBitmap::get_rle(unsigned int &rle_length) +{ + if(!rle) + compress(); + rle_length=rlelength; + return rle; +} + +// ----- compression + + +void +GBitmap::compress() +{ + if (grays > 2) + G_THROW( ERR_MSG("GBitmap.cant_compress") ); + GMonitorLock lock(monitor()); + if (bytes) + { + grle.resize(0); + grlerows.resize(0); + rlelength = encode(rle,grle); + if (rlelength) + { + gbytes_data.resize(0); + bytes = 0; + } + } +} + +void +GBitmap::uncompress() +{ + GMonitorLock lock(monitor()); + if (!bytes && rle) + decode(rle); +} + + + +unsigned int +GBitmap::get_memory_usage() const +{ + unsigned long usage = sizeof(GBitmap); + if (bytes) + usage += nrows * bytes_per_row + border; + if (rle) + usage += rlelength; + return usage; +} + + +void +GBitmap::minborder(int minimum) +{ + if (border < minimum) + { + GMonitorLock lock(monitor()); + if (border < minimum) + { + if (bytes) + { + GBitmap tmp(*this, minimum); + bytes_per_row = tmp.bytes_per_row; + tmp.gbytes_data.swap(gbytes_data); + bytes = bytes_data; + tmp.bytes = 0; + } + border = minimum; + gzerobuffer=zeroes(border + ncolumns + border); + } + } +} + + +#define NMONITORS 8 +static GMonitor monitors[NMONITORS]; + +void +GBitmap::share() +{ + if (!monitorptr) + { + unsigned long x = (unsigned long)this; + monitorptr = &monitors[(x^(x>>5)) % NMONITORS]; + } +} + + +// ----- gray levels + +void +GBitmap::set_grays(int ngrays) +{ + if (ngrays<2 || ngrays>256) + G_THROW( ERR_MSG("GBitmap.bad_levels") ); + // set gray levels + GMonitorLock lock(monitor()); + grays = ngrays; + if (ngrays>2 && !bytes) + uncompress(); +} + +void +GBitmap::change_grays(int ngrays) +{ + GMonitorLock lock(monitor()); + // set number of grays + int ng = ngrays - 1; + int og = grays - 1; + set_grays(ngrays); + // setup conversion table + unsigned char conv[256]; + for (int i=0; i<256; i++) + { + if (i > og) + conv[i] = ng; + else + conv[i] = (i*ng+og/2)/og; + } + // perform conversion + for (int row=0; row<nrows; row++) + { + unsigned char *p = (*this)[row]; + for (int n=0; n<ncolumns; n++) + p[n] = conv[ p[n] ]; + } +} + +void +GBitmap::binarize_grays(int threshold) +{ + GMonitorLock lock(monitor()); + if (bytes) + for (int row=0; row<nrows; row++) + { + unsigned char *p = (*this)[row]; + for(unsigned char const * const pend=p+ncolumns;p<pend;++p) + { + *p = (*p>threshold) ? 1 : 0; + } + } + grays = 2; +} + + +// ----- additive blitting + +#undef min +#undef max + +static inline int +min(int x, int y) +{ + return (x < y ? x : y); +} + +static inline int +max(int x, int y) +{ + return (x > y ? x : y); +} + +void +GBitmap::blit(const GBitmap *bm, int x, int y) +{ + // Check boundaries + if ((x >= ncolumns) || + (y >= nrows) || + (x + (int)bm->columns() < 0) || + (y + (int)bm->rows() < 0) ) + return; + + // Perform blit + GMonitorLock lock1(monitor()); + GMonitorLock lock2(bm->monitor()); + if (bm->bytes) + { + if (!bytes_data) + uncompress(); + // Blit from bitmap + const unsigned char *srow = bm->bytes + bm->border; + unsigned char *drow = bytes_data + border + y*bytes_per_row + x; + for (int sr = 0; sr < bm->nrows; sr++) + { + if (sr+y>=0 && sr+y<nrows) + { + int sc = max(0, -x); + int sc1 = min(bm->ncolumns, ncolumns-x); + while (sc < sc1) + { + drow[sc] += srow[sc]; + sc += 1; + } + } + srow += bm->bytes_per_row; + drow += bytes_per_row; + } + } + else if (bm->rle) + { + if (!bytes_data) + uncompress(); + // Blit from rle + const unsigned char *runs = bm->rle; + unsigned char *drow = bytes_data + border + y*bytes_per_row + x; + int sr = bm->nrows - 1; + drow += sr * bytes_per_row; + int sc = 0; + char p = 0; + while (sr >= 0) + { + const int z = read_run(runs); + if (sc+z > bm->ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync") ); + int nc = sc + z; + if (p && sr+y>=0 && sr+y<nrows) + { + if (sc + x < 0) + sc = min(-x, nc); + while (sc < nc && sc + x<ncolumns) + drow[sc++] += 1; + } + sc = nc; + p = 1 - p; + if (sc >= bm->ncolumns) + { + p = 0; + sc = 0; + drow -= bytes_per_row; + sr -= 1; + } + } + } +} + + + +void +GBitmap::blit(const GBitmap *bm, int xh, int yh, int subsample) +{ + // Use code when no subsampling is necessary + if (subsample == 1) + { + blit(bm, xh, yh); + return; + } + + // Check boundaries + if ((xh >= ncolumns * subsample) || + (yh >= nrows * subsample) || + (xh + (int)bm->columns() < 0) || + (yh + (int)bm->rows() < 0) ) + return; + + // Perform subsampling blit + GMonitorLock lock1(monitor()); + GMonitorLock lock2(bm->monitor()); + if (bm->bytes) + { + if (!bytes_data) + uncompress(); + // Blit from bitmap + int dr, dr1, zdc, zdc1; + euclidian_ratio(yh, subsample, dr, dr1); + euclidian_ratio(xh, subsample, zdc, zdc1); + const unsigned char *srow = bm->bytes + bm->border; + unsigned char *drow = bytes_data + border + dr*bytes_per_row; + for (int sr = 0; sr < bm->nrows; sr++) + { + if (dr>=0 && dr<nrows) + { + int dc = zdc; + int dc1 = zdc1; + for (int sc=0; sc < bm->ncolumns; sc++) + { + if (dc>=0 && dc<ncolumns) + drow[dc] += srow[sc]; + if (++dc1 >= subsample) + { + dc1 = 0; + dc += 1; + } + } + } + // next line in source + srow += bm->bytes_per_row; + // next line fraction in destination + if (++dr1 >= subsample) + { + dr1 = 0; + dr += 1; + drow += bytes_per_row; + } + } + } + else if (bm->rle) + { + if (!bytes_data) + uncompress(); + // Blit from rle + int dr, dr1, zdc, zdc1; + euclidian_ratio(yh+bm->nrows-1, subsample, dr, dr1); + euclidian_ratio(xh, subsample, zdc, zdc1); + const unsigned char *runs = bm->rle; + unsigned char *drow = bytes_data + border + dr*bytes_per_row; + int sr = bm->nrows -1; + int sc = 0; + char p = 0; + int dc = zdc; + int dc1 = zdc1; + while (sr >= 0) + { + int z = read_run(runs); + if (sc+z > bm->ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync") ); + int nc = sc + z; + + if (dr>=0 && dr<nrows) + while (z>0 && dc<ncolumns) + { + int zd = subsample - dc1; + if (zd > z) + zd = z; + if (p && dc>=0) + drow[dc] += zd; + z -= zd; + dc1 += zd; + if (dc1 >= subsample) + { + dc1 = 0; + dc += 1; + } + } + // next fractional row + sc = nc; + p = 1 - p; + if (sc >= bm->ncolumns) + { + sc = 0; + dc = zdc; + dc1 = zdc1; + p = 0; + sr -= 1; + if (--dr1 < 0) + { + dr1 = subsample - 1; + dr -= 1; + drow -= bytes_per_row; + } + } + } + } +} + + + +// ------ load bitmaps + + +unsigned int +GBitmap::read_integer(char &c, ByteStream &bs) +{ + unsigned int x = 0; + // eat blank before integer + while (c==' ' || c=='\t' || c=='\r' || c=='\n' || c=='#') + { + if (c=='#') + do { } while (bs.read(&c,1) && c!='\n' && c!='\r'); + c = 0; + bs.read(&c, 1); + } + // check integer + if (c<'0' || c>'9') + G_THROW( ERR_MSG("GBitmap.not_int") ); + // eat integer + while (c>='0' && c<='9') + { + x = x*10 + c - '0'; + c = 0; + bs.read(&c, 1); + } + return x; +} + + +void +GBitmap::read_pbm_text(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + for (int n = nrows-1; n>=0; n--) + { + for (int c = 0; c<ncolumns; c++) + { + char bit = 0; + bs.read(&bit,1); + while (bit==' ' || bit=='\t' || bit=='\r' || bit=='\n') + { + bit=0; + bs.read(&bit,1); + } + if (bit=='1') + row[c] = 1; + else if (bit=='0') + row[c] = 0; + else + G_THROW( ERR_MSG("GBitmap.bad_PBM") ); + } + row -= bytes_per_row; + } +} + +void +GBitmap::read_pgm_text(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + char lookahead = '\n'; + for (int n = nrows-1; n>=0; n--) + { + for (int c = 0; c<ncolumns; c++) + row[c] = grays - 1 - read_integer(lookahead, bs); + row -= bytes_per_row; + } +} + +void +GBitmap::read_pbm_raw(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + for (int n = nrows-1; n>=0; n--) + { + unsigned char acc = 0; + unsigned char mask = 0; + for (int c = 0; c<ncolumns; c++) + { + if (!mask) + { + bs.read(&acc, 1); + mask = (unsigned char)0x80; + } + if (acc & mask) + row[c] = 1; + else + row[c] = 0; + mask >>= 1; + } + row -= bytes_per_row; + } +} + +void +GBitmap::read_pgm_raw(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + for (int n = nrows-1; n>=0; n--) + { + for (int c = 0; c<ncolumns; c++) + { + unsigned char x; + bs.read((void*)&x, 1); + row[c] = grays - 1 - x; + } + row -= bytes_per_row; + } +} + +void +GBitmap::read_rle_raw(ByteStream &bs) +{ + // interpret runs data + unsigned char h; + unsigned char p = 0; + unsigned char *row = bytes_data + border; + int n = nrows - 1; + row += n * bytes_per_row; + int c = 0; + while (n >= 0) + { + bs.read(&h, 1); + int x = h; + if (x >= (int)RUNOVERFLOWVALUE) + { + bs.read(&h, 1); + x = h + ((x - (int)RUNOVERFLOWVALUE) << 8); + } + if (c+x > ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync") ); + while (x-- > 0) + row[c++] = p; + p = 1 - p; + if (c >= ncolumns) + { + c = 0; + p = 0; + row -= bytes_per_row; + n -= 1; + } + } +} + + +// ------ save bitmaps + +void +GBitmap::save_pbm(ByteStream &bs, int raw) +{ + // check arguments + if (grays > 2) + G_THROW( ERR_MSG("GBitmap.cant_make_PBM") ); + GMonitorLock lock(monitor()); + // header + { + GUTF8String head; + head.format("P%c\n%d %d\n", (raw ? '4' : '1'), ncolumns, nrows); + bs.writall((void*)(const char *)head, head.length()); + } + // body + if(raw) + { + if(!rle) + compress(); + const unsigned char *runs=rle; + const unsigned char * const runs_end=rle+rlelength; + const int count=(ncolumns+7)>>3; + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,count); + while(runs<runs_end) + { + rle_get_bitmap(ncolumns,runs,buf,false); + bs.writall(buf,count); + } + }else + { + if (!bytes) + uncompress(); + const unsigned char *row = bytes + border; + int n = nrows - 1; + row += n * bytes_per_row; + while (n >= 0) + { + unsigned char eol='\n'; + for (int c=0; c<ncolumns;) + { + unsigned char bit= (row[c] ? '1' : '0'); + bs.write((void*)&bit, 1); + c += 1; + if (c==ncolumns || (c&(int)RUNMSBMASK)==0) + bs.write((void*)&eol, 1); + } + // next row + row -= bytes_per_row; + n -= 1; + } + } +} + +void +GBitmap::save_pgm(ByteStream &bs, int raw) +{ + // checks + GMonitorLock lock(monitor()); + if (!bytes) + uncompress(); + // header + GUTF8String head; + head.format("P%c\n%d %d\n%d\n", (raw ? '5' : '2'), ncolumns, nrows, grays-1); + bs.writall((void*)(const char *)head, head.length()); + // body + const unsigned char *row = bytes + border; + int n = nrows - 1; + row += n * bytes_per_row; + while (n >= 0) + { + if (raw) + { + for (int c=0; c<ncolumns; c++) + { + char x = grays - 1 - row[c]; + bs.write((void*)&x, 1); + } + } + else + { + unsigned char eol='\n'; + for (int c=0; c<ncolumns; ) + { + head.format("%d ", grays - 1 - row[c]); + bs.writall((void*)(const char *)head, head.length()); + c += 1; + if (c==ncolumns || (c&0x1f)==0) + bs.write((void*)&eol, 1); + } + } + row -= bytes_per_row; + n -= 1; + } +} + +void +GBitmap::save_rle(ByteStream &bs) +{ + // checks + if (ncolumns==0 || nrows==0) + G_THROW( ERR_MSG("GBitmap.not_init") ); + GMonitorLock lock(monitor()); + if (grays > 2) + G_THROW( ERR_MSG("GBitmap.cant_make_PBM") ); + // header + GUTF8String head; + head.format("R4\n%d %d\n", ncolumns, nrows); + bs.writall((void*)(const char *)head, head.length()); + // body + if (rle) + { + bs.writall((void*)rle, rlelength); + } + else + { + unsigned char *runs = 0; + GPBuffer<unsigned char> gruns(runs); + int size = encode(runs,gruns); + bs.writall((void*)runs, size); + } +} + + +// ------ runs + + +void +GBitmap::makerows( + int nrows, const int ncolumns, unsigned char *runs, unsigned char *rlerows[]) +{ + while (nrows-- > 0) + { + rlerows[nrows] = runs; + int c; + for(c=0;c<ncolumns;c+=GBitmap::read_run(runs)) + EMPTY_LOOP; + if (c > ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync2") ); + } +} + + +void +GBitmap::rle_get_bitmap ( + const int ncolumns, + const unsigned char *&runs, + unsigned char *bitmap, + const bool invert ) +{ + const int obyte_def=invert?0xff:0; + const int obyte_ndef=invert?0:0xff; + int mask=0x80,obyte=0; + for(int c=ncolumns;c > 0 ;) + { + int x=read_run(runs); + c-=x; + while((x--)>0) + { + if(!(mask>>=1)) + { + *(bitmap++) = obyte^obyte_def; + obyte=0; + mask=0x80; + for(;x>=8;x-=8) + { + *(bitmap++)=obyte_def; + } + } + } + if(c>0) + { + int x=read_run(runs); + c-=x; + while((x--)>0) + { + obyte|=mask; + if(!(mask>>=1)) + { + *(bitmap++)=obyte^obyte_def; + obyte=0; + mask=0x80; + for(;(x>8);x-=8) + *(bitmap++)=obyte_ndef; + } + } + } + } + if(mask != 0x80) + { + *(bitmap++)=obyte^obyte_def; + } +} + +int +GBitmap::rle_get_bits(int rowno, unsigned char *bits) const +{ + GMonitorLock lock(monitor()); + if (!rle) + return 0; + if (rowno<0 || rowno>=nrows) + return 0; + if (!rlerows) + { + const_cast<GPBuffer<unsigned char *> &>(grlerows).resize(nrows); + makerows(nrows,ncolumns,rle,const_cast<unsigned char **>(rlerows)); + } + int n = 0; + int p = 0; + int c = 0; + unsigned char *runs = rlerows[rowno]; + while (c < ncolumns) + { + const int x=read_run(runs); + if ((c+=x)>ncolumns) + c = ncolumns; + while (n<c) + bits[n++] = p; + p = 1-p; + } + return n; +} + + +int +GBitmap::rle_get_runs(int rowno, int *rlens) const +{ + GMonitorLock lock(monitor()); + if (!rle) + return 0; + if (rowno<0 || rowno>=nrows) + return 0; + if (!rlerows) + { + const_cast<GPBuffer<unsigned char *> &>(grlerows).resize(nrows); + makerows(nrows,ncolumns,rle,const_cast<unsigned char **>(rlerows)); + } + int n = 0; + int d = 0; + int c = 0; + unsigned char *runs = rlerows[rowno]; + while (c < ncolumns) + { + const int x=read_run(runs); + if (n>0 && !x) + { + n--; + d = d-rlens[n]; + } + else + { + rlens[n++] = (c+=x)-d; + d = c; + } + } + return n; +} + + +int +GBitmap::rle_get_rect(GRect &rect) const +{ + GMonitorLock lock(monitor()); + if (!rle) + return 0; + int area = 0; + unsigned char *runs = rle; + rect.xmin = ncolumns; + rect.ymin = nrows; + rect.xmax = 0; + rect.ymax = 0; + int r = nrows; + while (--r >= 0) + { + int p = 0; + int c = 0; + int n = 0; + while (c < ncolumns) + { + const int x=read_run(runs); + if(x) + { + if (p) + { + if (c < rect.xmin) + rect.xmin = c; + if ((c += x) > rect.xmax) + rect.xmax = c-1; + n += x; + } + else + { + c += x; + } + } + p = 1-p; + } + area += n; + if (n) + { + rect.ymin = r; + if (r > rect.ymax) + rect.ymax = r; + } + } + if (area==0) + rect.clear(); + return area; +} + + + +// ------ helpers + +int +GBitmap::encode(unsigned char *&pruns,GPBuffer<unsigned char> &gpruns) const +{ + // uncompress rle information + if (nrows==0 || ncolumns==0) + { + gpruns.resize(0); + return 0; + } + if (!bytes) + { + unsigned char *runs; + GPBuffer<unsigned char> gruns(runs,rlelength); + memcpy((void*)runs, rle, rlelength); + gruns.swap(gpruns); + return rlelength; + } + gpruns.resize(0); + // create run array + int pos = 0; + int maxpos = 1024 + ncolumns + ncolumns; + unsigned char *runs; + GPBuffer<unsigned char> gruns(runs,maxpos); + // encode bitmap as rle + const unsigned char *row = bytes + border; + int n = nrows - 1; + row += n * bytes_per_row; + while (n >= 0) + { + if (maxpos < pos+ncolumns+ncolumns+2) + { + maxpos += 1024 + ncolumns + ncolumns; + gruns.resize(maxpos); + } + + unsigned char *runs_pos=runs+pos; + const unsigned char * const runs_pos_start=runs_pos; + append_line(runs_pos,row,ncolumns); + pos+=(size_t)runs_pos-(size_t)runs_pos_start; + row -= bytes_per_row; + n -= 1; + } + // return result + gruns.resize(pos); + gpruns.swap(gruns); + return pos; +} + +void +GBitmap::decode(unsigned char *runs) +{ + // initialize pixel array + if (nrows==0 || ncolumns==0) + G_THROW( ERR_MSG("GBitmap.not_init") ); + bytes_per_row = ncolumns + border; + if (runs==0) + G_THROW( ERR_MSG("GBitmap.null_arg") ); + int npixels = nrows * bytes_per_row + border; + if (!bytes_data) + { + gbytes_data.resize(npixels); + bytes = bytes_data; + } + gbytes_data.clear(); + gzerobuffer=zeroes(bytes_per_row + border); + // interpret runs data + int c, n; + unsigned char p = 0; + unsigned char *row = bytes_data + border; + n = nrows - 1; + row += n * bytes_per_row; + c = 0; + while (n >= 0) + { + int x = read_run(runs); + if (c+x > ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync2") ); + while (x-- > 0) + row[c++] = p; + p = 1 - p; + if (c >= ncolumns) + { + c = 0; + p = 0; + row -= bytes_per_row; + n -= 1; + } + } + // Free rle data possibly attached to this bitmap + grle.resize(0); + grlerows.resize(0); + rlelength = 0; +#ifndef NDEBUG + check_border(); +#endif +} + +class GBitmap::ZeroBuffer : public GPEnabled +{ +public: + ZeroBuffer(const unsigned int zerosize); + unsigned char *zerobuffer; + GPBuffer<unsigned char> gzerobuffer; +}; + +GBitmap::ZeroBuffer::ZeroBuffer(const unsigned int zerosize) +: gzerobuffer(zerobuffer,zerosize) +{ + gzerobuffer.clear(); + GBitmap::zerobuffer=zerobuffer; + GBitmap::zerosize=zerosize; +} + +static const unsigned char static_zerobuffer[]= +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // 4096 + +int GBitmap::zerosize = sizeof(static_zerobuffer); +unsigned char *GBitmap::zerobuffer=const_cast<unsigned char *>(static_zerobuffer); + +GP<GBitmap::ZeroBuffer> +GBitmap::zeroes(int required) +{ + GMonitorLock lock(&monitors[0]); // any monitor would do + static GP<GBitmap::ZeroBuffer> gzerobuffer; + if (zerosize < required) + { + int z; + for(z=zerosize;z<required;z <<= 1) + EMPTY_LOOP; + z=(z+0xfff)&(~0xfff); + gzerobuffer=new GBitmap::ZeroBuffer((unsigned int)z); + } + return gzerobuffer; +} + + +// Fills a bitmap with the given value +void +GBitmap::fill(unsigned char value) +{ + GMonitorLock lock(monitor()); + for(unsigned int y=0; y<rows(); y++) + { + unsigned char* bm_y = (*this)[y]; + for(unsigned int x=0; x<columns(); x++) + bm_y[x] = value; + } +} + + +void +GBitmap::append_long_run(unsigned char *&data, int count) +{ + while (count > MAXRUNSIZE) + { + data[0] = data[1] = 0xff; + data[2] = 0; + data += 3; + count -= MAXRUNSIZE; + } + if (count < RUNOVERFLOWVALUE) + { + data[0] = count; + data += 1; + } + else + { + data[0] = (count>>8) + GBitmap::RUNOVERFLOWVALUE; + data[1] = (count & 0xff); + data += 2; + } +} + + +void +GBitmap::append_line(unsigned char *&data,const unsigned char *row, + const int rowlen,bool invert) +{ + const unsigned char *rowend=row+rowlen; + bool p=!invert; + while(row<rowend) + { + int count=0; + if ((p=!p)) + { + if(*row) + for(++count,++row;(row<rowend)&&*row;++count,++row) + EMPTY_LOOP; + } + else if(!*row) + { + for(++count,++row;(row<rowend)&&!*row;++count,++row) + EMPTY_LOOP; + } + append_run(data,count); + } +} + +#if 0 +static inline int +GetRowTDLRNR( + GBitmap &bit,const int row, const unsigned char *&startptr, + const unsigned char *&stopptr) +{ + stopptr=(startptr=bit[row])+bit.columns(); + return 1; +} + +static inline int +GetRowTDLRNR( + GBitmap &bit,const int row, const unsigned char *&startptr, + const unsigned char *&stopptr) +{ + stopptr=(startptr=bit[row])+bit.columns(); + return 1; +} + +static inline int +GetRowTDRLNR( + GBitmap &bit,const int row, const unsigned char *&startptr, + const unsigned char *&stopptr) +{ + startptr=(stopptr=bit[row]-1)+bit.columns(); + return -1; +} +#endif // 0 + +GP<GBitmap> +GBitmap::rotate(int count) +{ + GP<GBitmap> newbitmap=this; + if((count%=4)) + { + if( count & 0x01 ) + { + newbitmap = new GBitmap(ncolumns, nrows); + }else + { + newbitmap = new GBitmap(nrows, ncolumns); + } + GMonitorLock lock(monitor()); + if (!bytes_data) + uncompress(); + GBitmap &dbitmap = *newbitmap; + dbitmap.set_grays(grays); + switch(count) + { + case 1: // rotate 90 counter clockwise + { + const int lastrow = dbitmap.rows()-1; + for(int y=0; y<nrows; y++) + { + const unsigned char *r=operator[] (y); + for(int x=0,xnew=lastrow;xnew>=0; x++,xnew--) + { + dbitmap[xnew][y] = r[x]; + } + } + } + break; + case 2: // rotate 180 counter clockwise + { + const int lastrow = dbitmap.rows()-1; + const int lastcolumn = dbitmap.columns()-1; + for(int y=0,ynew=lastrow;ynew>=0; y++,ynew--) + { + const unsigned char *r=operator[] (y); + unsigned char *d=dbitmap[ynew]; + for(int xnew=lastcolumn;xnew>=0; r++,--xnew) + { + d[xnew] = *r; + } + } + } + break; + case 3: // rotate 270 counter clockwise + { + const int lastcolumn = dbitmap.columns()-1; + for(int y=0,ynew=lastcolumn;ynew>=0;y++,ynew--) + { + const unsigned char *r=operator[] (y); + for(int x=0; x<ncolumns; x++) + { + dbitmap[x][ynew] = r[x]; + } + } + } + break; + } + if(grays == 2) + { + compress(); + dbitmap.compress(); + } + } + return newbitmap; +} + +#ifndef NDEBUG +void +GBitmap::check_border() const +{int col ; + if (bytes) + { + const unsigned char *p = (*this)[-1]; + for (col=-border; col<ncolumns+border; col++) + if (p[col]) + G_THROW( ERR_MSG("GBitmap.zero_damaged") ); + for (int row=0; row<nrows; row++) + { + p = (*this)[row]; + for (col=-border; col<0; col++) + if (p[col]) + G_THROW( ERR_MSG("GBitmap.left_damaged") ); + for (col=ncolumns; col<ncolumns+border; col++) + if (p[col]) + G_THROW( ERR_MSG("GBitmap.right_damaged") ); + } + } +} +#endif + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GBitmap.h b/kviewshell/plugins/djvu/libdjvu/GBitmap.h new file mode 100644 index 00000000..74669c05 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GBitmap.h @@ -0,0 +1,673 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GBitmap.h,v 1.9 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GBITMAP_H_ +#define _GBITMAP_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GSmartPointer.h" +#ifndef NDEBUG +#include "GException.h" +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class GRect; +class GMonitor; +class ByteStream; + +/** @name GBitmap.h + + Files #"GBitmap.h"# and #"GBitmap.cpp"# implement class \Ref{GBitmap}. + Instances of this class represent bilevel or gray-level images. The + ``bottom left'' coordinate system is used consistently in the DjVu library. + Line zero of a bitmap is the bottom line in the bitmap. Pixels are + organized from left to right within each line. As suggested by its name, + class #GBitmap# was initially a class for bilevel images only. It was + extended to handle gray-level images when arose the need to render + anti-aliased images. This class has been a misnomer since then. + + {\bf ToDo} --- Class #GBitmap# can internally represent bilevel images + using a run-length encoded representation. Some algorithms may benefit + from a direct access to this run information. + + @memo + Generic support for bilevel and gray-level images. + @author + L\'eon Bottou <leonb@research.att.com> + @version + #$Id: GBitmap.h,v 1.9 2004/04/17 23:56:11 leonb Exp $# + + */ +//@{ + + +/** Bilevel and gray-level images. Instances of class #GBitmap# represent + bilevel or gray-level images. Images are usually represented using one + byte per pixel. Value zero represents a white pixel. A value equal to + the number of gray levels minus one represents a black pixel. The number + of gray levels is returned by the function \Ref{get_grays} and can be + manipulated by the functions \Ref{set_grays} and \Ref{change_grays}. + + The bracket operator returns a pointer to the bytes composing one line of + the image. This pointer can be used to read or write the image pixels. + Line zero represents the bottom line of the image. + + The memory organization is setup in such a way that you can safely read a + few pixels located in a small border surrounding all four sides of the + image. The width of this border can be modified using the function + \Ref{minborder}. The border pixels are initialized to zero and therefore + represent white pixels. You should never write anything into border + pixels because they are shared between images and between lines. */ + +class GBitmap : public GPEnabled +{ +protected: + GBitmap(void); + GBitmap(int nrows, int ncolumns, int border=0); + GBitmap(const GBitmap &ref); + GBitmap(const GBitmap &ref, int border); + GBitmap(const GBitmap &ref, const GRect &rect, int border=0); + GBitmap(ByteStream &ref, int border=0); +public: + virtual ~GBitmap(); + void destroy(void); + /** @name Construction. */ + //@{ + /** Constructs an empty GBitmap object. The returned GBitmap has zero rows + and zero columns. Use function \Ref{init} to change the size of the + image. */ + static GP<GBitmap> create(void) {return new GBitmap;} + + /** Constructs a GBitmap with #nrows# rows and #ncolumns# columns. All + pixels are initialized to white. The optional argument #border# + specifies the size of the optional border of white pixels surrounding + the image. The number of gray levels is initially set to #2#. */ + static GP<GBitmap> create(const int nrows, const int ncolumns, const int border=0) + {return new GBitmap(nrows,ncolumns, border); } + + /** Copy constructor. Constructs a GBitmap by replicating the size, the + border and the contents of GBitmap #ref#. */ + static GP<GBitmap> create(const GBitmap &ref) + {return new GBitmap(ref);} + + /** Constructs a GBitmap by copying the contents of GBitmap #ref#. + Argument #border# specifies the width of the optional border. */ + static GP<GBitmap> create(const GBitmap &ref, const int border) + { return new GBitmap(ref,border); } + + /** Constructs a GBitmap by copying a rectangular segment #rect# of GBitmap + #ref#. The optional argument #border# specifies the size of the + optional border of white pixels surrounding the image. */ + static GP<GBitmap> create(const GBitmap &ref, const GRect &rect, const int border=0) + { return new GBitmap(ref,rect,border); } + + /** Constructs a GBitmap by reading PBM, PGM or RLE data from ByteStream + #ref# into this GBitmap. The optional argument #border# specifies the + size of the optional border of white pixels surrounding the image. See + \Ref{PNM and RLE file formats} for more information. */ + static GP<GBitmap> create(ByteStream &ref, const int border=0) + { return new GBitmap(ref,border); } + + //@} + + /** @name Initialization. */ + //@{ + /** Resets this GBitmap size to #nrows# rows and #ncolumns# columns and sets + all pixels to white. The optional argument #border# specifies the size + of the optional border of white pixels surrounding the image. The + number of gray levels is initialized to #2#. */ + void init(int nrows, int ncolumns, int border=0); + /** Initializes this GBitmap with the contents of the GBitmap #ref#. The + optional argument #border# specifies the size of the optional border of + white pixels surrounding the image. */ + void init(const GBitmap &ref, int border=0); + /** Initializes this GBitmap with a rectangular segment #rect# of GBitmap + #ref#. The optional argument #border# specifies the size of the + optional border of white pixels surrounding the image. */ + void init(const GBitmap &ref, const GRect &rect, int border=0); + /** Reads PBM, PGM or RLE data from ByteStream #ref# into this GBitmap. The + previous content of the GBitmap object is lost. The optional argument + #border# specifies the size of the optional border of white pixels + surrounding the image. See \Ref{PNM and RLE file formats} for more + information. */ + void init(ByteStream &ref, int border=0); + /** Assignment operator. Initializes this GBitmap by copying the size, the + border and the contents of GBitmap #ref#. */ + GBitmap& operator=(const GBitmap &ref); + /** Initializes all the GBitmap pixels to value #value#. */ + void fill(unsigned char value); + //@} + + /** @name Accessing the pixels. */ + //@{ + /** Returns the number of rows (the image height). */ + unsigned int rows() const; + /** Returns the number of columns (the image width). */ + unsigned int columns() const; + /** Returns a constant pointer to the first byte of row #row#. + This pointer can be used as an array to read the row elements. */ + const unsigned char *operator[] (int row) const; + /** Returns a pointer to the first byte of row #row#. + This pointer can be used as an array to read or write the row elements. */ + unsigned char *operator[] (int row); + /** Returns the size of a row in memory (in pixels). This number is equal + to the difference between pointers to pixels located in the same column + in consecutive rows. This difference can be larger than the number of + columns in the image. */ + unsigned int rowsize() const; + /** Makes sure that the border is at least #minimum# pixels large. This + function does nothing it the border width is already larger than + #minimum#. Otherwise it reorganizes the data in order to provide a + border of #minimum# pixels. */ + void minborder(int minimum); + //@} + + /** @name Managing gray levels. */ + //@{ + /** Returns the number of gray levels. + Value #2# denotes a bilevel image. */ + int get_grays() const; + /** Sets the number of gray levels without changing the pixels. + Argument #grays# must be in range #2# to #256#. */ + void set_grays(int grays); + /** Changes the number of gray levels. The argument #grays# must be in the + range #2# to #256#. All the pixel values are then rescaled and clipped + in range #0# to #grays-1#. */ + void change_grays(int grays); + /** Binarizes a gray level image using a threshold. The number of gray + levels is reduced to #2# as in a bilevel image. All pixels whose value + was strictly greater than #threshold# are set to black. All other pixels + are set to white. */ + void binarize_grays(int threshold=0); + //@} + + /** @name Optimizing the memory usage. + The amount of memory used by bilevel images can be reduced using + function \Ref{compress}, which encodes the image using a run-length + encoding scheme. The bracket operator decompresses the image on demand. + A few highly optimized functions (e.g. \Ref{blit}) can use a run-length + encoded bitmap without decompressing it. There are unfortunate locking + issues associated with this capability (c.f. \Ref{share} and + \Ref{monitor}). */ + //@{ + /** Reduces the memory required for a bilevel image by using a run-length + encoded representation. Functions that need to access the pixel array + will decompress the image on demand. */ + void compress(); + /** Decodes run-length encoded bitmaps and recreate the pixel array. + This function is usually called by #operator[]# when needed. */ + void uncompress(); + /** Returns the number of bytes allocated for this image. */ + unsigned int get_memory_usage() const; + /** Returns a possibly null pointer to a \Ref{GMonitor} for this bitmap. + You should use this monitor to ensure that the data representation of the + bitmap will not change while you are using it. We suggest using + class \Ref{GMonitorLock} which properly handles null monitor pointers. */ + GMonitor *monitor() const; + /** Associates a \Ref{GMonitor} with this bitmap. This function should be + called on all bitmaps susceptible of being simultaneously used by + several threads. It will make sure that function \Ref{monitor} returns + a pointer to a suitable monitor for this bitmap. */ + void share(); + //@} + + /** @name Accessing RLE data. + The next functions are useful for processing bilevel images + encoded using the run length encoding scheme. These functions always return + zero if the bitmap is not RLE encoded. Function \Ref{compress} must + be used to ensure that the bitmap is RLE encoded. */ + //@{ + /** Gets the pixels for line #rowno#. One line of pixel is stored as + #unsigned char# values into array #bits#. Each pixel is either 1 or 0. + The array must be large enough to hold the whole line. The number of + pixels is returned. */ + + int rle_get_bits(int rowno, unsigned char *bits) const; + + /** Gets the bitmap line rle data passed. One line of pixel is stored one + with 8 bits per #unsigned char# in an array. The array must be large + enough to hold the whole line. */ + + static void rle_get_bitmap(const int ncolumns,const unsigned char *&runs, + unsigned char *bitmap, const bool invert ); + + /** Gets the lengths of all runs in line #rowno#. The array #rlens# must be + large enough to accomodate #w+2# integers where #w# is the number of + columns in the image. These integers represent the lengths of + consecutive runs of alternatively white or black pixels. Lengths can be + zero in order to allow for lines starting with black pixels. This + function returns the total number of runs in the line. */ + int rle_get_runs(int rowno, int *rlens) const; + /** Gets the smallest rectangle enclosing black pixels. + Rectangle rect gives the coordinates of the smallest rectangle + containing all black pixels. Returns the number of black pixels. */ + int rle_get_rect(GRect &rect) const; + //@} + + /** @name Additive Blit. + The blit functions are designed to efficiently construct an anti-aliased + image by copying smaller images at predefined locations. The image of a + page, for instance, is composed by copying the images of characters at + predefined locations. These functions are fairly optimized. They can + directly use compressed GBitmaps (see \Ref{compress}). We consider in + this section that each GBitmap comes with a coordinate system defined as + follows. Position (#0#,#0#) corresponds to the bottom left corner of + the bottom left pixel. Position (#1#,#1#) corresponds to the top right + corner of the bottom left pixel, which is also the bottom left corner of + the second pixel of the second row. Position (#w#,#h#), where #w# and + #h# denote the size of the GBitmap, corresponds to the top right corner + of the top right pixel. */ + + //@{ + /** Performs an additive blit of the GBitmap #bm#. The GBitmap #bm# is + first positioned above the current GBitmap in such a way that position + (#u#,#v#) in GBitmap #bm# corresponds to position (#u#+#x#,#v#+#y#) in + the current GBitmap. The value of each pixel in GBitmap #bm# is then + added to the value of the corresponding pixel in the current GBitmap. + + {\bf Example}: Assume for instance that the current GBitmap is initially + white (all pixels have value zero). This operation copies the pixel + values of GBitmap #bm# at position (#x#,#y#) into the current GBitmap. + Note that function #blit# does not change the number of gray levels in + the current GBitmap. You may have to call \Ref{set_grays} to specify + how the pixel values should be interpreted. */ + void blit(const GBitmap *bm, int x, int y); + /** Performs an additive blit of the GBitmap #bm# with anti-aliasing. The + GBitmap #bm# is first positioned above the current GBitmap in such a + way that position (#u#,#v#) in GBitmap #bm# corresponds to position + (#u#+#x#/#subsample#,#v#+#y#/#subsample#) in the current GBitmap. This + mapping results in a contraction of GBitmap #bm# by a factor + #subsample#. Each pixel of the current GBitmap can be covered by a + maximum of #subsample^2# pixels of GBitmap #bm#. The value of + each pixel in GBitmap #bm# is then added to the value of the + corresponding pixel in the current GBitmap. + + {\bf Example}: Assume for instance that the current GBitmap is initially + white (all pixels have value zero). Each pixel of the current GBitmap + then contains the sum of the gray levels of the corresponding pixels in + GBitmap #bm#. There are up to #subsample*subsample# such pixels. If + for instance GBitmap #bm# is a bilevel image (pixels can be #0# or #1#), + the pixels of the current GBitmap can take values in range #0# to + #subsample*subsample#. Note that function #blit# does not change the + number of gray levels in the current GBitmap. You must call + \Ref{set_grays} to indicate that there are #subsample^2+1# gray + levels. Since there is at most 256 gray levels, this also means that + #subsample# should never be greater than #15#. + + {\bf Remark}: Arguments #x# and #y# do not represent a position in the + coordinate system of the current GBitmap. According to the above + discussion, the position is (#x/subsample#,#y/subsample#). In other + words, you can position the blit with a sub-pixel resolution. The + resulting anti-aliasing changes are paramount to the image quality. */ + void blit(const GBitmap *shape, int x, int y, int subsample); + //@} + + /** @name Saving images. + The following functions write PBM, PGM and RLE files. PBM and PGM are + well known formats for bilevel and gray-level images. The RLE is a + simple run-length encoding scheme for bilevel images. These files can be + read using the ByteStream based constructor or initialization function. + See \Ref{PNM and RLE file formats} for more information. */ + //@{ + /** Saves the image into ByteStream #bs# using the PBM format. Argument + #raw# selects the ``Raw PBM'' (1) or the ``Ascii PBM'' (0) format. The + image is saved as a bilevel image. All non zero pixels are considered + black pixels. See section \Ref{PNM and RLE file formats}. */ + void save_pbm(ByteStream &bs, int raw=1); + /** Saves the image into ByteStream #bs# using the PGM format. Argument + #raw# selects the ``Raw PGM'' (1) or the ``Ascii PGM'' (0) format. The + image is saved as a gray level image. See section + \Ref{PNM and RLE file formats}. */ + void save_pgm(ByteStream &bs, int raw=1); + /** Saves the image into ByteStream #bs# using the RLE file format. + The image is saved as a bilevel image. All non zero pixels are + considered black pixels. See section \Ref{PNM and RLE file formats}. */ + void save_rle(ByteStream &bs); + //@} + + /** @name Stealing or borrowing the memory buffer (advanced). */ + //@{ + /** Steals the memory buffer of a GBitmap. This function returns the + address of the memory buffer allocated by this GBitmap object. The + offset of the first pixel in the bottom line is written into variable + #offset#. Other lines can be accessed using pointer arithmetic (see + \Ref{rowsize}). The GBitmap object no longer ``owns'' the buffer: you + must explicitly de-allocate the buffer using #operator delete []#. This + de-allocation should take place after the destruction or the + re-initialization of the GBitmap object. This function will return a + null pointer if the GBitmap object does not ``own'' the buffer in the + first place. */ + unsigned char *take_data(size_t &offset); + /** Initializes this GBitmap by borrowing a memory segment. The GBitmap + then directly addresses the memory buffer #data# provided by the user. + This buffer must be large enough to hold #w*h# bytes representing each + one pixel. The GBitmap object does not ``own'' the buffer: you must + explicitly de-allocate the buffer using #operator delete []#. This + de-allocation should take place after the destruction or the + re-initialization of the GBitmap object. */ + inline void borrow_data(unsigned char &data, int w, int h); + /** Same as borrow_data, except GBitmap will call #delete[]#. */ + void donate_data(unsigned char *data, int w, int h); + /** Return a pointer to the rle data. */ + const unsigned char *get_rle(unsigned int &rle_length); + /** Initializes this GBitmap by setting the size to #h# rows and #w# + columns, and directly addressing the memory buffer #rledata# provided by + the user. This buffer contains #rledatalen# bytes representing the + bitmap in run length encoded form. The GBitmap object then ``owns'' the + buffer (unlike #borrow_data#, but like #donate_data#) and will + deallocate this buffer when appropriate: you should not deallocate this + buffer yourself. The encoding of buffer #rledata# is similar to the + data segment of the RLE file format (without the header) documented in + \Ref{PNM and RLE file formats}. */ + void donate_rle(unsigned char *rledata, unsigned int rledatalen, int w, int h); + /** Static function for parsing run data. + This function returns one run length encoded at position #data# + and increments the pointer #data# accordingly. */ + static inline int read_run(const unsigned char *&data); + static inline int read_run(unsigned char *&data); + /** Static function for generating run data. + This function encoded run length #count# at position #data# + and increments the pointer accordingly. The pointer must + initially point to a large enough data buffer. */ + static inline void append_run(unsigned char *&data, int count); + /** Rotates bitmap by 90, 180 or 270 degrees anticlockwise + and returns a new pixmap, input bitmap is not changed. + count can be 1, 2, or 3 for 90, 180, 270 degree rotation. + It returns the same bitmap if not rotated. + The input bitmap will be uncompressed for rotation*/ + GP<GBitmap> rotate(int count=0); + //@} + +// These are constants, but we use enum because that works on older compilers. + enum {MAXRUNSIZE=0x3fff}; + enum {RUNOVERFLOWVALUE=0xc0}; + enum {RUNMSBMASK=0x3f}; + enum {RUNLSBMASK=0xff}; + + +protected: + // bitmap components + unsigned short nrows; + unsigned short ncolumns; + unsigned short border; + unsigned short bytes_per_row; + unsigned short grays; + unsigned char *bytes; + unsigned char *bytes_data; + GPBuffer<unsigned char> gbytes_data; + unsigned char *rle; + GPBuffer<unsigned char> grle; + unsigned char **rlerows; + GPBuffer<unsigned char *> grlerows; + unsigned int rlelength; +private: + GMonitor *monitorptr; +public: + class ZeroBuffer; + friend class ZeroBuffer; + GP<ZeroBuffer> gzerobuffer; +private: + static int zerosize; + static unsigned char *zerobuffer; + static GP<ZeroBuffer> zeroes(int ncolumns); + static unsigned int read_integer(char &lookahead, ByteStream &ref); + static void euclidian_ratio(int a, int b, int &q, int &r); + int encode(unsigned char *&pruns,GPBuffer<unsigned char> &gpruns) const; + void decode(unsigned char *runs); + void read_pbm_text(ByteStream &ref); + void read_pgm_text(ByteStream &ref); + void read_pbm_raw(ByteStream &ref); + void read_pgm_raw(ByteStream &ref); + void read_rle_raw(ByteStream &ref); + static void append_long_run(unsigned char *&data, int count); + static void append_line(unsigned char *&data,const unsigned char *row, + const int rowlen,bool invert=false); + static void makerows(int,const int, unsigned char *, unsigned char *[]); + friend class DjVu_Stream; + friend class DjVu_PixImage; +public: +#ifndef NDEBUG + void check_border() const; +#endif +}; + + +/** @name PNM and RLE file formats + + {\bf PNM} --- There are actually three PNM file formats: PBM for bilevel + images, PGM for gray level images, and PPM for color images. These + formats are widely used by popular image manipulation packages such as + NetPBM \URL{http://www.arc.umn.edu/GVL/Software/netpbm.html} or + ImageMagick \URL{http://www.wizards.dupont.com/cristy/}. + + {\bf RLE} --- The binary RLE file format is a simple run-length encoding + scheme for storing bilevel images. Encoding or decoding a RLE encoded + file is extremely simple. Yet RLE encoded files are usually much smaller + than the corresponding PBM encoded files. RLE files always begin with a + header line composed of:\\ + - the two characters #"R4"#,\\ + - one or more blank characters,\\ + - the number of columns, encoded using characters #"0"# to #"9"#,\\ + - one or more blank characters,\\ + - the number of lines, encoded using characters #"0"# to #"9"#,\\ + - exactly one blank character (usually a line-feed character). + + The rest of the file encodes a sequence of numbers representing the + lengths of alternating runs of white and black pixels. Lines are encoded + starting with the top line and progressing towards the bottom line. Each + line starts with a white run. The decoder knows that a line is finished + when the sum of the run lengths for that line is equal to the number of + columns in the image. Numbers in range #0# to #191# are represented by a + single byte in range #0x00# to #0xbf#. Numbers in range #192# to #16383# + are represented by a two byte sequence: the first byte, in range #0xc0# to + #0xff#, encodes the six most significant bits of the number, the second + byte encodes the remaining eight bits of the number. This scheme allows + for runs of length zero, which are useful when a line starts with a black + pixel, and when a very long run (whose length exceeds #16383#) must be + split into smaller runs. + + @memo + Simple image file formats. */ + +//@} + + +// ---------------- IMPLEMENTATION + +inline unsigned int +GBitmap::rows() const +{ + return nrows; +} + +inline unsigned int +GBitmap::columns() const +{ + return ncolumns; +} + +inline unsigned int +GBitmap::rowsize() const +{ + return bytes_per_row; +} + +inline int +GBitmap::get_grays() const +{ + return grays; +} + +inline unsigned char * +GBitmap::operator[](int row) +{ + if (!bytes) uncompress(); + if (row<0 || row>=nrows) { +#ifndef NDEBUG + if (zerosize < bytes_per_row + border) + G_THROW( ERR_MSG("GBitmap.zero_small") ); +#endif + return zerobuffer + border; + } + return &bytes[row * bytes_per_row + border]; +} + +inline const unsigned char * +GBitmap::operator[](int row) const +{ + if (!bytes) ((GBitmap*)this)->uncompress(); + if (row<0 || row>=nrows) { +#ifndef NDEBUG + if (zerosize < bytes_per_row + border) + G_THROW( ERR_MSG("GBitmap.zero_small") ); +#endif + return zerobuffer + border; + } + return &bytes[row * bytes_per_row + border]; +} + +inline GBitmap& +GBitmap::operator=(const GBitmap &ref) +{ + init(ref, ref.border); + return *this; +} + +inline GMonitor * +GBitmap::monitor() const +{ + return monitorptr; +} + +inline void +GBitmap::euclidian_ratio(int a, int b, int &q, int &r) +{ + q = a / b; + r = a - b*q; + if (r < 0) + { + q -= 1; + r += b; + } +} + + +inline int +GBitmap::read_run(unsigned char *&data) +{ + register int z=*data++; + return (z>=RUNOVERFLOWVALUE)? + ((z&~RUNOVERFLOWVALUE)<<8)|(*data++):z; +} + +inline int +GBitmap::read_run(const unsigned char *&data) +{ + register int z=*data++; + return (z>=RUNOVERFLOWVALUE)? + ((z&~RUNOVERFLOWVALUE)<<8)|(*data++):z; +} + +inline void +GBitmap::append_run(unsigned char *&data, int count) +{ + if (count < RUNOVERFLOWVALUE) + { + data[0] = count; + data += 1; + } + else if (count <= MAXRUNSIZE) + { + data[0] = (count>>8) + GBitmap::RUNOVERFLOWVALUE; + data[1] = (count & 0xff); + data += 2; + } + else + { + append_long_run(data, count); + } +} + + +inline void +GBitmap::borrow_data(unsigned char &data,int w,int h) +{ + donate_data(&data,w,h); + bytes_data=0; +} + +// ---------------- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GContainer.cpp b/kviewshell/plugins/djvu/libdjvu/GContainer.cpp new file mode 100644 index 00000000..2019439c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GContainer.cpp @@ -0,0 +1,802 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GContainer.cpp,v 1.12 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GContainer.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ------------------------------------------------------------ +// DYNAMIC ARRAYS +// ------------------------------------------------------------ + + +GArrayBase::GArrayBase(const GArrayBase &ref) + : traits(ref.traits), + gdata(data,0,1), + minlo(ref.minlo), maxhi(ref.maxhi), + lobound(ref.lobound), hibound(ref.hibound) +{ + if (maxhi >= minlo) + gdata.resize(traits.size * (maxhi - minlo + 1),1); + if (hibound >= lobound) + traits.copy(traits.lea(data, lobound-minlo), + traits.lea(ref.data, lobound-minlo), + hibound - lobound + 1, 0); +} + + +GArrayBase::GArrayBase(const GCONT Traits &traits) + : traits(traits), + gdata(data,0,1), + minlo(0), maxhi(-1), + lobound(0), hibound(-1) +{ +} + + +GArrayBase::GArrayBase(const GCONT Traits &traits, int lobound, int hibound) + : traits(traits), + gdata(data,0,1), + minlo(0), maxhi(-1), + lobound(0), hibound(-1) +{ + resize(lobound, hibound); +} + + +GArrayBase::~GArrayBase() +{ + G_TRY { empty(); } G_CATCH_ALL { } G_ENDCATCH; +} + + +GArrayBase & +GArrayBase::operator= (const GArrayBase &ga) +{ + if (this == &ga) + return *this; + empty(); + if (ga.hibound >= ga.lobound) + { + resize(ga.lobound, ga.hibound); + traits.copy( traits.lea(data, lobound-minlo), + traits.lea(ga.data, ga.lobound-ga.minlo), + hibound - lobound + 1, 0 ); + } + return *this; +} + + +void +GArrayBase::steal(GArrayBase &ga) +{ + if (this != &ga) + { + empty(); + lobound = ga.lobound; + hibound = ga.hibound; + minlo = ga.minlo; + maxhi = ga.maxhi; + data = ga.data; + ga.data = 0; + ga.lobound = ga.minlo = 0; + ga.hibound = ga.maxhi = -1; + } +} + + +void +GArrayBase::empty() +{ + resize(0, -1); +} + + +void +GArrayBase::touch(int n) +{ + int nlo = (n<lobound ? n : lobound); + int nhi = (n>hibound ? n : hibound); + if (hibound < lobound) + nlo = nhi = n; + resize(nlo, nhi); +} + + +void +GArrayBase::resize(int lo, int hi) +{ + // Validation + int nsize = hi - lo + 1; + if (nsize < 0) + G_THROW( ERR_MSG("GContainer.bad_args") ); + // Destruction + if (nsize == 0) + { + if (hibound >= lobound) + traits.fini( traits.lea(data, lobound-minlo), hibound-lobound+1 ); + if (data) + gdata.resize(0,1); + lobound = minlo = 0; + hibound = maxhi = -1; + return; + } + // Simple extension + if (lo >= minlo && hi <= maxhi) + { + if (lobound > lo) + traits.init( traits.lea(data,lo-minlo), lobound-lo ); + else if (lo > lobound) + traits.fini( traits.lea(data,lobound-minlo), lo-lobound ); + if (hi > hibound) + traits.init( traits.lea(data,hibound-minlo+1), hi-hibound ); + else if (hibound > hi) + traits.fini( traits.lea(data,hi-minlo+1), hibound-hi ); + lobound = lo; + hibound = hi; + return; + } + // General case + int nminlo = minlo; + int nmaxhi = maxhi; + if (nminlo > nmaxhi) + nminlo = nmaxhi = lo; + while (nminlo > lo) { + int incr = nmaxhi - nminlo; + nminlo -= (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + while (nmaxhi < hi) { + int incr = nmaxhi - nminlo; + nmaxhi += (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + // allocate and move + int beg = lo; + int end = hi; + int bytesize = traits.size * (nmaxhi-nminlo+1); + void *ndata; + GPBufferBase gndata(ndata,bytesize,1); +#if GCONTAINER_ZERO_FILL + memset(ndata, 0, bytesize); // slower but cleaner +#endif + if (lo < lobound) + { traits.init( traits.lea(ndata,lo-nminlo), lobound-lo ); beg=lobound; } + else if (lobound < lo) + { traits.fini( traits.lea(data,lobound-minlo), lo-lobound); } + if (hibound < hi) + { traits.init( traits.lea(ndata,hibound-nminlo+1), hi-hibound ); end=hibound; } + else if (hi < hibound) + { traits.fini( traits.lea(data, hi-minlo+1), hibound-hi ); } + if (end >= beg) + { traits.copy( traits.lea(ndata, beg-nminlo), + traits.lea(data, beg-minlo), + end-beg+1, 1 ); } + // free and replace + void *tmp=data; + data=ndata; + ndata=tmp; + minlo = nminlo; + maxhi = nmaxhi; + lobound = lo; + hibound = hi; +} + + +void +GArrayBase::shift(int disp) +{ + lobound += disp; + hibound += disp; + minlo += disp; + maxhi += disp; +} + + +void +GArrayBase::del(int n, int howmany) +{ + if (howmany < 0) + G_THROW( ERR_MSG("GContainer.bad_howmany") ); + if (howmany == 0) + return; + if ( n < lobound || n+(int)howmany-1 > hibound) + G_THROW( ERR_MSG("GContainer.bad_sub2") ); + traits.fini( traits.lea(data, n-minlo), howmany ); + if ( n+howmany-1 < hibound) + traits.copy( traits.lea(data, n-minlo), + traits.lea(data, n-minlo+howmany), + hibound - (n+howmany-1), 1 ); + hibound = hibound - howmany; +} + + +static inline void * +nextptr(void *p, int elsize) +{ + return (void*)(((char*)p) + elsize); +} + + +static inline void * +prevptr(void *p, int elsize) +{ + return (void*)(((char*)p) - elsize); +} + + +void +GArrayBase::ins(int n, const void *src, int howmany) +{ + if (howmany < 0) + G_THROW( ERR_MSG("GContainer.bad_howmany") ); + if (howmany == 0) + return; + // Make enough room + if (hibound+howmany > maxhi) + { + int nmaxhi = maxhi; + while (nmaxhi < hibound+howmany) + nmaxhi += (nmaxhi < 8 ? 8 : (nmaxhi > 32768 ? 32768 : nmaxhi)); + int bytesize = traits.size * (nmaxhi-minlo+1); + void *ndata; // = operator new (bytesize); + GPBufferBase gndata(ndata,bytesize,1); + memset(ndata, 0, bytesize); // slower but cleaner + if (hibound >= lobound) + traits.copy( traits.lea(ndata, lobound-minlo), + traits.lea(data, lobound-minlo), + hibound-lobound+1, 1 ); + maxhi = nmaxhi; + void *tmp=data; + data = ndata; + ndata=tmp; + } + // Shift data + int elsize = traits.size; + void *pdst = traits.lea(data, hibound+howmany-minlo); + void *psrc = traits.lea(data, hibound-minlo); + void *pend = traits.lea(data, n-minlo); + while ((char*)psrc >= (char*)pend) + { + traits.copy( pdst, psrc, 1, 1 ); + pdst = prevptr(pdst, elsize); + psrc = prevptr(psrc, elsize); + } + hibound += howmany; + // Initialize new data + if (! src) + { + traits.init( traits.lea(data, n-minlo), howmany ); + hibound += howmany; + return; + } + // Initialize new data with copy constructor + pdst = traits.lea(data, n-minlo); + pend = traits.lea(data, n+howmany-minlo); + while ((char*)pdst < (char*)pend) + { + traits.copy( pdst, src, 1, 0); + pdst = nextptr(pdst, elsize); + } +} + + + +// ------------------------------------------------------------ +// GPOSITION +// ------------------------------------------------------------ + + + +void +GPosition::throw_invalid(void *c) const +{ + if (c != cont) + G_THROW( ERR_MSG("GContainer.bad_pos_cont") ); + else if (! ptr) + G_THROW( ERR_MSG("GContainer.bad_pos_null") ); + else + G_THROW( ERR_MSG("GContainer.bad_pos") ); +} + + + +// ------------------------------------------------------------ +// DOUBLY LINKED LISTS +// ------------------------------------------------------------ + + +GListBase::GListBase(const Traits& traits) + : traits(traits) +{ + nelem = 0; + head.next = head.prev = 0; +} + + +GListBase::GListBase(const GListBase &ref) + : traits(ref.traits) +{ + nelem = 0; + head.next = head.prev = 0; + GListBase::operator= (ref); +} + +#include <stdio.h> +GListBase::~GListBase() +{ + G_TRY + { + empty(); + } + G_CATCH_ALL + { + } + G_ENDCATCH; +} + + +void +GListBase::append(Node *n) +{ + // Link + n->next = 0; + n->prev = head.prev; + head.prev = n; + if (n->prev) + n->prev->next = n; + else + head.next = n; + // Finish + nelem += 1; +} + + +void +GListBase::prepend(Node *n) +{ + // Link + n->next = head.next; + n->prev = 0; + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + // Finish + nelem += 1; +} + + +void +GListBase::insert_after(GPosition pos, Node *n) +{ + // Prepare + if (pos.ptr) + { + if (pos.cont != (void*)this) + pos.throw_invalid((void*)this); + Node *p = pos.ptr; + n->prev = p; + n->next = p->next; + } + else + { + n->prev = 0; + n->next = head.next; + } + // Link + if (n->prev) + n->prev->next = n; + else + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + // Finish + nelem += 1; +} + + +void +GListBase::insert_before(GPosition pos, Node *n) +{ + // Prepare + if (pos.ptr) + { + if (pos.cont != (void*)this) + pos.throw_invalid((void*)this); + Node *p = pos.ptr; + n->prev = p->prev; + n->next = p; + } + else + { + n->prev = head.prev; + n->next = 0; + } + // Link + if (n->prev) + n->prev->next = n; + else + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + // Finish + nelem += 1; +} + + +void +GListBase::insert_before(GPosition pos, GListBase &fromlist, GPosition &frompos) +{ + // Check + if (!frompos.ptr || frompos.cont != (void*)&fromlist) + frompos.throw_invalid((void*)&fromlist); + if (pos.ptr && pos.cont != (void*)this) + pos.throw_invalid((void*)this); + // Update frompos + Node *n = frompos.ptr; + frompos.ptr = n->next; + if (pos.ptr == n) return; + // Unlink + if (n->next) + n->next->prev = n->prev; + else + fromlist.head.prev = n->prev; + if (n->prev) + n->prev->next = n->next; + else + fromlist.head.next = n->next; + fromlist.nelem -= 1; + // Prepare insertion + if (pos.ptr) + { + Node *p = pos.ptr; + n->prev = p->prev; + n->next = p; + } + else + { + n->prev = head.prev; + n->next = 0; + } + // Link + if (n->prev) + n->prev->next = n; + else + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + nelem += 1; +} + + +void +GListBase::del(GPosition &pos) +{ + // Check + if (!pos.ptr || pos.cont != (void*)this) return; + // Unlink + Node *n = pos.ptr; + if (n->next) + n->next->prev = n->prev; + else + head.prev = n->prev; + if (n->prev) + n->prev->next = n->next; + else + head.next = n->next; + // Finish + nelem -= 1; + traits.fini( (void*)n, 1); + operator delete ( (void*)n ); + pos.ptr = 0; +} + + +GPosition +GListBase::nth(unsigned int n) const +{ + Node *p = 0; + if ((int)n < nelem) + for (p=head.next; p; p=p->next) + if ( n-- == 0) + break; + return GPosition(p, (void*)this); +} + + +void +GListBase::empty() +{ + Node *n=head.next; + while (n) + { + Node *p = n->next; + traits.fini( (void*)n, 1 ); + operator delete ( (void*)n ); + n = p; + } + head.next = head.prev = 0; + nelem = 0; +} + + +GListBase & +GListBase::operator= (const GListBase & ref) +{ + if (this == &ref) + return *this; + empty(); + for(Node *n = ref.head.next; n; n=n->next) + { + Node *m = (Node*) operator new (traits.size); + traits.copy( (void*)m, (void*)n, 1, 0); + append(m); + } + return *this; +} + + + + + +// ------------------------------------------------------------ +// ASSOCIATIVE MAPS +// ------------------------------------------------------------ + + + + +GSetBase::GSetBase(const Traits &traits) + : traits(traits), nelems(0), nbuckets(0), + gtable(table), first(0) +{ + rehash(17); +} + + +GSetBase::GSetBase(const GSetBase &ref) + : traits(ref.traits), + nelems(0), nbuckets(0), gtable(table), first(0) +{ + GSetBase::operator= (ref); +} + + +GSetBase::~GSetBase() +{ + G_TRY { empty(); } G_CATCH_ALL { } G_ENDCATCH; +// delete [] table; +} + + +GCONT HNode * +GSetBase::hashnode(unsigned int hashcode) const +{ + int bucket = hashcode % nbuckets; + return table[bucket]; +} + +GCONT HNode * +GSetBase::installnode(HNode *n) +{ + // Rehash if table is more than 60% full + if (nelems*3 > nbuckets*2) + rehash( 2*nbuckets - 1 ); + // Create and insert + insertnode(n); + return n; +} + +void +GSetBase::insertnode(HNode *n) +{ + int bucket = n->hashcode % nbuckets; + n->prev = n->hprev = table[bucket]; + if (n->prev) + { + // bucket was not empty + n->next = n->prev->next; + n->prev->next = n; + if (n->next) + n->next->prev = n; + } + else + { + // bucket was empty. + n->next = first; + first = n; + if (n->next) + n->next->prev = n; + } + // finish + table[bucket] = n; + nelems += 1; +} + + +void +GSetBase::deletenode(GCONT HNode *n) +{ + if (n == 0) + return; + int bucket = n->hashcode % nbuckets; + // Regular links + if (n->next) + n->next->prev = n->prev; + if (n->prev) + n->prev->next = n->next; + else + first = (HNode*)(n->next); + // HPrev links + if (table[bucket] == n) + table[bucket] = n->hprev; + else + ((HNode*)(n->next))->hprev = n->hprev; + // Delete entry + traits.fini( (void*)n, 1 ); + operator delete ( (void*)n ); + nelems -= 1; +} + + +void +GSetBase::rehash(int newbuckets) +{ + // Save chain of nodes + Node *n = first; + // Simulate an empty map + nelems = 0; + first = 0; + // Allocate a new empty bucket table +// delete [] table; + gtable.resize(0); + nbuckets = newbuckets; + typedef HNode *HNodePtr; +// table = new HNodePtr[nbuckets]; + gtable.resize(nbuckets); + gtable.clear(); +// for (int i=0; i<nbuckets; i++) +// table[i] = 0; + // Insert saved nodes + while (n) + { + Node *p = n->next; + insertnode((HNode*)n); + n = p; + } +} + + +GSetBase& +GSetBase::operator=(const GSetBase &ref) +{ + if (this == &ref) + return *this; + empty(); + rehash(ref.nbuckets); + for (Node *n = ref.first; n; n=n->next) + { + HNode *m = (HNode*) operator new (traits.size); + traits.copy( (void*)m, (void*)n, 1, 0); + insertnode(m); + } + return *this; +} + + +GPosition +GSetBase::firstpos() const +{ + return GPosition(first, (void*)this); +} + + +void +GSetBase::del(GPosition &pos) +{ + if (pos.ptr && pos.cont==(void*)this) + { + deletenode((HNode*)pos.ptr); + pos.ptr = 0; + } +} + +void +GSetBase::empty() +{ + HNode *n = first; + while (n) + { + HNode *p = (HNode*)(n->next); + traits.fini( (void*)n, 1 ); + operator delete ( (void*)n ); + n = p; + } + first = 0; + nelems = 0; + gtable.clear(); +// for (int i=0; i<nbuckets; i++) +// table[i] = 0; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GContainer.h b/kviewshell/plugins/djvu/libdjvu/GContainer.h new file mode 100644 index 00000000..d21838dc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GContainer.h @@ -0,0 +1,1366 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GContainer.h,v 1.15 2004/05/13 15:16:34 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GCONTAINER_H_ +#define _GCONTAINER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GException.h" +#include "GSmartPointer.h" +#include <string.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// Supports old iterators (first/last/next/prev) on lists and maps? +#ifndef GCONTAINER_OLD_ITERATORS +#define GCONTAINER_OLD_ITERATORS 1 +#endif + +// Check array bounds at runtime ? +#ifndef GCONTAINER_BOUNDS_CHECK +#define GCONTAINER_BOUNDS_CHECK 1 +#endif + +// Clears allocated memory prior to running constructors ? +#ifndef GCONTAINER_ZERO_FILL +#define GCONTAINER_ZERO_FILL 1 +#endif + +// Avoid member templates (needed by old compilers) +#ifndef GCONTAINER_NO_MEMBER_TEMPLATES +#if defined(__GNUC__) && (__GNUC__==2) && (__GNUC_MINOR__<91) +#define GCONTAINER_NO_MEMBER_TEMPLATES 1 +#elif defined(_MSC_VER) && !defined(__ICL) +#define GCONTAINER_NO_MEMBER_TEMPLATES 1 +#elif defined(__MWERKS__) +#define GCONTAINER_NO_MEMBER_TEMPLATES 1 +#else +#define GCONTAINER_NO_MEMBER_TEMPLATES 0 +#endif +#endif + +// Define typename when needed +#ifndef GCONTAINER_NO_TYPENAME +#define GCONTAINER_NO_TYPENAME 0 +#endif +#if GCONTAINER_NO_TYPENAME +#define typename /**/ +#endif + + +/** @name GContainer.h + + Files #"GContainer.h"# and #"GContainer.cpp"# implement three main + template class for generic containers. + Class #GArray# (see \Ref{Dynamic Arrays}) implements an array of objects + with variable bounds. Class #GList# (see \Ref{Doubly Linked Lists}) + implements a doubly linked list of objects. Class #GMap# (see + \Ref{Associative Maps}) implements a hashed associative map. The + container templates are not thread-safe. Thread safety can be implemented + using the facilities provided in \Ref{GThreads.h}. + + @memo + Template class for generic containers. + @author + L\'eon Bottou <leonb@research.att.com> -- initial implementation.\\ + Andrei Erofeev <eaf@geocities.com> -- bug fixes. + @version + #$Id: GContainer.h,v 1.15 2004/05/13 15:16:34 leonb Exp $# */ +//@{ + + + +// ------------------------------------------------------------ +// HELPER CLASSES +// ------------------------------------------------------------ + + + +/* Namespace for containers support classes. This class is used as a + namespace for global identifiers related to the implementation of + containers. It is inherited by all container objects. This is disabled by + defining compilation symbol #GCONTAINER_NO_MEMBER_TEMPATES# to 1. */ + + +#ifdef _MSC_VER +// Language lawyer say MS is wrong on that one. +// Cf section 5.4.7 in november 1997 draft. +#pragma warning( disable : 4243 ) +#endif + + +// GPEnabled inhertenced removed again so the code works on more machines. +class GCont +#if GCONTAINER_NO_MEMBER_TEMPLATES +{ +}; +#else +{ +public: +#endif + // --- Pointers to type management functions + struct Traits + { + int size; + void *(*lea) (void *base, int n); + void (*init) (void *dst, int n); + void (*copy) (void *dst, const void* src, int n, int zap); + void (*fini) (void *dst, int n); + }; +#if !GCONTAINER_NO_MEMBER_TEMPLATES +protected: +#endif + // --- Management of simple types + template <int SZ> class TrivTraits + { + public: + // The unique object + static const Traits & traits(); + // Offset in an array of T + static void * lea(void* base, int n) + { return (void*)( ((char*)base) + SZ*n ); } + // Trivial default constructor + static void init(void* dst, int n) {} + // Trivial copy constructor + static void copy(void* dst, const void* src, int n, int ) + { memcpy(dst, src, SZ*n); } + // Trivial destructor + static void fini(void* dst, int n) {} + }; + // --- Management of regular types + template <class T> class NormTraits + { + public: + // The unique object + static const Traits & traits(); + // Offset in an array of T + static void * lea(void* base, int n) + { return (void*)( ((T*)base) + n ); } + // Template based default constructor + static void init(void* dst, int n) + { T* d = (T*)dst; while (--n>=0) { new ((void*)d) T; d++; } } + // Template based copy constructor + static void copy(void* dst, const void* src, int n, int zap) + { T* d = (T*)dst; const T *s = (const T*)src; + while (--n>=0) { new ((void*)d) T(*s); if (zap) { s->T::~T(); }; d++; s++; } } + // Template based destructor + static void fini(void* dst, int n) + { T* d = (T*)dst; while (--n>=0) { d->T::~T(); d++; } } + }; + // --- Base class for list nodes + struct Node + { + Node *next; + Node *prev; + }; + // -- Class for list nodes + template <class T> struct ListNode : public Node + { + T val; + }; + // -- Class for map nodes showing the hash + struct HNode : public Node + { + HNode *hprev; + unsigned int hashcode; + }; + // -- Class for map nodes showing the hash and the key + template <class K> struct SetNode : public HNode + { + K key; + }; + // -- Class for map nodes with everything + template <class K, class T> struct MapNode : public SetNode<K> + { + T val; + }; +#if !GCONTAINER_NO_MEMBER_TEMPLATES +}; +#endif + + +#if !GCONTAINER_NO_MEMBER_TEMPLATES +#define GCONT GCont:: +#else +#define GCONT +#endif + +template <int SZ> const GCONT Traits & +GCONT TrivTraits<SZ>::traits() +{ + static const Traits theTraits = { + SZ, + TrivTraits<SZ>::lea, + TrivTraits<SZ>::init, + TrivTraits<SZ>::copy, + TrivTraits<SZ>::fini + }; + return theTraits; +} + +template <class T> const GCONT Traits & +GCONT NormTraits<T>::traits() +{ + static const Traits theTraits = { + sizeof(T), + NormTraits<T>::lea, + NormTraits<T>::init, + NormTraits<T>::copy, + NormTraits<T>::fini + }; + return theTraits; +} + + +// ------------------------------------------------------------ +// DYNAMIC ARRAYS +// ------------------------------------------------------------ + + +/** @name Dynamic Arrays + + These class implement arrays of objects of any type. Each element is + identified by an integer subscript. The valid subscripts range is defined + by dynamically adjustable lower- and upper-bounds. Besides accessing and + setting elements, member functions are provided to insert or delete + elements at specified positions. + + Class \Ref{GArrayTemplate} implements all methods for manipulating arrays + of type #TYPE#. You should not however create instances of this class. + You should instead use one of the following classes: + \begin{itemize} + \item Class \Ref{GArray<TYPE>} is the most general class, + \item Class \Ref{GTArray<TYPE>} is more efficient, but only works for + types that do not require sophisticated constructors or destructors, + such as the plain old C types (e.g. #int# or #char# ...). + \item Class \Ref{GPArray<TYPE>} implements an array of smart-pointers + \Ref{GP<TYPE>} to objects of type #TYPE#. Using this class + reduces the size of the code generated by the template instanciation. + \end{itemize} + + Another variant of dynamic arrays is implemented in file \Ref{Arrays.h}. + The main difference is that class \Ref{TArray}, \Ref{DArray} and + \Ref{DPArray} implement a copy-on-demand scheme. + + @memo Dynamic arrays. */ +//@{ + +class GArrayBase : public GCont +{ +public: + // -- CONSTRUCTORS + GArrayBase(const GArrayBase &ref); + GArrayBase(const Traits &traits); + GArrayBase(const Traits &traits, int lobound, int hibound); + // -- DESTRUCTOR + ~GArrayBase(); + // -- ASSIGNMENT + GArrayBase& operator= (const GArrayBase &ga); + // -- ALTERATION + void empty(); + void touch(int n); + void resize(int lobound, int hibound); + void shift(int disp); + void del(int n, int howmany=1); + void ins(int n, const void *src, int howmany=1); + void steal(GArrayBase &ga); +protected: + const Traits &traits; + void *data; + GPBufferBase gdata; + int minlo; + int maxhi; + int lobound; + int hibound; +}; + + +/** Common base class for all dynamic arrays. + Class \Ref{GArrayTemplate} implements all methods for manipulating arrays + of type #TYPE#. You should not however create instances of this class. + You should instead use class \Ref{GArray}, \Ref{GTArray} or + \Ref{GPArray}. */ + +template<class TYPE> +class GArrayTemplate : protected GArrayBase +{ +public: + // -- CONSTRUCTORS + GArrayTemplate(const Traits &traits) : GArrayBase(traits) {} + GArrayTemplate(const Traits &traits, int lobound, int hibound) + : GArrayBase(traits, lobound, hibound) {} + // -- ACCESS + /** Returns the number of elements in the array. */ + int size() const + { return hibound-lobound+1; } + /** Returns the lower bound of the valid subscript range. */ + int lbound() const + { return lobound; } + /** Returns the upper bound of the valid subscript range. */ + int hbound() const + { return hibound; } + /** Returns a reference to the array element for subscript #n#. This + reference can be used for both reading (as "#a[n]#") and writing (as + "#a[n]=v#") an array element. This operation will not extend the valid + subscript range: an exception \Ref{GException} is thrown if argument #n# + is not in the valid subscript range. */ + inline TYPE& operator[](int const n); + /** Returns a constant reference to the array element for subscript #n#. + This reference can only be used for reading (as "#a[n]#") an array + element. This operation will not extend the valid subscript range: an + exception \Ref{GException} is thrown if argument #n# is not in the valid + subscript range. This variant of #operator[]# is necessary when dealing + with a #const GArray<TYPE>#. */ + inline const TYPE& operator[](int n) const; + // -- CONVERSION + /** Returns a pointer for reading or writing the array elements. This + pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator TYPE* () + { return ((TYPE*)data)-minlo; } + /** Returns a pointer for reading (but not modifying) the array elements. + This pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator const TYPE* () const + { return ((const TYPE*)data)-minlo; } + operator const TYPE* () // suppress warning with gcc-2.95 + { return ((const TYPE*)data)-minlo; } + // -- ALTERATION + /** Erases the array contents. All elements in the array are destroyed. + The valid subscript range is set to the empty range. */ + void empty() + { GArrayBase::empty(); } + /** Extends the subscript range so that it contains #n#. + This function does nothing if #n# is already int the valid subscript range. + If the valid range was empty, both the lower bound and the upper bound + are set to #n#. Otherwise the valid subscript range is extended + to encompass #n#. This function is very handy when called before setting + an array element: + \begin{verbatim} + int lineno=1; + GArray<GString> a; + while (! end_of_file()) { + a.touch(lineno); + a[lineno++] = read_a_line(); + } + \end{verbatim} */ + void touch(int n) + { if (n<lobound || n>hibound) GArrayBase::touch(n); } + /** Resets the valid subscript range to #0#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #hibound# to + #-1# resets the valid subscript range to the empty range. */ + void resize(int hibound) + { GArrayBase::resize(0, hibound); } + /** Resets the valid subscript range to #lobound#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #lobound# to #0# and + #hibound# to #-1# resets the valid subscript range to the empty range. */ + void resize(int lobound, int hibound) + { GArrayBase::resize(lobound, hibound); } + /** Shifts the valid subscript range. Argument #disp# is added to both + bounds of the valid subscript range. Array elements previously + located at subscript #x# will now be located at subscript #x+disp#. */ + void shift(int disp) + { GArrayBase::shift(disp); } + /** Deletes array elements. The array elements corresponding to + subscripts #n#...#n+howmany-1# are destroyed. All array elements + previously located at subscripts greater or equal to #n+howmany# + are moved to subscripts starting with #n#. The new subscript upper + bound is reduced in order to account for this shift. */ + void del(int n, int howmany=1) + { GArrayBase::del(n, howmany); } + /** Insert new elements into an array. This function inserts + #howmany# elements at position #n# into the array. These + elements are constructed using the default constructor for type + #TYPE#. All array elements previously located at subscripts #n# + and higher are moved to subscripts #n+howmany# and higher. The + upper bound of the valid subscript range is increased in order + to account for this shift. */ + void ins(int n, int howmany=1) + { GArrayBase::ins(n, 0, howmany); } + /** Insert new elements into an array. The new elements are + constructed by copying element #val# using the copy constructor + for type #TYPE#. See \Ref{ins(int n, unsigned int howmany=1)}. */ + void ins(int n, const TYPE &val, int howmany=1) + { GArrayBase::ins(n, (const void*)&val, howmany); } + /** Steals contents from array #ga#. After this call, array #ga# is empty, + and this array contains everything previously contained in #ga#. */ + void steal(GArrayTemplate &ga) + { GArrayBase::steal(ga); } + // -- SORTING + /** Sort array elements. Sort all array elements in ascending + order according to the less-or-equal comparison + operator for type #TYPE#. */ + void sort() + { sort(lbound(), hbound()); } + /** Sort array elements in subscript range #lo# to #hi#. Sort all array + elements whose subscripts are in range #lo# to #hi# in ascending order + according to the less-or-equal comparison operator for type #TYPE#. The + other elements of the array are left untouched. An exception is thrown + if arguments #lo# and #hi# are not in the valid subscript range. */ + void sort(int lo, int hi); +}; + + + +/* That one must be implemented as a regular template function. */ +template <class TYPE> void +GArrayTemplate<TYPE>::sort(int lo, int hi) +{ + if (hi <= lo) + return; + if (hi > hibound || lo<lobound) + G_THROW( ERR_MSG("GContainer.illegal_subscript") ); + TYPE *data = (TYPE*)(*this); + // Test for insertion sort + if (hi <= lo + 50) + { + for (int i=lo+1; i<=hi; i++) + { + int j = i; + TYPE tmp = data[i]; + while ((--j>=lo) && !(data[j]<=tmp)) + data[j+1] = data[j]; + data[j+1] = tmp; + } + return; + } + // -- determine suitable quick-sort pivot + TYPE tmp = data[lo]; + TYPE pivot = data[(lo+hi)/2]; + if (pivot <= tmp) + { tmp = pivot; pivot=data[lo]; } + if (data[hi] <= tmp) + { pivot = tmp; } + else if (data[hi] <= pivot) + { pivot = data[hi]; } + // -- partition set + int h = hi; + int l = lo; + while (l < h) + { + while (! (pivot <= data[l])) l++; + while (! (data[h] <= pivot)) h--; + if (l < h) + { + tmp = data[l]; + data[l] = data[h]; + data[h] = tmp; + l = l+1; + h = h-1; + } + } + // -- recursively restart + sort(lo, h); + sort(l, hi); +} + +template<class TYPE> inline TYPE& +GArrayTemplate<TYPE>::operator[](int const n) +{ +#if GCONTAINER_BOUNDS_CHECK + if (n<lobound || n>hibound) + { + G_THROW( ERR_MSG("GContainer.illegal_subscript") ); + } +#endif + return ((TYPE*)data)[n-minlo]; +} + + +template<class TYPE> inline const TYPE & +GArrayTemplate<TYPE>::operator[](int const n) const +{ +#if GCONTAINER_BOUNDS_CHECK + if (n<lobound || n>hibound) + { + G_THROW( ERR_MSG("GContainer.illegal_subscript") ); + } +#endif + return ((const TYPE*)data)[n-minlo]; +} + + + +/** Dynamic array for general types. + Template class #GArray<TYPE># implements an array of elements of type + #TYPE#. This template class must be able to access the following + functions. + \begin{itemize} + \item a default constructor #TYPE::TYPE()#, + \item a copy constructor #TYPE::TYPE(const TYPE &)#, + \item and optionally a destructor #TYPE::~TYPE()#. + \end{itemize} + This class only implement constructors. See class \Ref{GArrayTemplate} + for a description of all access methods. */ + +template<class TYPE> +class GArray : public GArrayTemplate<TYPE> +{ +public: + /** Constructs an empty array. The valid subscript range is initially + empty. Member function #touch# and #resize# provide convenient ways + to enlarge the subscript range. */ + GArray() + : GArrayTemplate<TYPE>(GCONT NormTraits<TYPE>::traits() ) {} + /** Constructs an array with subscripts in range 0 to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. */ + GArray(int hi) + : GArrayTemplate<TYPE>(GCONT NormTraits<TYPE>::traits(), 0, hi ) {} + /** Constructs an array with subscripts in range #lobound# to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. */ + GArray(int lo, int hi) + : GArrayTemplate<TYPE>(GCONT NormTraits<TYPE>::traits(), lo, hi ) {} + // Copy operator + GArray& operator=(const GArray &r) + { GArrayBase::operator=(r); return *this; } +}; + + +/** Dynamic array for smart pointers. + Template class #GPArray<TYPE># implements an array of elements of type + #GP<TYPE># (see \Ref{GSmartPointer.h}). Significantly smaller code sizes + can be achieved by using this class instead of the more general + #GArray<GP<TYPE>>#. + This class only implement constructors. See class \Ref{GArrayTemplate} + for a description of all access methods. */ + +template<class TYPE> +class GPArray : public GArrayTemplate<GP<TYPE> > +{ +public: + GPArray() + : GArrayTemplate<GP<TYPE> >(GCONT NormTraits<GPBase>::traits() ) {} + GPArray(int hi) + : GArrayTemplate<GP<TYPE> >(GCONT NormTraits<GPBase>::traits(), 0, hi ) {} + GPArray(int lo, int hi) + : GArrayTemplate<GP<TYPE> >(GCONT NormTraits<GPBase>::traits(), lo, hi ) {} + // Copy operator + GPArray& operator=(const GPArray &r) + { GArrayBase::operator=(r); return *this; } +}; + +/** Dynamic array for simple types. + Template class #GTArray<TYPE># implements an array of elements of {\em + simple} type #TYPE#. {\em Simple} means that objects of type #TYPE# can + be created, copied, moved or destroyed without using specific constructors + or destructor functions. Class #GTArray<TYPE># will move or copy objects + using simple bitwise copies. Otherwise you must use class #GArray<TYPE>#. + This class only implement constructors. See class \Ref{GArrayTemplate} + for a description of all access methods. */ +template<class TYPE> +class GTArray : public GArrayTemplate<TYPE> +{ +public: + GTArray() + : GArrayTemplate<TYPE>(GCONT TrivTraits<sizeof(TYPE)>::traits() ) {} + GTArray(int hi) + : GArrayTemplate<TYPE>(GCONT TrivTraits<sizeof(TYPE)>::traits(), 0, hi ) {} + GTArray(int lo, int hi) + : GArrayTemplate<TYPE>(GCONT TrivTraits<sizeof(TYPE)>::traits(), lo, hi ) {} + // Copy operator + GTArray& operator=(const GTArray &r) + { GArrayBase::operator=(r); return *this; } +}; + + +//@} + + + +// ------------------------------------------------------------ +// DOUBLY LINKED LISTS +// ------------------------------------------------------------ + + +/** @name Doubly Linked Lists + + The template classes \Ref{GList} and \Ref{GPList} implement a doubly + linked list of objects of arbitrary types. Member functions are provided + to search the list for an element, to insert or delete elements at + specified positions. Theses template class must be able to access + \begin{itemize} + \item a default constructor #TYPE::TYPE()#, + \item a copy constructor #TYPE::TYPE(const TYPE &)#, + \item optionally a destructor #TYPE::~TYPE()#, + \item and optionally a comparison operator #TYPE::operator==(const TYPE &)#. + \end{itemize} + @memo Doubly linked lists. +*/ +//@{ + +/** Generic iterator class. + This class represents a position in a list (see \Ref{GList}) or a map + (see \Ref{GMap}). As demonstrated by the following examples, + this class should be used to iterate over the objects contained + in a list or a map: + \begin{verbatim} + void print_list(GList<GString> a) + { + for (GPosition i = a ; i; ++i) + DjVuPrintMessage("%s\n", (const char*) a[i] ); + } + + void print_list_backwards(GList<GString> a) + { + for (GPosition i = a.lastpos() ; i; --i) + DjVuPrintMessage("%s\n", (const char*) a[i] ); + } + \end{verbatim} + GPosition objects should only be used with the list or map for which they + have been created (using the member functions #firstpos# or #lastpos# of + the container). Furthermore, you should never use a GPosition object + which designates a list element which has been removed from the list + (using member function #del# or by other means.) +*/ + +class GPosition : protected GCont +{ +public: + /** Creates a null GPosition object. */ + GPosition() : ptr(0), cont(0) {} + /** Creates a copy of a GPosition object. */ + GPosition(const GPosition &ref) : ptr(ref.ptr), cont(ref.cont) {} + /** Tests whether this GPosition object is non null. */ + operator int() const + { return !!ptr; } + /** Tests whether this GPosition object is null. */ + int operator !() const + { return !ptr; } + /** Moves this GPosition object to the next object in the container. */ + GPosition& operator ++() + { if (ptr) ptr = ptr->next; return *this; } + /** Moves this GPosition object to the previous object in the container. */ + GPosition& operator --() + { if (ptr) ptr = ptr->prev; return *this; } + // Internal. Do not use. + GPosition(Node *p, void *c) : ptr(p), cont(c) {} +#if GCONTAINER_BOUNDS_CHECK + Node *check(void *c) + { if (!ptr || c!=cont) throw_invalid(c); return ptr; } + const Node *check(void *c) const + { if (!ptr || c!=cont) throw_invalid(c); return ptr; } +#else + Node *check(void *c) + { return ptr; } + const Node *check(void *c) const + { return ptr; } +#endif +protected: + Node *ptr; + void *cont; + friend class GListBase; + friend class GSetBase; + void throw_invalid(void *c) const no_return; +}; + + +class GListBase : public GCont +{ +protected: + GListBase(const Traits& traits); + GListBase(const GListBase &ref); + void append(Node *n); + void prepend(Node *n); + void insert_after(GPosition pos, Node *n); + void insert_before(GPosition pos, Node *n); + void insert_before(GPosition pos, GListBase &fromlist, GPosition &frompos); + void del(GPosition &pos); +protected: + const Traits &traits; + int nelem; + Node head; +public: + ~GListBase(); + GListBase & operator= (const GListBase & gl); + GPosition firstpos() const { return GPosition(head.next, (void*)this); } + GPosition lastpos() const { return GPosition(head.prev, (void*)this); } + int isempty() const { return nelem==0; }; + GPosition nth(unsigned int n) const; + void empty(); +}; + + +template<class TI> +class GListImpl : public GListBase +{ +protected: + GListImpl(); + typedef GCONT ListNode<TI> LNode; + static Node * newnode(const TI &elt); + int operator==(const GListImpl<TI> &l2) const; + int search(const TI &elt, GPosition &pos) const; +}; + +template<class TI> +GListImpl<TI>::GListImpl() + : GListBase( GCONT NormTraits<LNode>::traits() ) +{ +} + +template<class TI> GCONT Node * +GListImpl<TI>::newnode(const TI &elt) +{ + LNode *n = (LNode *) operator new (sizeof(LNode )); +#if GCONTAINER_ZERO_FILL + memset(n, 0, sizeof(LNode )); +#endif + new ((void*)&(n->val)) TI(elt); + return (Node*) n; +} + +template<class TI> int +GListImpl<TI>::operator==(const GListImpl<TI> &l2) const +{ + Node *p, *q; + for (p=head.next, q=l2.head.next; p && q; p=p->next, q=q->next ) + if (((LNode*)p)->val != ((LNode*)q)->val) + return 0; + return p==0 && q==0; +} + +template<class TI> int +GListImpl<TI>::search(const TI &elt, GPosition &pos) const +{ + Node *n = (pos ? pos.check((void*)this) : head.next); + for (; n; n=n->next) + if ( ((LNode *)n)->val == elt ) + break; + if (n) pos = GPosition(n, (void*)this); + return (n != 0); +} + + +/** Common base class for all doubly linked lists. + Class \Ref{GListTemplate} implements all methods for manipulating lists + of of objects of type #TYPE#. You should not however create instances of + this class. You should instead use class \Ref{GList} or \Ref{GPList}. */ + +template <class TYPE, class TI> +class GListTemplate : protected GListImpl<TI> +{ +public: + // -- ACCESS + /** Returns the number of elements in the list. */ + int size() const + { return this->nelem; } + /** Returns the first position in the list. See \Ref{GPosition}. */ + GPosition firstpos() const + { return GListImpl<TI>::firstpos(); } + /** Returns the last position in the list. See \Ref{GPosition}. */ + GPosition lastpos() const + { return GListImpl<TI>::lastpos(); } + /** Implicit notation for GList::firstpos(). */ + operator GPosition() const + { return firstpos(); } + /** Returns a reference to the list element at position #pos#. This + reference can be used for both reading (as "#a[n]#") and modifying (as + "#a[n]=v#") a list element. Using an invalid position will cause a + segmentation violation. See \Ref{GPosition} for efficient operations on + positions. */ + TYPE& operator[](GPosition pos) + { return (TYPE&) (((typename GListImpl<TI>::LNode *)pos.check((void*)this))->val); } + /** Returns a constant reference to the list element at position #pos#. + This reference only be used for reading a list element. An exception + \Ref{GException} is thrown if #pos# is not a valid position. This + variant of #operator[]# is necessary when dealing with a #const + GList<TYPE>#. See \Ref{GPosition} for efficient operations on + positions. */ + const TYPE& operator[](GPosition pos) const + { return (const TYPE&) (((const typename GListImpl<TI>::LNode *)pos.check((void*)this))->val); } + // -- TEST + /** Tests whether a list is empty. + Returns a non zero value if the list contains no elements. */ + int isempty() const + { return this->nelem==0; } + /** Compares two lists. Returns a non zero value if and only if both lists + contain the same elements (as tested by #TYPE::operator==(const TYPE&)# + in the same order. */ + int operator==(const GListTemplate<TYPE,TI> &l2) const + { return GListImpl<TI>::operator==(l2); } + // -- SEARCHING + /** Returns the position #pos# of the #n#-th list element. An invalid + position is returned if the list contains less than #n# elements. The + operation works by sequentially scanning the list until reaching the + #n#-th element. */ + GPosition nth(unsigned int n) const + { return GListImpl<TI>::nth(n); } + /* Compatibility */ + int nth(unsigned int n, GPosition &pos) const + { GPosition npos=nth(n); if (npos) pos=npos; return !!pos; } + /** Tests whether the list contains a given element. If the list contains + #elt#, the position of the the first list element equal to #elt# as + checked by #TYPE::operator==(const TYPE&)# is returned. Otherwise an + invalid position is returned. */ + GPosition contains(const TYPE &elt) const + { GPosition pos; GListImpl<TI>::search((const TI&)elt, pos); return pos; } + /** Searches the list for a given element. If position #pos# is a valid + position for this list, the search starts at the specified position. If + position #pos# is not a valid position, the search starts at the + beginning of the list. The list elements are sequentially compared with + #elt# using #TYPE::operator==(const TYPE&)#. As soon as a list element + is equal to #elt#, function #search# sets argument #pos# with the + position of this list element and returns 1. If however the search + reaches the end of the list, function #search# returns 0 and leaves + #pos# unchanged. */ + int search(const TYPE &elt, GPosition &pos) const + { return GListImpl<TI>::search((const TI&)elt, pos); } + // -- ALTERATION + /** Erases the list contents. All list elements are destroyed and + unlinked. The list is left with zero elements. */ + void empty() + { GListImpl<TI>::empty(); } + /** Inserts an element after the last element of the list. + The new element is initialized with a copy of argument #elt#. */ + void append(const TYPE &elt) + { GListImpl<TI>::append(newnode((const TI&)elt)); } + /** Inserts an element before the first element of the list. + The new element is initialized with a copy of argument #elt#. */ + void prepend(const TYPE &elt) + { GListImpl<TI>::prepend(newnode((const TI&)elt)); } + /** Inserts a new element after the list element at position #pos#. When + position #pos# is null the element is inserted at the beginning of the + list. The new element is initialized with a copy of #elt#. */ + void insert_after(GPosition pos, const TYPE &elt) + { GListImpl<TI>::insert_after(pos, newnode((const TI&)elt)); } + /** Inserts a new element before the list element at position #pos#. When + position #pos# is null the element is inserted at the end of the + list. The new element is initialized with a copy of #elt#. */ + void insert_before(GPosition pos, const TYPE &elt) + { GListImpl<TI>::insert_before(pos, newnode((const TI&)elt)); } + /** Inserts an element of another list into this list. This function + removes the element at position #frompos# in list #frompos#, inserts it + in the current list before the element at position #pos#, and advances + #frompos# to the next element in list #fromlist#. When position #pos# is + null the element is inserted at the end of the list. */ + void insert_before(GPosition pos, GListTemplate<TYPE,TI> &fromlist, GPosition &frompos) + { GListImpl<TI>::insert_before(pos, fromlist, frompos); } + /** Destroys the list element at position #pos#. This function does + nothing unless position #pos# is a valid position. */ + void del(GPosition &pos) + { GListImpl<TI>::del(pos); } + /* Old iterators. Do not use. */ +#if GCONTAINER_OLD_ITERATORS + void first(GPosition &pos) const { pos = firstpos(); } + void last(GPosition &pos) const { pos = lastpos(); } + const TYPE *next(GPosition &pos) const + { if (!pos) return 0; const TYPE *x=&((*this)[pos]); ++pos; return x; } + const TYPE *prev(GPosition &pos) const + { if (!pos) return 0; const TYPE *x=&((*this)[pos]); --pos; return x; } + TYPE *next(GPosition &pos) + { if (!pos) return 0; TYPE *x=&((*this)[pos]); ++pos; return x; } + TYPE *prev(GPosition &pos) + { if (!pos) return 0; TYPE *x=&((*this)[pos]); --pos; return x; } +#endif +}; + + +/** Doubly linked lists. Template class #GList<TYPE># implements a doubly + linked list of elements of type #TYPE#. This class only implement + constructors. See class \Ref{GListTemplate} and \Ref{GPosition} for a + description of all access methods. */ + +template <class TYPE> +class GList : public GListTemplate<TYPE,TYPE> +{ +public: + /** Null Constructor. Constructs a list with zero elements. */ + GList() : GListTemplate<TYPE,TYPE>() {} + GList& operator=(const GList &r) + { GListBase::operator=(r); return *this; } +}; + + +/** Doubly linked lists for smart pointers. + Template class #GList<TYPE># implements a doubly linked list of elements + of type #GP<TYPE># (see \Ref{GSmartPointer.h}). Significantly smaller + code sizes can be achieved by using this class instead of the more general + #GArray<GP<TYPE>>#. + This class only implement constructors. See class \Ref{GListTemplate} and + \Ref{GPosition} for a description of all access methods. */ + +template <class TYPE> +class GPList : public GListTemplate<GP<TYPE>,GPBase> +{ +public: + /** Null Constructor. Constructs a list with zero elements. */ + GPList() : GListTemplate<GP<TYPE>,GPBase>() {} + GPList& operator=(const GPList &r) + { GListBase::operator=(r); return *this; } +}; + + +//@} + + + +// ------------------------------------------------------------ +// ASSOCIATIVE MAPS +// ------------------------------------------------------------ + +/** @name Associative Maps + + These template classes implements a associative maps. The associative map + contains an arbitrary number of entries. Each entry is a pair containing + one element of type #KTYPE# (named the "key") and one element of type + #VTYPE# (named the "value"). All entries have distinct keys. + These template class must be able to access the following functions: + \begin{itemize} + \item a #VTYPE# default constructor #VTYPE::VTYPE()#, + \item a #VTYPE# copy constructor #VTYPE::VTYPE(const VTYPE &)#, + \item optionally a #VTYPE# destructor #VTYPE::~VTYPE()#, + \item a #KTYPE# default constructor #KTYPE::KTYPE()#, + \item a #KTYPE# copy constructor #KTYPE::KTYPE(const KTYPE &)#, + \item optionally a #KTYPE# destructor #KTYPE::~KTYPE()#, + \item a #KTYPE# comparison operator #KTYPE::operator==(const KTYPE &)#, + \item and a #KTYPE# hashing function #hash(const KTYPE&)#. + \end{itemize} + The hashing function must return an #unsigned int# number. Multiple + invocations of the hashing function with equal arguments (in the sense of + #KTYPE::operator==#) must always return the same number. + Position objects (see \Ref{GPosition}) may be used to iterate over the + entries contained by an associative map. + @memo Associative maps. +*/ +//@{ + +class GSetBase : public GCont +{ +protected: + GSetBase(const Traits &traits); + GSetBase(const GSetBase &ref); + static GCONT HNode *newnode(const void *key); + HNode *hashnode(unsigned int hashcode) const; + HNode *installnode(HNode *n); + void deletenode(HNode *n); +protected: + const Traits &traits; + int nelems; + int nbuckets; + HNode **table; + GPBuffer<HNode *> gtable; + HNode *first; +private: + void insertnode(HNode *n); + void rehash(int newbuckets); +public: + ~GSetBase(); + GSetBase& operator=(const GSetBase &ref); + GPosition firstpos() const; + void del(GPosition &pos); + void empty(); +}; + +template <class K> +class GSetImpl : public GSetBase +{ +protected: + GSetImpl(); + GSetImpl(const Traits &traits); + typedef GCONT SetNode<K> SNode; + HNode *get(const K &key) const; + HNode *get_or_throw(const K &key) const; + HNode *get_or_create(const K &key); +public: + GPosition contains(const K &key) const + { return GPosition( get(key), (void*)this); } + void del(const K &key) + { deletenode(get(key)); } +}; + +template<class K> +GSetImpl<K>::GSetImpl() + : GSetBase( GCONT NormTraits<GCONT SetNode<K> >::traits() ) +{ +} + +template<class K> +GSetImpl<K>::GSetImpl(const Traits &traits) + : GSetBase(traits) +{ +} + +template<class K> GCONT HNode * +GSetImpl<K>::get(const K &key) const +{ + unsigned int hashcode = hash(key); + for (SNode *s=(SNode*)hashnode(hashcode); s; s=(SNode*)(s->hprev)) + if (s->hashcode == hashcode && s->key == key) return s; + return 0; +} + +#if GCONTAINER_BOUNDS_CHECK +template<class K> GCONT HNode * +GSetImpl<K>::get_or_throw(const K &key) const +{ + HNode *m = get(key); + if (!m) + { + G_THROW( ERR_MSG("GContainer.cannot_add") ); + } + return m; +} +#else +template<class K> inline GCONT HNode * +GSetImpl<K>::get_or_throw(const K &key) const +{ + return get(key); +} +#endif + +template<class K> GCONT HNode * +GSetImpl<K>::get_or_create(const K &key) +{ + HNode *m = get(key); + if (m) return m; + SNode *n = (SNode*) operator new (sizeof(SNode)); +#if GCONTAINER_ZERO_FILL + memset(n, 0, sizeof(SNode)); +#endif + new ((void*)&(n->key)) K ( key ); + n->hashcode = hash((const K&)(n->key)); + installnode(n); + return n; +} + +template <class K, class TI> +class GMapImpl : public GSetImpl<K> +{ +protected: + GMapImpl(); + GMapImpl(const GCONT Traits &traits); + typedef GCONT MapNode<K,TI> MNode; + GCONT HNode* get_or_create(const K &key); +}; + +template<class K, class TI> +GMapImpl<K,TI>::GMapImpl() + : GSetImpl<K> ( GCONT NormTraits<GCONT MapNode<K,TI> >::traits() ) +{ +} + +template<class K, class TI> +GMapImpl<K,TI>::GMapImpl(const GCONT Traits &traits) + : GSetImpl<K>(traits) +{ +} + +template<class K, class TI> GCONT HNode * +GMapImpl<K,TI>::get_or_create(const K &key) +{ + GCONT HNode *m = get(key); + if (m) return m; + MNode *n = (MNode*) operator new (sizeof(MNode)); +#if GCONTAINER_ZERO_FILL + memset(n, 0, sizeof(MNode)); +#endif + new ((void*)&(n->key)) K (key); + new ((void*)&(n->val)) TI (); + n->hashcode = hash((const K&)(n->key)); + installnode(n); + return n; +} + + + +/** Common base class for all associative maps. + Class \Ref{GArrayTemplate} implements all methods for manipulating + associative maps with key type #KTYPE# and value type #VTYPE#. + You should not however create instances of this class. + You should instead use class \Ref{GMap} or \Ref{GPMap}. */ + +template <class KTYPE, class VTYPE, class TI> +class GMapTemplate : protected GMapImpl<KTYPE,TI> +{ +public: + /** Returns the number of elements in the map. */ + int size() const + { return this->nelems; } + /** Returns the first position in the map. */ + GPosition firstpos() const + { return GMapImpl<KTYPE,TI>::firstpos(); } + /** Implicit notation for GMap::firstpos(). */ + operator GPosition() const + { return firstpos(); } + /** Tests whether the associative map is empty. + Returns a non zero value if and only if the map contains zero entries. */ + int isempty() const + { return this->nelems==0; } + /** Searches an entry for key #key#. If the map contains an entry whose key + is equal to #key# according to #KTYPE::operator==(const KTYPE&)#, this + function returns its position. Otherwise it returns an invalid + position. */ + GPosition contains(const KTYPE &key) const + { return GMapImpl<KTYPE,TI>::contains(key); } + /* Compatibility */ + GPosition contains(const KTYPE &key, GPosition &pos) const + { return pos = GMapImpl<KTYPE,TI>::contains(key); } + // -- ALTERATION + /** Erases the associative map contents. All entries are destroyed and + removed. The map is left with zero entries. */ + void empty() + { GMapImpl<KTYPE,TI>::empty(); } + /** Returns a constant reference to the key of the map entry at position + #pos#. An exception \Ref{GException} is thrown if position #pos# is not + valid. There is no direct way to change the key of a map entry. */ + const KTYPE &key(const GPosition &pos) const + { return (const KTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(pos.check((void*)this)))->key); } + /** Returns a reference to the value of the map entry at position #pos#. + This reference can be used for both reading (as "#a[n]#") and modifying + (as "#a[n]=v#"). An exception \Ref{GException} is thrown if position + #pos# is not valid. */ + VTYPE& operator[](const GPosition &pos) + { return (VTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(pos.check((void*)this)))->val); } + /** Returns a constant reference to the value of the map entry at position + #pos#. This reference can only be used for reading (as "#a[n]#") the + entry value. An exception \Ref{GException} is thrown if position #pos# + is not valid. */ + const VTYPE& operator[](const GPosition &pos) const + { return (const VTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(pos.check((void*)this)))->val); } + /** Returns a constant reference to the value of the map entry for key + #key#. This reference can only be used for reading (as "#a[n]#") the + entry value. An exception \Ref{GException} is thrown if no entry + contains key #key#. This variant of #operator[]# is necessary when + dealing with a #const GMAP<KTYPE,VTYPE>#. */ + const VTYPE& operator[](const KTYPE &key) const + { return (const VTYPE&)(((const typename GMapImpl<KTYPE,TI>::MNode*)(get_or_throw(key)))->val); } + /** Returns a reference to the value of the map entry for key #key#. This + reference can be used for both reading (as "#a[n]#") and modifying (as + "#a[n]=v#"). If there is no entry for key #key#, a new entry is created + for that key with the null constructor #VTYPE::VTYPE()#. */ + VTYPE& operator[](const KTYPE &key) + { return (VTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(get_or_create(key)))->val); } + /** Destroys the map entry for position #pos#. + Nothing is done if position #pos# is not a valid position. */ + void del(GPosition &pos) + { GSetBase::del(pos); } + /** Destroys the map entry for key #key#. + Nothing is done if there is no entry for key #key#. */ + void del(const KTYPE &key) + { GMapImpl<KTYPE,TI>::del(key); } + /* Old iterators. Do not use. */ +#if GCONTAINER_OLD_ITERATORS + void first(GPosition &pos) const { pos = firstpos(); } + const VTYPE *next(GPosition &pos) const + { if (!pos) return 0; const VTYPE *x=&((*this)[pos]); ++pos; return x; } + VTYPE *next(GPosition &pos) + { if (!pos) return 0; VTYPE *x=&((*this)[pos]); ++pos; return x; } +#endif +}; + + + +/** Associative maps. + Template class #GMap<KTYPE,VTYPE># implements an associative map. + The map contains an arbitrary number of entries. Each entry is a + pair containing one element of type #KTYPE# (named the "key") and one + element of type #VTYPE# (named the "value"). + The entry associated to a particular value of the key can retrieved + very efficiently. + This class only implement constructors. See class \Ref{GMapTemplate} and + \Ref{GPosition} for a description of all access methods.*/ + +template <class KTYPE, class VTYPE> +class GMap : public GMapTemplate<KTYPE,VTYPE,VTYPE> +{ +public: + // -- ACCESS + GMap() : GMapTemplate<KTYPE,VTYPE,VTYPE>() {} + GMap& operator=(const GMap &r) + { GSetBase::operator=(r); return *this; } +}; + +/** Associative maps for smart-pointers. + Template class #GMap<KTYPE,VTYPE># implements an associative map for key + type #KTYPE# and value type #GP<VTYPE># (see \Ref{GSmartPointer.h}). The + map contains an arbitrary number of entries. Each entry is a pair + containing one element of type #KTYPE# (named the "key") and one aelement + of type #VTYPE# (named the "value"). The entry associated to a particular + value of the key can retrieved very efficiently. + Significantly smaller code sizes can be achieved by using this class + instead of the more general #GMap<KTYPE,GP<VTYPE>># (see \Ref{GMap}). + This class only implement constructors. See class \Ref{GMapTemplate} and + \Ref{GPosition} for a description of all access methods.*/ + +template <class KTYPE, class VTYPE> +class GPMap : public GMapTemplate<KTYPE,GP<VTYPE>,GPBase> +{ +public: + GPMap() : GMapTemplate<KTYPE,GP<VTYPE>,GPBase>() {} + GPMap& operator=(const GPMap &r) + { GSetBase::operator=(r); return *this; } +}; + + +// ------------------------------------------------------------ +// HASH FUNCTIONS +// ------------------------------------------------------------ + + +/** @name Hash functions + These functions let you use template class \Ref{GMap} with the + corresponding elementary types. The returned hash code may be reduced to + an arbitrary range by computing its remainder modulo the upper bound of + the range. + @memo Hash functions for elementary types. */ +//@{ + +/** Hashing function (unsigned int). */ +static inline unsigned int +hash(const unsigned int & x) +{ + return x; +} + +/** Hashing function (int). */ +static inline unsigned int +hash(const int & x) +{ + return (unsigned int)x; +} + +/** Hashing function (long). */ +static inline unsigned int +hash(const long & x) +{ + return (unsigned int)x; +} + +/** Hashing function (unsigned long). */ +static inline unsigned int +hash(const unsigned long & x) +{ + return (unsigned int)x; +} + +/** Hashing function (void *). */ +static inline unsigned int +hash(void * const & x) +{ + return (unsigned long) x; +} + +/** Hashing function (const void *). */ +static inline unsigned int +hash(const void * const & x) +{ + return (unsigned long) x; +} + +/** Hashing function (float). */ +static inline unsigned int +hash(const float & x) +{ + // optimizer will get rid of unnecessary code + unsigned int *addr = (unsigned int*)&x; + if (sizeof(float)<2*sizeof(unsigned int)) + return addr[0]; + else + return addr[0]^addr[1]; +} + +/** Hashing function (double). */ +static inline unsigned int +hash(const double & x) +{ + // optimizer will get rid of unnecessary code + unsigned int *addr = (unsigned int*)&x; + if (sizeof(double)<2*sizeof(unsigned int)) + return addr[0]; + else if (sizeof(double)<4*sizeof(unsigned int)) + return addr[0]^addr[1]; + else + return addr[0]^addr[1]^addr[2]^addr[3]; +} + + +//@} +//@} +//@} + +// ------------ THE END + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/GException.cpp b/kviewshell/plugins/djvu/libdjvu/GException.cpp new file mode 100644 index 00000000..f3f84dda --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GException.cpp @@ -0,0 +1,284 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GException.cpp,v 1.14 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "GException.h" +#include "DjVuMessageLite.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// - Author: Leon Bottou, 05/1997 + +GException::GException() + : cause(0), file(0), func(0), line(0), source(GException::GINTERNAL) +{ +} + +const char * const +GException::outofmemory = ERR_MSG("GException.outofmemory"); + +GException::GException(const GException & exc) + : file(exc.file), func(exc.func), line(exc.line), source(exc.source) +{ + if (exc.cause && exc.cause!=outofmemory) + { + char *s = new char[strlen(exc.cause)+1]; + strcpy(s, exc.cause); + cause = s; + } + else + { + cause = exc.cause; + } +} + +GException::GException (const char *xcause, const char *file, int line, + const char *func, const source_type xsource) + : file(file), func(func), line(line), source(xsource) +{ + // good place to set a breakpoint and DEBUG message too. + // It'd hard to track exceptions which seem to go from nowhere +#ifdef DEBUG_MSG + DEBUG_MSG("GException::GException(): cause=" << (xcause ? xcause : "unknown") << "\n"); +#endif + if (xcause && xcause!=outofmemory) + { + char *s = new char[strlen(xcause)+1]; + strcpy(s, xcause); + cause = s; + } + else + { + cause = xcause; + } +} + +GException::~GException(void) +{ + if (cause && cause!=outofmemory ) + delete [] const_cast<char*>(cause); + cause=file=func=0; +} + +GException & +GException::operator=(const GException & exc) +{ + if (cause && cause!=outofmemory) + delete [] const_cast<char*>(cause); + cause = 0; + file = exc.file; + func = exc.func; + line = exc.line; + source=exc.source; + if (exc.cause && exc.cause!=outofmemory) + { + char *s = new char[strlen(exc.cause)+1]; + strcpy(s, exc.cause); + cause = s; + } + else + { + cause = exc.cause; + } + return *this; +} + +void +GException::perror(void) const +{ + fflush(0); + DjVuPrintErrorUTF8("*** "); + DjVuMessageLite::perror(get_cause()); + if (file && line>0) + DjVuPrintErrorUTF8("*** (%s:%d)\n", file, line); + else if (file) + DjVuPrintErrorUTF8("*** (%s)\n", file); + if (func) + DjVuPrintErrorUTF8("*** '%s'\n", func); + DjVuPrintErrorUTF8("\n"); +} + +const char* +GException::get_cause(void) const +{ + if (! cause) + return "Invalid exception"; + return cause; +} + +int +GException::cmp_cause(const char s1[] , const char s2[]) +{ + int retval; + if(! s2 || !s2[0]) + { + retval=(s1&&s1[0])?1:(-1); + }else if(! s1 || !s1[0]) + { + retval=(-1); + }else + { + const char *end_s1=strpbrk(s1,"\t\n"); + const int n1=end_s1?(int)((size_t)end_s1-(size_t)s1):strlen(s1); + const char *end_s2=strpbrk(s1,"\t\n"); + const int n2=end_s2?(int)((size_t)end_s2-(size_t)s2):strlen(s2); + retval=(n1==n2)?strncmp(s1,s2,n1):strcmp(s1,s2); + } + return retval; +} + +int +GException::cmp_cause(const char s2[]) const +{ + return cmp_cause(cause,s2); +} + +#ifdef USE_EXCEPTION_EMULATION + +GExceptionHandler *GExceptionHandler::head = 0; + +void +GExceptionHandler::emthrow(const GException &gex) +{ + if (head) + { + head->current = gex; + longjmp(head->jump, -1); + } + else + { + DjVuPrintErrorUTF8("\n*** Unhandled exception"); + gex.perror(); + abort(); + } +} + +#else // ! USE_EXCEPTION_EMULATION + +static int abort_on_exception = 0; + +void +#ifndef NO_LIBGCC_HOOKS +GExceptionHandler::exthrow(const GException &ex) +#else +GExceptionHandler::exthrow(const GException ex) +#endif /* NO_LIBGCC_HOOKS */ +{ + if (abort_on_exception) + abort(); + throw ex; +} + +void +GExceptionHandler::rethrow(void) +{ + if (abort_on_exception) + abort(); + throw; +} + +#endif + + + +// ------ MEMORY MANAGEMENT HANDLER + +#ifndef NEED_DJVU_MEMORY +// This is not activated when C++ memory management +// is overidden. The overriding functions handle +// memory exceptions by themselves. +# if defined(_MSC_VER) +// Microsoft is different! +static int throw_memory_error(size_t) { G_THROW(GException::outofmemory); return 0; } +static int (*old_handler)(size_t) = _set_new_handler(throw_memory_error); +# else // !_MSC_VER +// Standard C++ +static void throw_memory_error() { G_THROW(GException::outofmemory); } +# if !defined(WIN32) && !defined(__CYGWIN32__) && !defined(OS2) +# ifdef HAVE_STDINCLUDES +static void (*old_handler)() = std::set_new_handler(throw_memory_error); +# else +static void (*old_handler)() = set_new_handler(throw_memory_error); +# endif // HAVE_STDINCLUDES +# endif // ! WIN32 +# endif // !_MSC_VER +#endif // !NEED_DJVU_MEMORY + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GException.h b/kviewshell/plugins/djvu/libdjvu/GException.h new file mode 100644 index 00000000..97286987 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GException.h @@ -0,0 +1,356 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GException.h,v 1.10 2005/06/07 23:42:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GEXCEPTION_H_ +#define _GEXCEPTION_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#ifndef no_return +#ifdef __GNUC__ +#define no_return __attribute__ ((noreturn)) +#else +#define no_return +#endif +#endif + +/** @name GException.h + + Files #"GException.h"# and #"GException.cpp"# define a portable exception + scheme used through the DjVu Reference Library. This scheme can use native + C++ exceptions or an exception emulation based on #longjmp#/#setjmp#. A + particular model can be forced a compile time by defining option + #CPP_SUPPORTS_EXCEPTIONS# or #USE_EXCEPTION_EMULATION#. + + This emulation code was motivated because many compilers did not properly + support exceptions as mandated by the C++ standard documents. This + emulation is now considered obsolete because (a) it is not able to call + the proper destructors when an exception occurs, and (b) it is not thread + safe. Although all modern C++ compiler handle exception decently, the + exception handling intrinsics are not always thread safe. Therefore we + urge programmers to {\em only} use exceptions to signal error conditions + that force the library to discontinue execution. + + There are four macros for handling exceptions. Macros #G_TRY#, #G_CATCH# and + #G_ENDCATCH# are used to define an exception catching block. Exceptions can + be thrown at all times using macro #G_THROW(cause)#. An exception can be + re-thrown from a catch block using macro #G_RETHROW#. + + Example: + \begin{verbatim} + G_TRY + { + // program lines which may result in a call to THROW() + G_THROW("message"); + } + G_CATCH(ex) + { + // Variable ex refers to a GException object. + ex.perror(); + // You can rethrow the exception to an outer exception handler. + G_RETHROW; + } + G_ENDCATCH; + \end{verbatim} + + @memo + Portable exceptions. + @author + L\'eon Bottou <leonb@research.att.com> -- initial implementation.\\ + Andrei Erofeev <eaf@geocities.com> -- fixed message memory allocation. + @version + #$Id: GException.h,v 1.10 2005/06/07 23:42:22 leonb Exp $# */ +//@{ + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** Exception class. + The library always uses macros #G_TRY#, #G_THROW#, #G_CATCH# and #G_ENDCATCH# for + throwing and catching exceptions (see \Ref{GException.h}). These macros + only deal with exceptions of type #GException#. */ + +class GException { +public: + enum source_type { GINTERNAL=0, GEXTERNAL, GAPPLICATION, GOTHER }; + /** Constructs a GException. This constructor is usually called by macro + #G_THROW#. Argument #cause# is a plain text error message. As a + convention, string #ByteStream::EndOfFile# is used when reaching an unexpected + end-of-file condition and string #DataPool::Stop# is used when the user + interrupts the execution. The remaining arguments are usually provided + by the predefined macros #__FILE__#, #__LINE__#, and (G++ and EGCS only) + #__PRETTY_FUNCTION__#. */ + GException (const char *cause, const char *file=0, int line=0, + const char *func=0, const source_type source=GINTERNAL); + + /** Copy Constructor. */ + GException (const GException & exc); + + /** Null Constructor. */ + GException (); + + /** Destructor. */ + virtual ~GException(void); + + /** Copy Operator. */ + GException & operator=(const GException & exc); + + /** Prints an error message on stderr. + This function no longer takes a message parameter because + some instances used a i18n message id and other instances + used a literal string. */ + void perror(void) const; + + /** Returns the string describing the cause of the exception. The returned + pointer is never null. Exception handlers should not rely on the value + of the string #cause#. As a convention however, string + #ByteStream::EndOfFile# is used + when reaching an unexpected end-of-file condition and string + #DataPool::Stop# is used when the user interrupts the execution. These + strings can be tested by the exception handlers, with + #cmp_cause#. Similar conventional strings may be defined + in the future. They all will be small strings with only uppercase + characters. */ + const char* get_cause(void) const; + + /** Compares the cause with the specified string, ignoring anything after + the first tab. */ + int cmp_cause(const char s2[]) const; + + /** Compares the cause with the specified string, ignoring anything after + the first tab. */ + static int cmp_cause(const char s1[],const char s2[]); + + /** Returns the function name from which the exception was thrown. + A null pointer is returned if no function name is available. */ + const char* get_function(void) const { return func; } + + /** Returns the file name from which the exception was thrown. + A null pointer is returned if no file name is available. */ + const char* get_file(void) const { return file; } + + /** Returns the exception source */ + source_type get_source(void) const { return source; } + + /** Returns the line number from which the exception was thrown. + A zero is returned if no line number is available. */ + int get_line(void) const { return line; }; + + // Magic cause string + static const char * const outofmemory; + +private: + const char *cause; + const char *file; + const char *func; + int line; + source_type source; +}; + +//@} + +#undef G_TRY +#undef G_CATCH +#undef G_CATCH_ALL +#undef G_ENDCATCH +#undef G_RETHROW +#undef G_THROW +#undef G_THROW_TYPE +#undef G_THROW_INTERNAL +#undef G_THROW_EXTERNAL +#undef G_THROW_APPLICATION +#undef G_THROW_OTHER + +// Check if compiler supports native exceptions +#if defined(_MSC_VER) +#define CPP_SUPPORTS_EXCEPTIONS +#endif +#if defined(__MWERKS__) +#define CPP_SUPPORTS_EXCEPTIONS +#endif +#if defined(__EXCEPTIONS) +#define CPP_SUPPORTS_EXCEPTIONS +#endif +// Decide which exception model to use +#ifndef CPP_SUPPORTS_EXCEPTIONS +#ifndef USE_EXCEPTION_EMULATION +#define USE_EXCEPTION_EMULATION +#endif +#endif + + +#ifndef USE_EXCEPTION_EMULATION + +// Compiler supports ANSI C++ exceptions. +// Defined exception macros accordingly. + +class GExceptionHandler { +public: +#ifndef NO_LIBGCC_HOOKS + static void exthrow(const GException &) no_return; +#else + static void exthrow(const GException ) no_return; +#endif /* NO_LIBGCC_HOOKS */ + static void rethrow(void) no_return; +}; + +#define G_TRY try +#define G_CATCH(n) catch(const GException &n) { +#define G_CATCH_ALL catch(...) { +#define G_ENDCATCH } +#define G_RETHROW GExceptionHandler::rethrow() +#define G_EMTHROW(ex) GExceptionHandler::exthrow(ex) +#ifdef __GNUG__ +#define G_THROW_TYPE(msg,xtype) GExceptionHandler::exthrow \ + (GException(msg, __FILE__, __LINE__, __PRETTY_FUNCTION__, xtype)) +#else +#define G_THROW_TYPE(msg,xtype) GExceptionHandler::exthrow \ + (GException(msg, __FILE__, __LINE__,0, xtype)) +#endif + +#else // USE_EXCEPTION_EMULATION + +// Compiler does not support ANSI C++ exceptions. +// Emulate with setjmp/longjmp. + +#include <setjmp.h> + +class GExceptionHandler { +public: + jmp_buf jump; + GExceptionHandler *next; + GException current; +public: + static GExceptionHandler *head; + static void emthrow(const GException &) no_return; +public: + GExceptionHandler() { next = head; }; + ~GExceptionHandler() { head = next; }; +}; + +#define G_TRY do { GExceptionHandler __exh; \ + if (!setjmp(__exh.jump)) \ + { GExceptionHandler::head = &__exh; + +#define G_CATCH_ALL } else { GExceptionHandler::head = __exh.next; +#define G_CATCH(n) G_CATCH_ALL const GException& n = __exh.current; + +#define G_ENDCATCH } } while(0) + +#define G_RETHROW GExceptionHandler::emthrow(__exh.current) + +#ifdef __GNUG__ +#define G_THROW_TYPE(msg,xtype) GExceptionHandler::emthrow \ + (GException(msg, __FILE__, __LINE__, __PRETTY_FUNCTION__, xtype)) +#define G_EMTHROW(ex) GExceptionHandler::emthrow(ex) +#else +#define G_THROW_TYPE(m,xtype) GExceptionHandler::emthrow \ + (GException(m, __FILE__, __LINE__,0, xtype)) +#define G_EMTHROW(ex) GExceptionHandler::emthrow(ex) +#endif + +#endif // !CPP_SUPPORTS_EXCEPTIONS + + +inline void +G_EXTHROW +(const GException &ex,const char *msg=0,const char *file=0,int line=0, + const char *func=0, const GException::source_type source=GException::GINTERNAL) +{ + G_EMTHROW( (msg||file||line||func)? + GException(msg?msg:ex.get_cause(), + file?file:ex.get_file(), + line?line:ex.get_line(), + func?func:ex.get_function(), + source) + :ex); +} + +inline void +G_EXTHROW +(const char msg[],const char *file=0,int line=0,const char *func=0, + const GException::source_type source=GException::GINTERNAL ) +{ + G_EMTHROW(GException(msg,file,line,func,source)); +} + +#define G_THROW(msg) G_THROW_TYPE(msg,GException::GINTERNAL) +#define G_THROW_INTERNAL(msg) G_THROW_TYPE(msg,GException::GINTERNAL) +#define G_THROW_EXTERNAL(msg) G_THROW_TYPE(msg,GException::GEXTERNAL) +#define G_THROW_APPLICATION(msg) G_THROW_TYPE(msg,GException::GAPPLICATION) +#define G_THROW_OTHER(msg) G_THROW_TYPE(msg,GException::GOTHER) + +// -------------- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GIFFManager.cpp b/kviewshell/plugins/djvu/libdjvu/GIFFManager.cpp new file mode 100644 index 00000000..973c6cec --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GIFFManager.cpp @@ -0,0 +1,663 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GIFFManager.cpp,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GIFFManager.h" +#include "GException.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GIFFChunk::~GIFFChunk(void) {} + +GIFFManager::~GIFFManager(void) {} + +GP<GIFFManager> +GIFFManager::create(void) +{ + GIFFManager *iff=new GIFFManager(); + GP<GIFFManager> retval=iff; + iff->init(); + return retval; +} + +GP<GIFFManager> +GIFFManager::create(const GUTF8String &name) +{ + GIFFManager *iff=new GIFFManager(); + GP<GIFFManager> retval=iff; + iff->init(name); + return retval; +} + +void +GIFFChunk::set_name(GUTF8String name) +{ + DEBUG_MSG("GIFFChunk::set_name(): name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + const int colon=name.search(':'); + if(colon>=0) + { + type=name.substr(0,colon); + name=name.substr(colon+1,(unsigned int)-1); + if(name.search(':')>=0) + G_THROW( ERR_MSG("GIFFManager.one_colon") ); + } + + DEBUG_MSG("auto-setting type to '" << type << "'\n"); + + if (name.contains(".[]")>=0) + G_THROW( ERR_MSG("GIFFManager.bad_char") ); + + strncpy(GIFFChunk::name, (const char *)name, 4); + GIFFChunk::name[4]=0; + for(int i=strlen(GIFFChunk::name);i<4;i++) + GIFFChunk::name[i]=' '; +} + +bool +GIFFChunk::check_name(GUTF8String name) +{ + GUTF8String type; + const int colon=name.search(':'); + if(colon>=0) + { + type=name.substr(0,colon); + name=name.substr(colon+1,(unsigned int)-1); + } + + const GUTF8String sname=(name.substr(0,4)+" ").substr(0,4); + + DEBUG_MSG("GIFFChunk::check_name(): type='" << type << "' name='" << sname << "'\n"); + return (type==GIFFChunk::type || !type.length() && GIFFChunk::type=="FORM") + && sname==GIFFChunk::name; +} + +void +GIFFChunk::save(IFFByteStream & istr, bool use_trick) +{ + DEBUG_MSG("GIFFChunk::save(): saving chunk '" << get_full_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (is_container()) + { + istr.put_chunk(get_full_name(), use_trick); + if (chunks.size()) + { + GPosition pos; + for(pos=chunks;pos;++pos) + if (chunks[pos]->get_type()=="PROP") + chunks[pos]->save(istr); + for(pos=chunks;pos;++pos) + if (chunks[pos]->get_type()!="PROP") + chunks[pos]->save(istr); + } else + { + DEBUG_MSG("but it's empty => saving empty container.\n"); + } + istr.close_chunk(); + } else + { + istr.put_chunk(get_name(), use_trick); + istr.get_bytestream()->writall((const char *) data, data.size()); + istr.close_chunk(); + } +} + +void +GIFFChunk::add_chunk(const GP<GIFFChunk> & chunk, int position) +{ + DEBUG_MSG("GIFFChunk::add_chunk(): Adding chunk to '" << get_name() << + "' @ position=" << position << "\n"); + DEBUG_MAKE_INDENT(3); + + if (!type.length()) + { + DEBUG_MSG("Converting the parent to FORM\n"); + type="FORM"; + } + + if (chunk->get_type()=="PROP") + { + DEBUG_MSG("Converting the parent to LIST\n"); + type="LIST"; + } + + GPosition pos; + if (position>=0 && chunks.nth(position, pos)) + { + chunks.insert_before(pos, chunk); + }else + { + chunks.append(chunk); + } +} + +GUTF8String +GIFFChunk::decode_name(const GUTF8String &name, int &number) +{ + DEBUG_MSG("GIFFChunk::decode_name(): Checking brackets in name '" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (name.search('.')>=0) + G_THROW( ERR_MSG("GIFFManager.no_dots") ); + + number=0; + const int obracket=name.search('['); + GUTF8String short_name; + if (obracket >= 0) + { + const int cbracket=name.search(']',obracket+1); + if (cbracket < 0) + G_THROW( ERR_MSG("GIFFManager.unmatched") ); + if (name.length() > (unsigned int)(cbracket+1)) + G_THROW( ERR_MSG("GIFFManager.garbage") ); +// number =atoi((const char *)name.substr(obracket+1,cbracket-obracket-1)); + number= name.substr(obracket+1,cbracket-obracket-1).toInt(); + short_name=name.substr(0,obracket); + }else + { + short_name=name; + } + + const int colon=short_name.search(':'); + if (colon>=0) + short_name=short_name.substr(colon+1,(unsigned int)-1); + + for(int i=short_name.length();i<4;i++) + short_name.setat(i, ' '); + + DEBUG_MSG("short_name='" << short_name << "'\n"); + DEBUG_MSG("number=" << number << "\n"); + + return short_name; +} + +void +GIFFChunk::del_chunk(const GUTF8String &name) + // The name may contain brackets to specify the chunk number +{ + DEBUG_MSG("GIFFChunk::del_chunk(): Deleting chunk '" << name << + "' from '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + int number; + const GUTF8String short_name=decode_name(name,number); + + GPosition pos=chunks; + for(int num=0;pos;++pos) + { + if ((chunks[pos]->get_name()==short_name)&&(num++ == number)) + { + chunks.del(pos); + break; + } + } + if(! pos) + { + G_THROW( ERR_MSG("GIFFManager.no_chunk") "\t"+short_name+"\t"+GUTF8String(number)+"\t"+get_name()); + } +} + +GP<GIFFChunk> +GIFFChunk::get_chunk(const GUTF8String &name, int * pos_ptr) + // The name may contain brackets to specify the chunk number +{ + DEBUG_MSG("GIFFChunk::get_chunk(): Returning chunk '" << name << + "' from '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + int number; + const GUTF8String short_name=decode_name(name,number); + + int num=0; + int pos_num; + GP<GIFFChunk> retval; + GPosition pos; + for(pos=chunks, pos_num=0;pos;++pos, pos_num++) + { + if (chunks[pos]->get_name()==short_name && num++==number) + { + if (pos_ptr) + *pos_ptr=pos_num; + retval=chunks[pos]; + break; + } + } + return retval; +} + +int +GIFFChunk::get_chunks_number(void) +{ + DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name << + "' in '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + return chunks.size(); +} + +int +GIFFChunk::get_chunks_number(const GUTF8String &name) +{ + DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name << + "' in '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (name.contains("[]")>=0) + G_THROW( ERR_MSG("GIFFManager.no_brackets") ); + + int number; + GUTF8String short_name=decode_name(name,number); + + int num=0; + for(GPosition pos=chunks;pos;++pos) + num+=(chunks[pos]->get_name()==short_name); + return num; +} + +//************************************************************************ + +void +GIFFManager::add_chunk(GUTF8String parent_name, const GP<GIFFChunk> & chunk, + int pos) + // parent_name is the fully qualified name of the PARENT + // IT MAY BE EMPTY + // All the required chunks will be created + // pos=-1 means to append the chunk +{ + DEBUG_MSG("GIFFManager::add_chunk(): Adding chunk to name='" << parent_name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!top_level->get_name().length()) + { + if ((!parent_name.length())||(parent_name[0]!='.')) + G_THROW( ERR_MSG("GIFFManager.no_top_name") ); + if (parent_name.length() < 2) + { + // 'chunk' is actually the new top-level chunk + DEBUG_MSG("since parent_name=='.', making the chunk top-level\n"); + if (!chunk->is_container()) + G_THROW( ERR_MSG("GIFFManager.no_top_cont") ); + top_level=chunk; + return; + } + + DEBUG_MSG("Setting the name of the top-level chunk\n"); + const int next_dot=parent_name.search('.',1); + if(next_dot>=0) + { + top_level->set_name(parent_name.substr(1,next_dot-1)); + }else + { + top_level->set_name(parent_name.substr(1,(unsigned int)-1)); + } + } + + DEBUG_MSG("top level chunk name='" << top_level->get_name() << "'\n"); + + if (parent_name.length() && parent_name[0] == '.') + { + int next_dot=parent_name.search('.',1); + if(next_dot<0) + { + next_dot=parent_name.length(); + } + GUTF8String top_name=parent_name.substr(1,next_dot-1); + if (!top_level->check_name(top_name)) + G_THROW( ERR_MSG("GIFFManager.wrong_name") "\t"+top_name); + parent_name=parent_name.substr(next_dot,(unsigned int)-1); + } + + GP<GIFFChunk> cur_sec=top_level; + const char * start, * end=(const char *)parent_name-1; + do + { + for(start=++end;*end&&(*end!='.');end++) + EMPTY_LOOP; + if (end>start) + { + GUTF8String name(start,end-start); + GUTF8String short_name; + int number=0; + const int obracket=name.search('['); + if (obracket >= 0) + { + const int cbracket=name.search(']',obracket+1); + if (cbracket < 0) + G_THROW( ERR_MSG("GIFFManager.unmatched") ); +// number=atoi((const char *)name.substr(obracket+1,cbracket-obracket-1)); + number = name.substr(obracket+1,cbracket-obracket-1).toInt(); + short_name=name.substr(0,obracket); + }else + { + short_name=name; + } + + for(int i=cur_sec->get_chunks_number(short_name);i<number+1;i++) + cur_sec->add_chunk(GIFFChunk::create(short_name)); + cur_sec=cur_sec->get_chunk(name); + if (!cur_sec) + G_THROW( ERR_MSG("GIFFManager.unknown") "\t"+name); + } + } while(*end); + cur_sec->add_chunk(chunk, pos); +} + +void +GIFFManager::add_chunk(GUTF8String name, const TArray<char> & data) + // name is fully qualified name of the chunk TO BE INSERTED. + // it may contain brackets at the end to set the position + // All the required chunks will be created +{ + DEBUG_MSG("GIFFManager::add_chunk(): adding plain chunk with name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + GUTF8String chunk_name; + const int lastdot=name.rsearch('.'); + if(lastdot < 0) + { + chunk_name=name; + name=name.substr(0,lastdot); + }else + { + chunk_name=name.substr(lastdot+1,(unsigned int)-1); + } + + int pos=-1; + const int obracket=chunk_name.search('['); + if (obracket >= 0) + { + const int cbracket=chunk_name.search(']',obracket+1); + if (cbracket < 0) + G_THROW( ERR_MSG("GIFFManager.unmatched") ); + if (name.length() > (unsigned int)(cbracket+1)) + G_THROW( ERR_MSG("GIFFManager.garbage") ); +// pos=atoi((const char *)chunk_name.substr(obracket+1,cbracket-obracket-1)); + pos = chunk_name.substr(obracket+1,cbracket-obracket-1).toInt(); + chunk_name=chunk_name.substr(0,obracket); + } + DEBUG_MSG("Creating new chunk with name " << chunk_name << "\n"); + GP<GIFFChunk> chunk; + chunk=GIFFChunk::create(chunk_name, data); + add_chunk(name, chunk, pos); +} + +void +GIFFManager::del_chunk(void) +{ + DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk\n"); + DEBUG_MAKE_INDENT(3); + + G_THROW( ERR_MSG("GIFFManager.del_empty") ); +} + +void +GIFFManager::del_chunk(GUTF8String name) + // "name" should be fully qualified, that is contain dots. + // It may also end with [] to set the chunk order number +{ + DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk '" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!name.length()) + G_THROW( ERR_MSG("GIFFManager.del_empty") ); + + if (name[0]=='.') + { + const int next_dot=name.search('.',1); + if (next_dot < 0) + { + if (top_level->check_name(name.substr(1,(unsigned int)-1))) + { + DEBUG_MSG("Removing top level chunk..\n"); + top_level=GIFFChunk::create(); + return; + } + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1)); + } + const GUTF8String top_name=name.substr(1,next_dot-1); + if (!top_level->check_name(top_name)) + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name); + name=name.substr(next_dot+1,(unsigned int)-1); + } + + GP<GIFFChunk> cur_sec=top_level; + const char * start, * end=(const char *)name-1; + do + { + for(start=++end;*end&&(*end!='.');end++) + EMPTY_LOOP; + if (end>start && *end=='.') + cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start)); + if (!cur_sec) + G_THROW( ERR_MSG("GIFFManager.cant_find") "\t"+GUTF8String(name)); + } while(*end); + + if (!start[0]) + { + G_THROW(GUTF8String( ERR_MSG("GIFFManager.malformed") "\t")+name); + } + + cur_sec->del_chunk(start); +} + +GP<GIFFChunk> +GIFFManager::get_chunk(GUTF8String name, int * pos_num) + // "name" should be fully qualified, that is contain dots. + // It may also end with [] to set the chunk order number +{ + DEBUG_MSG("GIFFManager::get_chunk(): Returning chunk '" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!name.length()) + G_THROW( ERR_MSG("GIFFManager.get_empty") ); + + if (name[0]=='.') + { + const int next_dot=name.search('.',1); + if (next_dot < 0) + { + if (top_level->check_name(name.substr(1,(unsigned int)-1))) + { + DEBUG_MSG("Removing top level chunk..\n"); + return top_level; + } + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1)); + } + const GUTF8String top_name=name.substr(1,next_dot-1); + if (!top_level->check_name(top_name)) + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name); + name=name.substr(next_dot+1,(unsigned int)-1); + } + + GP<GIFFChunk> cur_sec=top_level; + const char * start, * end=(const char *) name-1; + do + { + for(start=++end;*end&&(*end!='.');end++) + EMPTY_LOOP; + if (end>start) + cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start), pos_num); + if (!cur_sec) + break; + } while(*end); + + return cur_sec; +} + +int +GIFFManager::get_chunks_number(void) +{ + DEBUG_MSG("GIFFManager::get_chunks_number()\n"); + DEBUG_MAKE_INDENT(3); + return top_level->get_chunks_number(); +} + +int +GIFFManager::get_chunks_number(const GUTF8String &name) + // Returns the number of chunks with given fully qualified name +{ + DEBUG_MSG("GIFFManager::get_chunks_number(): name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + int retval; + const int last_dot=name.rsearch('.'); + if (last_dot<0) + { + retval=top_level->get_chunks_number(name); + }else if(!last_dot) + { + retval=(top_level->get_name()==name.substr(1,(unsigned int)-1))?1:0; + }else + { + GP<GIFFChunk> chunk=get_chunk(name.substr(0,last_dot)); + retval=( chunk + ?(chunk->get_chunks_number(name.substr(last_dot+1,(unsigned int)-1))) + :0 ); + } + return retval; +} + +void +GIFFManager::load_chunk(IFFByteStream & istr, GP<GIFFChunk> chunk) +{ + DEBUG_MSG("GIFFManager::load_chunk(): loading contents of chunk '" << + chunk->get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + int chunk_size; + GUTF8String chunk_id; + while ((chunk_size=istr.get_chunk(chunk_id))) + { + if (istr.check_id(chunk_id)) + { + GP<GIFFChunk> ch=GIFFChunk::create(chunk_id); + load_chunk(istr, ch); + chunk->add_chunk(ch); + } else + { + TArray<char> data(chunk_size-1); + istr.get_bytestream()->readall( (char*)data, data.size()); + GP<GIFFChunk> ch=GIFFChunk::create(chunk_id, data); + chunk->add_chunk(ch); + } + istr.close_chunk(); + } +} + +void +GIFFManager::load_file(const TArray<char> & data) +{ + GP<ByteStream> str=ByteStream::create((const char *)data, data.size()); + load_file(str); +} + +void +GIFFManager::load_file(GP<ByteStream> str) +{ + DEBUG_MSG("GIFFManager::load_file(): Loading IFF file.\n"); + DEBUG_MAKE_INDENT(3); + + GP<IFFByteStream> gistr=IFFByteStream::create(str); + IFFByteStream &istr=*gistr; + GUTF8String chunk_id; + if (istr.get_chunk(chunk_id)) + { + if (chunk_id.substr(0,5) != "FORM:") + G_THROW( ERR_MSG("GIFFManager.cant_find2") ); + set_name(chunk_id); + load_chunk(istr, top_level); + istr.close_chunk(); + } +} + +void +GIFFManager::save_file(TArray<char> & data) +{ + GP<ByteStream> gstr=ByteStream::create(); + save_file(gstr); + data=gstr->get_data(); +} + +void +GIFFManager::save_file(GP<ByteStream> str) +{ + GP<IFFByteStream> istr=IFFByteStream::create(str); + top_level->save(*istr, 1); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GIFFManager.h b/kviewshell/plugins/djvu/libdjvu/GIFFManager.h new file mode 100644 index 00000000..722f592f --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GIFFManager.h @@ -0,0 +1,394 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GIFFManager.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GIFFMANAGER_H +#define _GIFFMANAGER_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "IFFByteStream.h" +#include "GContainer.h" +#include "Arrays.h" +#include "GSmartPointer.h" +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name GIFFManager.h + + Files #"GIFFManager.h"# and #"GIFFManager.cpp"# define more convenient + interface to IFF files. You may want to use the {\Ref GIFFManager} class + instead of coping with {\Ref IFFByteStream} especially when you have to + insert or move chunks, which is a kind of tricky with sequential access + provided by {\Ref IFFByteStream}. + + You will mostly deal with {\Ref GIFFManager} class, but sometimes you may + want to use {\Ref GIFFChunk}s as well thus bypassing {\Ref GIFFManager}'s + interface and working with the chunks hierarchy yourself. + + Interface to IFF files. + @author + Andrei Erofeev <eaf@geocities.com> -- Initial implementation. + @version + #$Id: GIFFManager.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ + +/** #GIFFChunk# is the base class for other IFF chunks understood by + {\Ref GIFFManager}. It provides some basic interface, and is not supposed + to be used on its own. */ + +class GIFFChunk : public GPEnabled +{ +protected: + GIFFChunk(void); + GIFFChunk(const GUTF8String &name); + GIFFChunk(const GUTF8String &name, const TArray<char> & data); +public: + /// Default creator. + static GP<GIFFChunk> create(void) {return new GIFFChunk();} + + /** Creates the chunk with the given name. The {\em name} may not + contain dots colons or brackets */ + static GP<GIFFChunk> create(const GUTF8String &name) + {return new GIFFChunk(name);} + + /** Creates the {\em plain chunk} containing raw data */ + static GP<GIFFChunk> create(const GUTF8String &name, const TArray<char> & data) + { return new GIFFChunk(name,data); } + + /// Destructor + virtual ~GIFFChunk(void); + + /// Returns the name of the chunk (without possible #FORM:# or similar prefixes) + GUTF8String get_name(void) const; + /// Returns full chunk name, with possible container specification + GUTF8String get_full_name(void) const; + /// Returns the chunk type, like #CAT# for chunk #CAT:DJVU# + GUTF8String get_type(void) const; + /// Returns TRUE if the chunk may contain other chunks or FALSE otherwise + bool is_container(void) const; + /** Sets the chunk name. The {\em name} may not contain dots or brackets, + but {\bf may} contain colons. */ + void set_name(GUTF8String name); + /** Parses the {\em name} probably containing colon and compares it + with its own name returning TRUE if they are the same */ + bool check_name(GUTF8String name); + + /** Adds the {\em chunk} to the chunks list at position {\em order}. + Set {\em order} to #-1# to append the chunk to the list. + {\bf Note!} By adding chunk #PROP# you will convert this chunk + to type #LIST# {\em automatically}. */ + void add_chunk(const GP<GIFFChunk> & chunk, int order=-1); + /** Removes the chunk with given {\em name}. The {\em name} may not + contain dots, but MAY contain colons and brackets (the latter - + for specifying the chunk number) */ + void del_chunk(const GUTF8String &name); + /** Returns the chunk with given {\em name}. The {\em name} may not + contain dots, but MAY contain colons and brackets (the latter - + for specifying the chunk number). If {\em position} is not zero + then the chunk position in its parent will be put into #*position# */ + GP<GIFFChunk>get_chunk(const GUTF8String &name, int * position=0); + /** Returns the number of chunks with given {\em name}. The {\em name} + may not contain dots and brackets. If {\em name} is ZERO, the + total number of chunks will be returned. */ + int get_chunks_number(const GUTF8String &name); + int get_chunks_number(void); + /** Returns the data array for plain chunks */ + TArray<char> get_data(void) const; + + /** Saves the chunk into the {\Ref IFFByteStream}. + Set {\em use_trick} to #1# if this is a top-level chunk */ + void save(IFFByteStream & istr, bool use_trick=0); +private: + char name[5]; + GUTF8String type; + GPList<GIFFChunk> chunks; + TArray<char> data; + static GUTF8String decode_name(const GUTF8String &name, int &number); +}; + +inline GUTF8String +GIFFChunk::get_name(void) const { return GUTF8String(name, 4); } + +inline GUTF8String +GIFFChunk::get_type(void) const { return type; } + +inline GUTF8String +GIFFChunk::get_full_name(void) const { return get_type()+":"+get_name(); } + +inline bool +GIFFChunk::is_container(void) const { return type.length()!=0; } + +inline TArray<char> +GIFFChunk::get_data(void) const { return data; } + +inline +GIFFChunk::GIFFChunk(void) { name[0]=0; } + +inline +GIFFChunk::GIFFChunk(const GUTF8String &name) { set_name(name); } + +inline +GIFFChunk::GIFFChunk(const GUTF8String &name, const TArray<char> & data_in) : + data(data_in) +{ + set_name(name); +} + +//************************************************************************ + +/** Intuitive interface to IFF files. + + It's too terrible to keep reading/writing IFF files chunk after chunk + using {\Ref IFFByteStream}s. This class allows you to operate with chunks + as with structures or arrays without even caring about the byte streams. + + Some of the examples are below: + \begin{verbatim} + GP<GIFFChunk> chunk; + chunk=manager1.get_chunk("BG44[2]"); + manager2.add_chunk(".FORM:DJVU.BG44[-1]", chunk); + \end{verbatim} + + {\bf Chunk name} + \begin{itemize} + \item Every chunk name may contain optional prefix #FORM:#, #LIST:#, + #PROP:# or #CAT:#. If the prefix is omitted and the chunk happens + to contain other chunks, #FORM:# will be assumed. + \item Every chunk name may be {\em short} or {\em complete}. + {\em short} chunk names may not contain dots as they're a + subchunks names with respect to a given chunk. + {\em complete} chunk names may contain dots. But there may be + or may not be the {\em leading dot} in the name. If the + {\em leading dot} is present, then the name is assumed to contain + the name of the top-level chunk as well. Otherwise it's treated + {\em with respect} to the top-level chunk. You may want to use + the leading dot only when you add a chunk to an empty document, + since a command like #manager.addChunk(".FORM:DJVU.BG44", chunk)# + will create the top level chunk of the requested type (#FORM:DJVU#) + and will add chunk #BG44# to it {\em automatically}. + \item You may use {\em brackets} in the name to specify the chunk's + position. The meaning of the number inside the brackets depends + on the function you call. In most of the cases this is the number + of the chunk with the given name in the parent chunk. But sometimes + (as in #addChunk(name, buffer, length)#) the brackets at the + end of the #name# actually specify the {\em position} of the + chunk in the parent. For example, to insert #INCL# chunk into + #DJVU# form at position #1# (make it the second) you may want to + use #manager.addChunk(".DJVU.INCL[1]", data, size)#. At the same + time, to get 2-nd chunk with name #BG44# from form #DJVU# you + should do smth like #chunk=manager.getChunk("BG44[1]")#. Note, that + here the manager will search for chunk #BG44# in form #DJVU# and + will take the second {\em found} one. + \end{itemize} */ + +class GIFFManager : public GPEnabled +{ +protected: + GIFFManager(void); + void init(void); + void init(const GUTF8String &name); +public: + /// Default creator. + static GP<GIFFManager> create(void); + + /** Creates the {\Ref GIFFManager} and assigns name {\em name} to + the top-level chunk. you may use chunk type names (before colon) + to set the top-level chunk type, or omit it to work with #FORM# */ + static GP<GIFFManager> create(const GUTF8String &name); + + /// Virtual destructor. + virtual ~GIFFManager(void); + + /// Sets the name of the top level chunk to {\em name} + void set_name(const GUTF8String &name); + /** Adds the chunk {\em chunk} to chunk with name {\em parent_name} at + position {\em pos}. {\em parent_name} may contain dots, brackets + and colons. All missing chunks in the chain will be created. + + {\bf Examples:} + \begin{verbatim} + ;; To set the top-level chunk to 'ch' + m.addChunk(".", ch); + ;; To add 'ch' to the top-level chunk "DJVU" creating it if necessary + m.addChunk(".DJVU", ch); + ;; Same as above regardless of top-level chunk name + m.addChunk("", ch); + ;; To add 'ch' to 2nd FORM DJVU in top-level form DJVM + m.addChunk(".FORM:DJVM.FORM:DJVU[1]", ch); + ;; Same thing regardless of the top-level chunk name + m.addChunk("FORM:DJVU[1]", ch); + \end{verbatim} */ + void add_chunk(GUTF8String parent_name, const GP<GIFFChunk> & chunk, int pos=-1); + /** If {\em name}={\em name1}.{\em name2} where {\em name2} doesn't + contain dots, then #addChunk()# will create plain chunk with + name {\em name2} with data {\em buffer} of size {\em length} and + will add it to chunk {\em name1} in the same way as + #addChunk(name, chunk, pos)# function would do it. The #pos# in + this case is either #-1# (append) or is extracted from between + brackets if the {\em name} ends with them. + + {\bf Examples:} + \begin{verbatim} + ;; To insert INCL chunk at position 2 (make it 3rd) + m.addChunk("INCL[2]", data, length); + ;; To append chunk BG44 to 2nd DjVu file inside DjVm archive: + m.addChunk(".DJVM.DJVU[1].BG44", data, length); + \end{verbatim} */ + void add_chunk(GUTF8String name, const TArray<char> & data); + /** Will remove chunk with name {\em name}. You may use dots, colons + and brackets to specify the chunk uniquely. + + {\bf Examples:} + \begin{verbatim} + ;; To remove 2nd DjVu document from DjVm archive use + m.delChunk(".DJVM.DJVU[1]"); + ;; Same thing without top-level chunk name specification + m.delChunk("DJVU[1]"); + ;; Same thing for the first DJVU chunk + m.delChunk("DJVU"); + \end{verbatim} + */ + void del_chunk(GUTF8String name); + void del_chunk(void); + /** Will return the number of chunks with given name. The {\em name} may + not end with brackets, but may contain them inside. It may also + contain dots and colons. If {\em name} is ZERO, the total number + of chunks will be returned. + + {\bf Examples:} + \begin{verbatim} + ;; To get the number of DJVU forms inside DjVm document + m.getChunksNumber(".DJVM.DJVU"); + ;; Same thing without top-level chunk name specification + m.getChunksNumber("DJVU"); + \end{verbatim} + */ + int get_chunks_number(const GUTF8String &name); + int get_chunks_number(void); + + /** Returns the chunk with name {\em name}. The {\em name} may contain dots + colons and slashes. If {\em position} is not zero, #*position# will + be assigned the position of the found chunk in the parent chunk. + + {\bf Examples:} + \begin{verbatim} + ;; To get the directory chunk of DjVm document + m.getChunk(".DJVM.DIR0"); + ;; To get chunk corresponding to 2nd DJVU form + m.getChunk(".DJVU[1]"); + \end{verbatim} */ + GP<GIFFChunk>get_chunk(GUTF8String name, int * position=0); + + /** Loads the composite {\em chunk}'s contents from stream {\em istr}. */ + void load_chunk(IFFByteStream & istr, GP<GIFFChunk> chunk); + /** Loads the file contents from stream {\em str} */ + void load_file(GP<ByteStream> str); + /** Loads the file contents from the data array {\em data} */ + void load_file(const TArray<char> & data); + /** Saves all the chunks into stream {\em str} */ + void save_file(GP<ByteStream> str); + /** Saves all the chunks into the data array {\em data} */ + void save_file(TArray<char> & data); + +private: + GP<GIFFChunk> top_level; + + static const char * check_leading_dot(const GUTF8String &name); +private: //dummy methods + static void save_file(ByteStream *); + static void load_file(ByteStream *); +}; + +inline void +GIFFManager::set_name(const GUTF8String &name) +{ + top_level->set_name(name); +} + +inline +GIFFManager::GIFFManager(void) {} + +inline void +GIFFManager::init(void) +{ + top_level=GIFFChunk::create(); +} + +inline void +GIFFManager::init(const GUTF8String &name) +{ + top_level=GIFFChunk::create(name); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GMapAreas.cpp b/kviewshell/plugins/djvu/libdjvu/GMapAreas.cpp new file mode 100644 index 00000000..5a85e1fc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GMapAreas.cpp @@ -0,0 +1,1066 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GMapAreas.cpp,v 1.9 2004/05/05 15:12:42 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GMapAreas.h" +#include "GException.h" +#include "debug.h" + +#include <math.h> +#include <stdio.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/**************************************************************************** +***************************** GMapArea definition *************************** +****************************************************************************/ + +const char GMapArea::MAPAREA_TAG[] = "maparea"; +const char GMapArea::RECT_TAG[] = "rect"; +const char GMapArea::POLY_TAG[] = "poly"; +const char GMapArea::OVAL_TAG[] = "oval"; +const char GMapArea::NO_BORDER_TAG[] = "none"; +const char GMapArea::XOR_BORDER_TAG[] = "xor"; +const char GMapArea::SOLID_BORDER_TAG[] = "border"; +const char GMapArea::SHADOW_IN_BORDER_TAG[] = "shadow_in"; +const char GMapArea::SHADOW_OUT_BORDER_TAG[] = "shadow_out"; +const char GMapArea::SHADOW_EIN_BORDER_TAG[] = "shadow_ein"; +const char GMapArea::SHADOW_EOUT_BORDER_TAG[] = "shadow_eout"; +const char GMapArea::BORDER_AVIS_TAG[] = "border_avis"; +const char GMapArea::HILITE_TAG[] = "hilite"; +const char GMapArea::URL_TAG[] = "url"; +const char GMapArea::TARGET_SELF[] = "_self"; +static const char zero_width[] = ERR_MSG("GMapAreas.zero_width"); +static const char zero_height[] = ERR_MSG("GMapAreas.zero_height"); +static const char width_1[] = ERR_MSG("GMapAreas.width_1"); +static const char width_3_32 [] = ERR_MSG("GMapAreas.width_3-32"); +static const char error_poly_border [] = ERR_MSG("GMapAreas.poly_border"); +static const char error_poly_hilite [] = ERR_MSG("GMapAreas.poly_hilite"); +static const char error_oval_border [] = ERR_MSG("GMapAreas.oval_border"); +static const char error_oval_hilite [] = ERR_MSG("GMapAreas.oval_hilite"); +static const char error_too_few_points [] = ERR_MSG("GMapAreas.too_few_points"); +static const char error_intersect [] = ERR_MSG("GMapAreas.intersect"); + +GMapArea::~GMapArea() {} + +GMapRect::~GMapRect() {} + +GMapPoly::~GMapPoly() {} + +GMapOval::~GMapOval() {} + +void +GMapArea::initialize_bounds(void) +{ + xmin=gma_get_xmin(); + xmax=gma_get_xmax(); + ymin=gma_get_ymin(); + ymax=gma_get_ymax(); + bounds_initialized=true; +} + +int +GMapArea::get_xmin(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return xmin; +} + +int +GMapArea::get_ymin(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return ymin; +} + +int +GMapArea::get_xmax(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return xmax; +} + +int +GMapArea::get_ymax(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return ymax; +} + +GRect +GMapArea::get_bound_rect(void) const +{ + return GRect(get_xmin(), get_ymin(), get_xmax()-get_xmin(), + get_ymax()-get_ymin()); +} + +void +GMapArea::move(int dx, int dy) +{ + if (dx || dy) + { + if (bounds_initialized) + { + xmin+=dx; + ymin+=dy; + xmax+=dx; + ymax+=dy; + } + gma_move(dx, dy); + } +} + +void +GMapArea::resize(int new_width, int new_height) +{ + if (get_xmax()-get_xmin()!=new_width || + get_ymax()-get_ymin()!=new_height) + { + gma_resize(new_width, new_height); + bounds_initialized=false; + } +} + +void +GMapArea::transform(const GRect & grect) +{ + if (grect.xmin!=get_xmin() || grect.ymin!=get_ymin() || + grect.xmax!=get_xmax() || grect.ymax!=get_ymax()) + { + gma_transform(grect); + bounds_initialized=false; + } +} + +char const * const +GMapArea::check_object(void) +{ + char const *retval; + if (get_xmax()==get_xmin()) + { + retval=zero_width; + } + else if (get_ymax()==get_ymin()) + { + retval=zero_height; + } + else if ((border_type==XOR_BORDER || + border_type==SOLID_BORDER) && border_width!=1) + { + retval=width_1; + } + else if ((border_type==SHADOW_IN_BORDER || + border_type==SHADOW_OUT_BORDER || + border_type==SHADOW_EIN_BORDER || + border_type==SHADOW_EOUT_BORDER)&& + (border_width<3 || border_width>32)) + { + retval=width_3_32; + }else + { + retval=gma_check_object(); + } + return retval; +} + +bool +GMapArea::is_point_inside(int x, int y) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return (x>=xmin && x<xmax && y>=ymin && y<ymax) ? + gma_is_point_inside(x, y) : false; +} + +GUTF8String +GMapArea::print(void) +{ + // Make this hard check to make sure, that *no* illegal GMapArea + // can be stored into a file. + const char * const errors=check_object(); + if (errors[0]) + { + G_THROW(errors); + } + + int i; + GUTF8String tmp; + GUTF8String url1, target1, comment1; + const GUTF8String url_str=url; + for(i=0;i<(int) url_str.length();i++) + { + char ch=url_str[i]; + if (ch=='"') + url1+='\\'; + url1+=ch; + } + for(i=0;i<(int) target.length();i++) + { + char ch=target[i]; + if (ch=='"') + target1+='\\'; + target1+=ch; + } + for(i=0;i<(int) comment.length();i++) + { + char ch=comment[i]; + if (ch=='"') + comment1+='\\'; + comment1+=ch; + } + + GUTF8String border_color_str; + border_color_str.format("#%02X%02X%02X", + (border_color & 0xff0000) >> 16, + (border_color & 0xff00) >> 8, + (border_color & 0xff)); + + static const GUTF8String left('('); + static const GUTF8String right(')'); + static const GUTF8String space(' '); + static const GUTF8String quote('"'); + GUTF8String border_type_str; + switch(border_type) + { + case NO_BORDER: + border_type_str=left+NO_BORDER_TAG+right; + break; + case XOR_BORDER: + border_type_str=left+XOR_BORDER_TAG+right; + break; + case SOLID_BORDER: + border_type_str=left+SOLID_BORDER_TAG+space+border_color_str+right; + break; + case SHADOW_IN_BORDER: + border_type_str=left+SHADOW_IN_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + case SHADOW_OUT_BORDER: + border_type_str=left+SHADOW_OUT_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + case SHADOW_EIN_BORDER: + border_type_str=left+SHADOW_EIN_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + case SHADOW_EOUT_BORDER: + border_type_str=left+SHADOW_EOUT_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + default: + border_type_str=left+XOR_BORDER_TAG+right; + break; + } + + GUTF8String hilite_str; + if (hilite_color!=0xffffffff) + { + hilite_str.format("(%s #%02X%02X%02X)", + HILITE_TAG, (hilite_color & 0xff0000) >> 16, + (hilite_color & 0xff00) >> 8, + (hilite_color & 0xff)); + } + + GUTF8String URL; + if (target1==TARGET_SELF) + { + URL=quote+url1+quote; + }else + { + URL=left+URL_TAG+space+quote+url1+quote+space+quote+target1+quote+right; + } + + GUTF8String total=left+MAPAREA_TAG+space+URL+space+quote+comment1+quote+space+gma_print()+border_type_str; + if (border_always_visible) + total+=space+left+BORDER_AVIS_TAG+right; + if ( hilite_str.length() > 0 ) + total+=space+hilite_str; + total+=right; + return total; +} + +/* +void +GMapArea::map(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect = GRect(xmin, ymin, xmax, ymax); + mapper.map(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} +void +GMapArea::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect = GRect(xmin, ymin, xmax, ymax); + mapper.unmap(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} +*/ + + +/// Virtual function generating a list of defining coordinates +/// (default are the opposite corners of the enclosing rectangle) +void GMapArea::get_coords( GList<int> & CoordList ) const +{ + CoordList.append( get_xmin() ); + CoordList.append( get_ymin() ); + CoordList.append( get_xmax() ); + CoordList.append( get_ymax() ); +} + + +/**************************************************************************** +**************************** GMapRect definition **************************** +****************************************************************************/ + +void +GMapRect::gma_resize(int new_width, int new_height) +{ + xmax=xmin+new_width; + ymax=ymin+new_height; +} + +void +GMapRect::gma_transform(const GRect & grect) +{ + xmin=grect.xmin; ymin=grect.ymin; + xmax=grect.xmax; ymax=grect.ymax; +} + +GUTF8String +GMapRect::gma_print(void) +{ + GUTF8String buffer; + return buffer.format("(%s %d %d %d %d) ", + RECT_TAG, xmin, ymin, xmax-xmin, ymax-ymin); +} + +void +GMapRect::map(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.map(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} +void +GMapRect::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.unmap(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} + +/**************************************************************************** +**************************** GMapPoly definition **************************** +****************************************************************************/ + +inline int +GMapPoly::sign(int x) { return x<0 ? -1 : x>0 ? 1 : 0; } + +bool +GMapPoly::does_side_cross_rect(const GRect & grect, int side) +{ + int x1=xx[side], x2=xx[(side+1)%points]; + int y1=yy[side], y2=yy[(side+1)%points]; + int xmin=x1<x2 ? x1 : x2; + int ymin=y1<y2 ? y1 : y2; + int xmax=x1+x2-xmin; + int ymax=y1+y2-ymin; + + if (xmax<grect.xmin || xmin>grect.xmax || + ymax<grect.ymin || ymin>grect.ymax) return false; + + return + x1>=grect.xmin && x1<=grect.xmax && y1>=grect.ymin && y1<=grect.ymax || + x2>=grect.xmin && x2<=grect.xmax && y2>=grect.ymin && y2<=grect.ymax || + do_segments_intersect(grect.xmin, grect.ymin, grect.xmax, grect.ymax, + x1, y1, x2, y2) || + do_segments_intersect(grect.xmax, grect.ymin, grect.xmin, grect.ymax, + x1, y1, x2, y2); +} + +bool +GMapPoly::is_projection_on_segment(int x, int y, int x1, int y1, int x2, int y2) +{ + int res1=(x-x1)*(x2-x1)+(y-y1)*(y2-y1); + int res2=(x-x2)*(x2-x1)+(y-y2)*(y2-y1); + return sign(res1)*sign(res2)<=0; +} + +bool +GMapPoly::do_segments_intersect(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22) +{ + int res11=(x11-x21)*(y22-y21)-(y11-y21)*(x22-x21); + int res12=(x12-x21)*(y22-y21)-(y12-y21)*(x22-x21); + int res21=(x21-x11)*(y12-y11)-(y21-y11)*(x12-x11); + int res22=(x22-x11)*(y12-y11)-(y22-y11)*(x12-x11); + if (!res11 && !res12) + { + // Segments are on the same line + return + is_projection_on_segment(x11, y11, x21, y21, x22, y22) || + is_projection_on_segment(x12, y12, x21, y21, x22, y22) || + is_projection_on_segment(x21, y21, x11, y11, x12, y12) || + is_projection_on_segment(x22, y22, x11, y11, x12, y12); + } + int sign1=sign(res11)*sign(res12); + int sign2=sign(res21)*sign(res22); + return sign1<=0 && sign2<=0; +} + +bool +GMapPoly::are_segments_parallel(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22) +{ + return (x12-x11)*(y22-y21)-(y12-y11)*(x22-x21)==0; +} + +char const * const +GMapPoly::check_data(void) +{ + if (open && points<2 || !open && points<3) + return error_too_few_points; + for(int i=0;i<sides;i++) + { + for(int j=i+2;j<sides;j++) + { + if (i != (j+1)%points ) + { + if (do_segments_intersect(xx[i], yy[i], xx[i+1], yy[i+1], + xx[j], yy[j], xx[(j+1)%points], yy[(j+1)%points])) + { + return error_intersect; + } + } + } + } + return ""; +} + +void +GMapPoly::optimize_data(void) +{ + // Removing segments of length zero + int i; + for(i=0;i<sides;i++) + { + while(xx[i]==xx[(i+1)%points] && yy[i]==yy[(i+1)%points]) + { + for(int k=(i+1)%points;k<points-1;k++) + { + xx[k]=xx[k+1]; yy[k]=yy[k+1]; + } + points--; sides--; + if (!points) return; + } + } + // Concatenating consequitive parallel segments + for(i=0;i<sides;i++) + { + while((open && i+1<sides || !open) && + are_segments_parallel(xx[i], yy[i], + xx[(i+1)%points], yy[(i+1)%points], + xx[(i+1)%points], yy[(i+1)%points], + xx[(i+2)%points], yy[(i+2)%points])) + { + for(int k=(i+1)%points;k<points-1;k++) + { + xx[k]=xx[k+1]; yy[k]=yy[k+1]; + } + points--; sides--; + if (!points) return; + } + } +} + +bool +GMapPoly::gma_is_point_inside(const int xin, const int yin) const +{ + if (open) + return false; + + int xfar=get_xmax()+(get_xmax()-get_xmin()); + + int intersections=0; + for(int i=0;i<points;i++) + { + int res1=yy[i]-yin; + if (!res1) continue; + int res2, isaved=i; + while(!(res2=yy[(i+1)%points]-yin)) i++; + if (isaved!=i) + { + // Some points fell exactly on the line + if ((xx[(isaved+1)%points]-xin)* + (xx[i%points]-xin)<=0) + { + // Test point is exactly on the boundary + return true; + } + } + if (res1<0 && res2>0 || res1>0 && res2<0) + { + int x1=xx[i%points], y1=yy[i%points]; + int x2=xx[(i+1)%points], y2=yy[(i+1)%points]; + int _res1=(xin-x1)*(y2-y1)-(yin-y1)*(x2-x1); + int _res2=(xfar-x1)*(y2-y1)-(yin-y1)*(x2-x1); + if (!_res1 || !_res2) + { + // The point is on this boundary + return true; + } + if (sign(_res1)*sign(_res2)<0) intersections++; + } + } + return (intersections % 2)!=0; +} + +int +GMapPoly::gma_get_xmin(void) const +{ + int x=xx[0]; + for(int i=1;i<points;i++) + if (x>xx[i]) x=xx[i]; + return x; +} + +int +GMapPoly::gma_get_xmax(void) const +{ + int x=xx[0]; + for(int i=1;i<points;i++) + if (x<xx[i]) x=xx[i]; + return x+1; +} + +int +GMapPoly::gma_get_ymin(void) const +{ + int y=yy[0]; + for(int i=1;i<points;i++) + if (y>yy[i]) y=yy[i]; + return y; +} + +int +GMapPoly::gma_get_ymax(void) const +{ + int y=yy[0]; + for(int i=1;i<points;i++) + if (y<yy[i]) y=yy[i]; + return y+1; +} + +void +GMapPoly::gma_move(int dx, int dy) +{ + for(int i=0;i<points;i++) + { + xx[i]+=dx; yy[i]+=dy; + } +} + +void +GMapPoly::gma_resize(int new_width, int new_height) +{ + int width=get_xmax()-get_xmin(); + int height=get_ymax()-get_ymin(); + int xmin=get_xmin(), ymin=get_ymin(); + for(int i=0;i<points;i++) + { + xx[i]=xmin+(xx[i]-xmin)*new_width/width; + yy[i]=ymin+(yy[i]-ymin)*new_height/height; + } +} + +void +GMapPoly::gma_transform(const GRect & grect) +{ + int width=get_xmax()-get_xmin(); + int height=get_ymax()-get_ymin(); + int xmin=get_xmin(), ymin=get_ymin(); + for(int i=0;i<points;i++) + { + xx[i]=grect.xmin+(xx[i]-xmin)*grect.width()/width; + yy[i]=grect.ymin+(yy[i]-ymin)*grect.height()/height; + } +} + +char const * const +GMapPoly::gma_check_object(void) const +{ + const char * str; + str=(border_type!=NO_BORDER && + border_type!=SOLID_BORDER && + border_type!=XOR_BORDER) ? error_poly_border: + ((hilite_color!=0xffffffff) ? error_poly_hilite:""); + return str; +} + +GMapPoly::GMapPoly(const int * _xx, const int * _yy, int _points, bool _open) : + open(_open), points(_points) +{ + sides=points-(open!=0); + + xx.resize(points-1); yy.resize(points-1); + for(int i=0;i<points;i++) + { + xx[i]=_xx[i]; yy[i]=_yy[i]; + } + optimize_data(); + char const * const res=check_data(); + if (res[0]) + G_THROW(res); +} + +int +GMapPoly::add_vertex(int x, int y) +{ + points++; + sides=points-(open!=0); + + xx.resize(points-1); yy.resize(points-1); + xx[points-1] = x; + yy[points-1] = y; + + return points; +} + +void +GMapPoly::close_poly() +{ + open = false; + sides=points; +} + +GUTF8String +GMapPoly::gma_print(void) +{ + static const GUTF8String space(' '); + GUTF8String res=GUTF8String('(')+POLY_TAG+space; + for(int i=0;i<points;i++) + { + GUTF8String buffer; + res+=buffer.format("%d %d ", xx[i], yy[i]); + } + res.setat(res.length()-1, ')'); + res+=space; + return res; +} + +/// Virtual function generating a list of defining coordinates +void GMapPoly::get_coords( GList<int> & CoordList ) const +{ + for(int i = 0 ; i < points ; i++) + { + CoordList.append( xx[i] ); + CoordList.append( yy[i] ); + } +} + +void +GMapPoly::map(GRectMapper &mapper) +{ + get_bound_rect(); + for(int i=0; i<points; i++) + { + mapper.map(xx[i], yy[i]); + } + clear_bounds(); +} + +void +GMapPoly::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + for(int i=0; i<points; i++) + { + mapper.unmap(xx[i], yy[i]); + } + clear_bounds(); +} + + + +/**************************************************************************** +**************************** GMapOval definition **************************** +****************************************************************************/ + +void +GMapOval::gma_resize(int new_width, int new_height) +{ + xmax=xmin+new_width; + ymax=ymin+new_height; + initialize(); +} + +void +GMapOval::gma_transform(const GRect & grect) +{ + xmin=grect.xmin; ymin=grect.ymin; + xmax=grect.xmax; ymax=grect.ymax; + initialize(); +} + +bool +GMapOval::gma_is_point_inside(const int x, const int y) const +{ + return + sqrt((double)((x-xf1)*(x-xf1)+(y-yf1)*(y-yf1))) + + sqrt((double)((x-xf2)*(x-xf2)+(y-yf2)*(y-yf2))) <= 2*rmax; +} + +char const * const +GMapOval::gma_check_object(void) const +{ + return (border_type!=NO_BORDER && + border_type!=SOLID_BORDER && + border_type!=XOR_BORDER)?error_oval_border: + ((hilite_color!=0xffffffff) ? error_oval_hilite:""); +} + +void +GMapOval::initialize(void) +{ + int xc=(xmax+xmin)/2; + int yc=(ymax+ymin)/2; + int f; + + a=(xmax-xmin)/2; + b=(ymax-ymin)/2; + if (a>b) + { + rmin=b; rmax=a; + f=(int) sqrt((double)(rmax*rmax-rmin*rmin)); + xf1=xc+f; xf2=xc-f; yf1=yf2=yc; + } else + { + rmin=a; rmax=b; + f=(int) sqrt((double)(rmax*rmax-rmin*rmin)); + yf1=yc+f; yf2=yc-f; xf1=xf2=xc; + } +} + +GMapOval::GMapOval(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin), + xmax(rect.xmax), ymax(rect.ymax) +{ + initialize(); +} + +GUTF8String +GMapOval::gma_print(void) +{ + GUTF8String buffer; + return buffer.format("(%s %d %d %d %d) ", + OVAL_TAG, xmin, ymin, xmax-xmin, ymax-ymin); +} + +void +GMapOval::map(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.map(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); + initialize(); +} + +void +GMapOval::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.unmap(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); + initialize(); +} + +GMapArea::GMapArea(void) : target("_self"), border_type(NO_BORDER), + border_always_visible(false), border_color(0xff), border_width(1), + hilite_color(0xffffffff), bounds_initialized(0) {} + +GMapRect::GMapRect(void) : xmin(0), ymin(0), xmax(0), ymax(0) {} + +GMapRect::GMapRect(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin), + xmax(rect.xmax), ymax(rect.ymax) {} + +GMapRect & +GMapRect::operator=(const GRect & rect) +{ + xmin=rect.xmin; + xmax=rect.xmax; + ymin=rect.ymin; + ymax=rect.ymax; + return *this; +} + +void +GMapRect::gma_move(int dx, int dy) +{ + xmin+=dx; + xmax+=dx; + ymin+=dy; + ymax+=dy; +} + +bool +GMapRect::gma_is_point_inside(const int x, const int y) const +{ + return (x>=xmin)&&(x<xmax)&&(y>=ymin)&&(y<ymax); +} + +GP<GMapArea> +GMapRect::get_copy(void) const { return new GMapRect(*this); } + +GMapPoly::GMapPoly(void) : points(0), sides(0) {} + +void +GMapPoly::move_vertex(int i, int x, int y) +{ + xx[i]=x; yy[i]=y; + clear_bounds(); +} + +GP<GMapArea> +GMapPoly::get_copy(void) const { return new GMapPoly(*this); } + +GMapOval::GMapOval(void) : xmin(0), ymin(0), xmax(0), ymax(0) {} + +void +GMapOval::gma_move(int dx, int dy) +{ + xmin+=dx; xmax+=dx; ymin+=dy; ymax+=dy; + xf1+=dx; yf1+=dy; xf2+=dx; yf2+=dy; +} + +GP<GMapArea> +GMapOval::get_copy(void) const +{ + return new GMapOval(*this); +} + +static GUTF8String +GMapArea2xmltag(const GMapArea &area,const GUTF8String &coords) +{ + GUTF8String retval("<AREA coords=\"" + +coords+"\" shape=\""+area.get_shape_name()+"\" " + +"alt=\""+area.comment.toEscaped()+"\" "); + if(area.url.length()) + { + retval+="href=\""+area.url+"\" "; + }else + { + retval+="nohref=\"nohref\" "; + } + if(area.target.length()) + { + retval+="target=\""+area.target.toEscaped()+"\" "; + } + // highlight + if( area.hilite_color != GMapArea::NO_HILITE && + area.hilite_color != GMapArea::XOR_HILITE ) + { + retval+=GUTF8String().format( "highlight=\"#%06X\" ", area.hilite_color ); + } + const char *b_type="none"; + switch( area.border_type ) + { + case GMapArea::NO_BORDER: + b_type = "none"; + break; + case GMapArea::XOR_BORDER: + b_type = "xor"; + break; + case GMapArea::SOLID_BORDER: + b_type = "solid"; + break; + case GMapArea::SHADOW_IN_BORDER: + b_type = "shadowin"; + break; + case GMapArea::SHADOW_OUT_BORDER: + b_type = "shadowout"; + break; + case GMapArea::SHADOW_EIN_BORDER: + b_type = "etchedin"; + break; + case GMapArea::SHADOW_EOUT_BORDER: + b_type = "etchedout"; + break; + } + retval=retval+"bordertype=\""+b_type+"\" "; + if( area.border_type != GMapArea::NO_BORDER) + { + retval+="bordercolor=\""+GUTF8String().format("#%06X",area.border_color) + +"\" border=\""+GUTF8String(area.border_width)+"\" "; + } + if(area.border_always_visible ) + retval=retval+"visible=\"visible\" "; + return retval+"/>\n"; +} + +GUTF8String +GMapRect::get_xmltag(const int height) const +{ + return GMapArea2xmltag( *this, GUTF8String(get_xmin()) + +","+GUTF8String(height-1-get_ymax()) + +","+GUTF8String(get_xmax()) + +","+GUTF8String(height-1-get_ymin())); +#if 0 + GUTF8String retval; + return retval; +#endif +} + +GUTF8String +GMapOval::get_xmltag(const int height) const +{ + return GMapArea2xmltag( *this, GUTF8String(get_xmin()) + +","+GUTF8String(height-1-get_ymax()) + +","+GUTF8String(get_xmax()) + +","+GUTF8String(height-1-get_ymin())); +#if 0 + GUTF8String retval; + return retval; +#endif +} + +GUTF8String +GMapPoly::get_xmltag(const int height) const +{ + GList<int> CoordList; + get_coords(CoordList); + GPosition pos=CoordList; + GUTF8String retval; + if(pos) + { + GUTF8String coords(CoordList[pos]); + while(++pos) + { + coords+=","+GUTF8String(height-1-CoordList[pos]); + if(! ++pos) + break; + coords+=","+GUTF8String(CoordList[pos]); + } + retval=GMapArea2xmltag( *this, coords); + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GMapAreas.h b/kviewshell/plugins/djvu/libdjvu/GMapAreas.h new file mode 100644 index 00000000..251427ed --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GMapAreas.h @@ -0,0 +1,565 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GMapAreas.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GMAPAREAS_H +#define _GMAPAREAS_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GSmartPointer.h" +#include "GContainer.h" +#include "GString.h" +#include "GRect.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name GMapAreas.h + + Files #"GMapAreas.h"# and #"GMapAreas.cpp"# implement base objects + used by the plugin to display and manage hyperlinks and highlighted + areas inside a \Ref{DjVuImage} page. + + The currently supported areas can be rectangular (\Ref{GMapRect}), + elliptical (\Ref{GMapOval}) and polygonal (\Ref{GMapPoly}). Every + map area besides the definition of its shape contains information + about display style and optional {\bf URL}, which it may refer to. + If this {\bf URL} is not empty then the map area will work like a + hyperlink. + + The classes also implement some useful functions to ease geometry + manipulations + + @memo Definition of base map area classes + @author Andrei Erofeev <eaf@geocities.com> + @version + #$Id: GMapAreas.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +// ---------- GMAPAREA --------- + +/** This is the base object for all map areas. It defines some standard + interface to access the geometrical properties of the areas and + describes the area itsef: + \begin{itemize} + \item #url# If the optional #URL# is specified, the map area will + also work as a hyperlink meaning that if you click it with + your mouse pointer, the browser will be advised to load + the page referenced by the #URL#. + \item #target# Defines where the specified #URL# should be loaded + \item #comment# This is a string displayed in a status line or in + a popup window when the mouse pointer moves over the hyperlink + area + \item #border_type#, #border_color# and #border_width# describes + how the area border should be drawn + \item #area_color# describes how the area should be highlighted. + \end{itemize} + + The map areas can be displayed using two different techniques, which + can be combined together: + \begin{itemize} + \item Visible border. The border of a map area can be drawn in several + different ways (like #XOR_BORDER# or #SHADOW_IN_BORDER#). + It can be made always visible, or appearing only when the + mouse pointer moves over the map area. + \item Highlighted contents. Contents of rectangular map areas can + also be highlighted with some given color. + \end{itemize} +*/ + +class GMapArea : public GPEnabled +{ +protected: + GMapArea(void); +public: +// // Default creator. +// static GP<GMapArea> create(void) {return new GMapArea();} + + /// Virtual destructor. + virtual ~GMapArea(); + + static const char MAPAREA_TAG []; + static const char RECT_TAG []; + static const char POLY_TAG []; + static const char OVAL_TAG []; + static const char NO_BORDER_TAG []; + static const char XOR_BORDER_TAG []; + static const char SOLID_BORDER_TAG []; + static const char SHADOW_IN_BORDER_TAG []; + static const char SHADOW_OUT_BORDER_TAG []; + static const char SHADOW_EIN_BORDER_TAG []; + static const char SHADOW_EOUT_BORDER_TAG []; + static const char BORDER_AVIS_TAG []; + static const char HILITE_TAG []; + static const char URL_TAG []; + static const char TARGET_SELF []; + + enum BorderType { NO_BORDER=0, XOR_BORDER=1, SOLID_BORDER=2, + SHADOW_IN_BORDER=3, SHADOW_OUT_BORDER=4, + SHADOW_EIN_BORDER=5, SHADOW_EOUT_BORDER=6 }; + + enum Special_Hilite_Color{ NO_HILITE=0xFFFFFFFF, XOR_HILITE=0xFF000000}; + + // Enumeration for reporting the type of map area. "MapUnknown" is reported + // for objects of type GMapArea (there shouldn't be any). + enum MapAreaType { UNKNOWN, RECT, OVAL, POLY }; + + /** Optional URL which this map area can be associated with. + If it's not empty then clicking this map area with the mouse + will make the browser load the HTML page referenced by + this #url#. Note: This may also be a relative URL, so the + GURL class is not used. */ + GUTF8String url; + /** The target for the #URL#. Standard targets are: + \begin{itemize} + \item #_blank# - Load the link in a new blank window + \item #_self# - Load the link into the plugin window + \item #_top# - Load the link into the top-level frame + \end{itemize} */ + GUTF8String target; + /** Comment (displayed in a status line or as a popup hint when + the mouse pointer moves over the map area */ + GUTF8String comment; + /** Border type. Defines how the map area border should be drawn + \begin{itemize} + \item #NO_BORDER# - No border drawn + \item #XOR_BORDER# - The border is drawn using XOR method. + \item #SOLID_BORDER# - The border is drawn as a solid line + of a given color. + \item #SHADOW_IN_BORDER# - Supported for \Ref{GMapRect} only. + The map area area looks as if it was "pushed-in". + \item #SHADOW_OUT_BORDER# - The opposite of #SHADOW_OUT_BORDER# + \item #SHADOW_EIN_BORDER# - Also for \Ref{GMapRect} only. + Is translated as "shadow etched in" + \item #SHADOW_EOUT_BORDER# - The opposite of #SHADOW_EIN_BORDER#. + \end{itemize} */ + BorderType border_type; + /** If #TRUE#, the border will be made always visible. Otherwise + it will be drawn when the mouse moves over the map area. */ + bool border_always_visible; + /// Border color (when relevant) in #0x00RRGGBB# format + unsigned long int border_color; + /// Border width in pixels + int border_width; + /** Specified a color for highlighting the internal area of the map + area. Will work with rectangular map areas only. The color is + specified in \#00RRGGBB format. A special value of \#FFFFFFFF disables + highlighting and \#FF000000 is for XOR highlighting. */ + unsigned long int hilite_color; + + /// Returns 1 if the given point is inside the hyperlink area + bool is_point_inside(int x, int y) const; + + /// Returns xmin of the bounding rectangle + int get_xmin(void) const; + /// Returns ymin of the bounding rectangle + int get_ymin(void) const; + /** Returns xmax of the bounding rectangle. In other words, if #X# is + a coordinate of the last point in the right direction, the + function will return #X+1# */ + int get_xmax(void) const; + /** Returns xmax of the bounding rectangle. In other words, if #Y# is + a coordinate of the last point in the top direction, the + function will return #Y+1# */ + int get_ymax(void) const; + /// Returns the hyperlink bounding rectangle + GRect get_bound_rect(void) const; + /** Moves the hyperlink along the given vector. Is used by the + hyperlinks editor. */ + void move(int dx, int dy); + /** Resizes the hyperlink to fit new bounding rectangle while + keeping the (xmin, ymin) points at rest. */ + void resize(int new_width, int new_height); + /** Transforms the hyperlink to be within the specified rectangle */ + void transform(const GRect & grect); + /** Checks if the object is OK. Especially useful with \Ref{GMapPoly} + where edges may intersect. If there is a problem it returns a + string describing it. */ + char const * const check_object(void); + /** Stores the contents of the hyperlink object in a lisp-like format + for saving into #ANTa# chunk (see \Ref{DjVuAnno}) */ + GUTF8String print(void); + + virtual GUTF8String get_xmltag(const int height) const=0; + + /// Virtual function returning the shape type. + virtual MapAreaType const get_shape_type( void ) const { return UNKNOWN; }; + /// Virtual function returning the shape name. + virtual char const * const get_shape_name(void) const=0; + /// Virtual function generating a copy of this object + virtual GP<GMapArea> get_copy(void) const=0; + /// Virtual function generating a list of defining coordinates + /// (default are the opposite corners of the enclosing rectangle) + virtual void get_coords( GList<int> & CoordList ) const; + /// Virtual function maps maparea from one area to another using mapper + virtual void map(GRectMapper &mapper)=0; + /// Virtual function unmaps maparea from one area to another using mapper + virtual void unmap(GRectMapper &mapper)=0; + +protected: + virtual int gma_get_xmin(void) const=0; + virtual int gma_get_ymin(void) const=0; + virtual int gma_get_xmax(void) const=0; + virtual int gma_get_ymax(void) const=0; + virtual void gma_move(int dx, int dy)=0; + virtual void gma_resize(int new_width, int new_height)=0; + virtual void gma_transform(const GRect & grect)=0; + virtual bool gma_is_point_inside(const int x, const int y) const=0; + virtual char const * const gma_check_object(void) const=0; + virtual GUTF8String gma_print(void)=0; + + void clear_bounds(void) { bounds_initialized=0; } +private: + int xmin, xmax, ymin, ymax; + bool bounds_initialized; + + void initialize_bounds(void); +}; + +// ---------- GMAPRECT --------- + +/** Implements rectangular map areas. This is the only kind of map areas + supporting #SHADOW_IN_BORDER#, #SHADOW_OUT_BORDER#, #SHADOW_EIN_BORDER# + and #SHADOW_EOUT_BORDER# types of border and area highlighting. */ + +class GMapRect: public GMapArea +{ +protected: + GMapRect(void); + GMapRect(const GRect & rect); +public: + /// Default creator. + static GP<GMapRect> create(void) {return new GMapRect();} + /// Create with the specified GRect. + static GP<GMapRect> create(const GRect &rect) {return new GMapRect(rect);} + + virtual ~GMapRect(); + + /// Returns the width of the rectangle + int get_width(void) const { return xmax-xmin; } + /// Returns the height of the rectangle + int get_height(void) const { return ymax-ymin; } + + /// Changes the #GMapRect#'s geometry + GMapRect & operator=(const GRect & rect); + + /// Returns \Ref{GRect} describing the map area's rectangle + operator GRect(void); + + virtual GUTF8String get_xmltag(const int height) const; + /// Returns MapRect + virtual MapAreaType const get_shape_type( void ) const { return RECT; }; + /// Returns #"rect"# + virtual char const * const get_shape_name(void) const; + /// Returns a copy of the rectangle + virtual GP<GMapArea> get_copy(void) const; + /// Virtual function maps rectangle from one area to another using mapper + virtual void map(GRectMapper &mapper); + /// Virtual function unmaps rectangle from one area to another using mapper + virtual void unmap(GRectMapper &mapper); +protected: + int xmin, ymin, xmax, ymax; + virtual int gma_get_xmin(void) const; + virtual int gma_get_ymin(void) const; + virtual int gma_get_xmax(void) const; + virtual int gma_get_ymax(void) const; + virtual void gma_move(int dx, int dy); + virtual void gma_resize(int new_width, int new_height); + virtual void gma_transform(const GRect & grect); + virtual bool gma_is_point_inside(const int x, const int y) const; + virtual char const * const gma_check_object(void) const; + virtual GUTF8String gma_print(void); +}; + +// ---------- GMAPPOLY --------- + +/** Implements polygonal map areas. The only supported types of border + are #NO_BORDER#, #XOR_BORDER# and #SOLID_BORDER#. Its contents can not + be highlighted either. It's worth mentioning here that despite its + name the polygon may be open, which basically makes it a broken line. + This very specific mode is used by the hyperlink editor when creating + the polygonal hyperlink. */ + +class GMapPoly : public GMapArea +{ +protected: + GMapPoly(void); + GMapPoly(const int * xx, const int * yy, int points, bool open=false); +public: + /// Default creator + static GP<GMapPoly> create(void) {return new GMapPoly();} + + /// Create from specified coordinates. + static GP<GMapPoly> create( + const int xx[], const int yy[], const int points, const bool open=false) + {return new GMapPoly(xx,yy,points,open);} + + /// Virtual destructor. + virtual ~GMapPoly(); + + /// Returns 1 if side #side# crosses the specified rectangle #rect#. + bool does_side_cross_rect(const GRect & grect, int side); + + /// Returns the number of vertices in the polygon + int get_points_num(void) const; + + /// Returns the number sides in the polygon + int get_sides_num(void) const; + + /// Returns x coordinate of vertex number #i# + int get_x(int i) const; + + /// Returns y coordinate of vertex number #i# + int get_y(int i) const; + + /// Moves vertex #i# to position (#x#, #y#) + void move_vertex(int i, int x, int y); + + /// Adds a new vertex and returns number of vertices in the polygon + int add_vertex(int x, int y); + + /// Closes the polygon if it is not closed + void close_poly(); + /// Optimizes the polygon + void optimize_data(void); + /// Checks validity of the polygon + char const * const check_data(void); + + virtual GUTF8String get_xmltag(const int height) const; + /// Returns MapPoly + virtual MapAreaType const get_shape_type( void ) const { return POLY; }; + /// Returns #"poly"# all the time + virtual char const * const get_shape_name(void) const; + /// Returns a copy of the polygon + virtual GP<GMapArea> get_copy(void) const; + /// Virtual function generating a list of defining coordinates + void get_coords( GList<int> & CoordList ) const; + /// Virtual function maps polygon from one area to another using mapper + virtual void map(GRectMapper &mapper); + /// Virtual function unmaps polygon from one area to another using mapper + virtual void unmap(GRectMapper &mapper); +protected: + virtual int gma_get_xmin(void) const; + virtual int gma_get_ymin(void) const; + virtual int gma_get_xmax(void) const; + virtual int gma_get_ymax(void) const; + virtual void gma_move(int dx, int dy); + virtual void gma_resize(int new_width, int new_height); + virtual void gma_transform(const GRect & grect); + virtual bool gma_is_point_inside(const int x, const int y) const; + virtual char const * const gma_check_object(void) const; + virtual GUTF8String gma_print(void); +private: + bool open; + int points, sides; + GTArray<int> xx, yy; + static int sign(int x); + static bool is_projection_on_segment(int x, int y, int x1, int y1, int x2, int y2); + static bool do_segments_intersect(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22); + static bool are_segments_parallel(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22); +}; + +// ---------- GMAPOVAL --------- + +/** Implements elliptical map areas. The only supported types of border + are #NO_BORDER#, #XOR_BORDER# and #SOLID_BORDER#. Its contents can not + be highlighted either. */ + +class GMapOval: public GMapArea +{ +protected: + GMapOval(void); + GMapOval(const GRect & rect); +public: + /// Default creator. + static GP<GMapOval> create(void) {return new GMapOval();} + + /// Create from the specified GRect. + static GP<GMapOval> create(const GRect &rect) {return new GMapOval(rect);} + + /// Virtual destructor. + virtual ~GMapOval(); + + /// Returns (xmax-xmin)/2 + int get_a(void) const; + /// Returns (ymax-ymin)/2 + int get_b(void) const; + /// Returns the lesser of \Ref{get_a}() and \Ref{get_b}() + int get_rmin(void) const; + /// Returns the greater of \Ref{get_a}() and \Ref{get_b}() + int get_rmax(void) const; + + virtual GUTF8String get_xmltag(const int height) const; + /// Returns MapOval + virtual MapAreaType const get_shape_type( void ) const { return OVAL; }; + /// Returns #"oval"# + virtual char const * const get_shape_name(void) const; + /// Returns a copy of the oval + virtual GP<GMapArea> get_copy(void) const; + /// Virtual function maps oval from one area to another using mapper + virtual void map(GRectMapper &mapper); + /// Virtual function unmaps oval from one area to another using mapper + virtual void unmap(GRectMapper &mapper); +protected: + virtual int gma_get_xmin(void) const; + virtual int gma_get_ymin(void) const; + virtual int gma_get_xmax(void) const; + virtual int gma_get_ymax(void) const; + virtual void gma_move(int dx, int dy); + virtual void gma_resize(int new_width, int new_height); + virtual void gma_transform(const GRect & grect); + virtual bool gma_is_point_inside(const int x, const int y) const; + virtual char const * const gma_check_object(void) const; + virtual GUTF8String gma_print(void); +private: + int rmax, rmin; + int a, b; + int xf1, yf1, xf2, yf2; + int xmin, ymin, xmax, ymax; + + void initialize(void); +}; + +inline +GMapRect::operator GRect(void) +{ + return GRect(xmin, ymin, xmax-xmin, ymax-ymin); +} + +inline int +GMapRect::gma_get_xmin(void) const { return xmin; } + +inline int +GMapRect::gma_get_ymin(void) const { return ymin; } + +inline int +GMapRect::gma_get_xmax(void) const { return xmax; } + +inline int +GMapRect::gma_get_ymax(void) const { return ymax; } + +inline char const * const +GMapRect::gma_check_object(void) const{ return ""; } + +inline char const * const +GMapRect::get_shape_name(void) const { return RECT_TAG; } + +inline int +GMapPoly::get_points_num(void) const { return points; } + +inline int +GMapPoly::get_sides_num(void) const { return sides; } + +inline int +GMapPoly::get_x(int i) const { return xx[i]; } + +inline int +GMapPoly::get_y(int i) const { return yy[i]; } + +inline char const * const +GMapPoly::get_shape_name(void) const { return POLY_TAG; } + +inline int +GMapOval::get_a(void) const { return a; } + +inline int +GMapOval::get_b(void) const { return b; } + +inline int +GMapOval::get_rmin(void) const { return rmin; } + +inline int +GMapOval::get_rmax(void) const { return rmax; } + +inline int +GMapOval::gma_get_xmin(void) const { return xmin; } + +inline int +GMapOval::gma_get_ymin(void) const { return ymin; } + +inline int +GMapOval::gma_get_xmax(void) const { return xmax; } + +inline int +GMapOval::gma_get_ymax(void) const { return ymax; } + +inline char const * const +GMapOval::get_shape_name(void) const { return OVAL_TAG; } + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GOS.cpp b/kviewshell/plugins/djvu/libdjvu/GOS.cpp new file mode 100644 index 00000000..35aa8997 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GOS.cpp @@ -0,0 +1,370 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GOS.cpp,v 1.12 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GException.h" +#include "GThreads.h" +#include "GOS.h" +#include "GURL.h" + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <math.h> +#include <string.h> + +#ifdef WIN32 +# include <atlbase.h> +# include <windows.h> +# include <direct.h> +#endif + +#ifdef OS2 +# define INCL_DOS +# include <os2.h> +#endif + +#if defined(UNIX) || defined(OS2) +# include <errno.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/time.h> +# include <fcntl.h> +# include <pwd.h> +# include <stdio.h> +# include <unistd.h> +#endif + +#ifdef macintosh +# include <unix.h> +# include <errno.h> +# include <unistd.h> +#endif + +// -- TRUE FALSE +#undef TRUE +#undef FALSE +#define TRUE 1 +#define FALSE 0 + +// -- MAXPATHLEN +#ifndef MAXPATHLEN +# ifdef _MAX_PATH +# define MAXPATHLEN _MAX_PATH +# else +# define MAXPATHLEN 1024 +# endif +#else +# if ( MAXPATHLEN < 1024 ) +# undef MAXPATHLEN +# define MAXPATHLEN 1024 +# endif +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#if defined(AUTOCONF) && !defined(HAVE_STRERROR) +# define NEED_STRERROR +#elif defined(sun) && !defined(__svr4__) +# define NEED_STRERROR +#elif defined(REIMPLEMENT_STRERROR) +# define NEED_STRERROR +#endif +#ifdef NEED_STRERROR +char * +strerror(int errno) +{ + extern int sys_nerr; + extern char *sys_errlist[]; + if (errno>0 && errno<sys_nerr) + return sys_errlist[errno]; + return "unknown stdio error"; +} +#endif + + +static const char slash='/'; +static const char percent='%'; +static const char backslash='\\'; +static const char colon=':'; +static const char dot='.'; +static const char nillchar=0; + + +// ----------------------------------------- +// Functions for dealing with filenames +// ----------------------------------------- + +static inline int +finddirsep(const GUTF8String &fname) +{ +#if defined(UNIX) + return fname.rsearch('/',0); +#elif defined(WIN32) || defined(OS2) + return fname.rcontains("\\/",0); +#elif defined(macintosh) + return fname.rcontains(":/",0); +#else +#error "Define something here for your operating system" +#endif +} + + +// basename(filename[, suffix]) +// -- returns the last component of filename and removes suffix +// when present. works like /bin/basename. +GUTF8String +GOS::basename(const GUTF8String &gfname, const char *suffix) +{ + if(!gfname.length()) + return gfname; + + const char *fname=gfname; +#if defined(WIN32) || defined(OS2) + // Special cases + if (fname[1] == colon) + { + if(!fname[2]) + { + return gfname; + } + if (!fname[3] && (fname[2]== slash || fname[2]== backslash)) + { + char string_buffer[4]; + string_buffer[0] = fname[0]; + string_buffer[1] = colon; + string_buffer[2] = backslash; + string_buffer[3] = 0; + return string_buffer; + } + } +#endif + + + // Allocate buffer + GUTF8String retval(gfname,finddirsep(gfname)+1,(unsigned int)(-1)); + fname=retval; + + // Process suffix + if (suffix) + { + if (suffix[0]== dot ) + suffix ++; + if (suffix[0]) + { + const GUTF8String gsuffix(suffix); + const int sl = gsuffix.length(); + const char *s = fname + strlen(fname); + if (s > fname + sl) + { + s = s - (sl + 1); + if(*s == dot && (GUTF8String(s+1).downcase() == gsuffix.downcase())) + { + retval.setat((int)((size_t)s-(size_t)fname),0); + } + } + } + } + return retval; +} + + + +// errmsg -- +// -- A small helper function returning a +// stdio error message in a static buffer. + +static GNativeString +errmsg() +{ + GNativeString buffer; + const char *errname = strerror(errno); + buffer.format("%s (errno = %d)", errname, errno); + return buffer; +} + + + +// ----------------------------------------- +// Functions for measuring time +// ----------------------------------------- + +// ticks() -- +// -- returns the number of milliseconds elapsed since +// a system dependent date. +unsigned long +GOS::ticks() +{ +#if defined(UNIX) + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) + G_THROW(errmsg()); + return (unsigned long)( ((tv.tv_sec & 0xfffff)*1000) + + (tv.tv_usec/1000) ); +#elif defined(WIN32) + DWORD clk = GetTickCount(); + return (unsigned long)clk; +#elif defined(OS2) + ULONG clk = 0; + DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, (PVOID)&clk, sizeof(ULONG)); + return clk; +#elif defined(macintosh) + return (unsigned long)((double)TickCount()*16.66); +#else +# error "Define something here for your operating system" +#endif +} + +// sleep(int milliseconds) -- +// -- sleeps during the specified time (in milliseconds) +void +GOS::sleep(int milliseconds) +{ +#if defined(UNIX) + struct timeval tv; + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = (milliseconds - (tv.tv_sec * 1000)) * 1000; +# if defined(THREADMODEL) && (THREADMODEL==COTHREADS) + GThread::select(0, NULL, NULL, NULL, &tv); +# else + select(0, NULL, NULL, NULL, &tv); +# endif +#elif defined(WIN32) + Sleep(milliseconds); +#elif defined(OS2) + DosSleep(milliseconds); +#elif defined(macintosh) + unsigned long tick = ticks(), now; + while (1) { + now = ticks(); + if ((tick+milliseconds) < now) + break; + GThread::yield(); + } +#endif +} + + +// ----------------------------------------- +// Testing +// ----------------------------------------- + +// cwd([dirname]) +// -- changes directory to dirname (when specified). +// returns the full path name of the current directory. +GUTF8String +GOS::cwd(const GUTF8String &dirname) +{ +#if defined(UNIX) || defined(macintosh) || defined(OS2) + if (dirname.length() && chdir(dirname.getUTF82Native())==-1)//MBCS cvt + G_THROW(errmsg()); + char *string_buffer; + GPBuffer<char> gstring_buffer(string_buffer,MAXPATHLEN+1); + char *result = getcwd(string_buffer,MAXPATHLEN); + if (!result) + G_THROW(errmsg()); + return GNativeString(result).getNative2UTF8();//MBCS cvt +#elif defined (WIN32) + char drv[2]; + if (dirname.length() && _chdir(dirname.getUTF82Native())==-1)//MBCS cvt + G_THROW(errmsg()); + drv[0]= dot ; drv[1]=0; + char *string_buffer; + GPBuffer<char> gstring_buffer(string_buffer,MAXPATHLEN+1); + char *result = getcwd(string_buffer,MAXPATHLEN); + GetFullPathName(drv, MAXPATHLEN, string_buffer, &result); + return GNativeString(string_buffer).getNative2UTF8();//MBCS cvt +#else +#error "Define something here for your operating system" +#endif +} + +GUTF8String +GOS::getenv(const GUTF8String &name) +{ + GUTF8String retval; + if(name.length()) + { + const char *env=::getenv(name.getUTF82Native()); + if(env) + { + retval=GNativeString(env); + } + } + return retval; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GOS.h b/kviewshell/plugins/djvu/libdjvu/GOS.h new file mode 100644 index 00000000..3e57fb40 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GOS.h @@ -0,0 +1,161 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GOS.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GOS_H_ +#define _GOS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GOS.h + Files #"GOS.h"# and #"GOS.cpp"# implement operating system + dependent functions with a unified interface. All these functions + are implemented as static member of class \Ref{GOS}. + Functions are provided for testing the presence of a file or a directory + (\Ref{GOS::is_file}, \Ref{GOS::is_dir}), for manipulating file and directory names + (\Ref{GOS::dirname}, \Ref{GOS::basename}, \Ref{GOS::expand_name}, + for obtaining and changing the current directory (\Ref{GOS::cwd}), + for converting between file names and urls (\Ref{GOS::filename_to_url}, + \Ref{GOS::url_to_filename}), and for counting time (\Ref{GOS::ticks}, + \Ref{GOS::sleep}). + + @memo + Operating System dependent functions. + @author + L\'eon Bottou <leonb@research.att.com> -- Initial implementation + @version + #$Id: GOS.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# +*/ +//@{ + +#include "DjVuGlobal.h" +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class GURL; + +/** Operating System dependent functions. */ +class GOS +{ + public: + // ----------------------------------------- + // Functions for dealing with filenames + // ----------------------------------------- + + /** Returns the last component of file name #filename#. If the filename + suffix matches argument #suffix#, the filename suffix is removed. This + function works like the unix command #/bin/basename#, but also supports + the naming conventions of other operating systems. */ + static GUTF8String basename(const GUTF8String &filename, const char *suffix=0); + + /** Sets and returns the current working directory. + When argument #dirname# is specified, the current directory is changed + to #dirname#. This function always returns the fully qualified name + of the current directory. */ + static GUTF8String cwd(const GUTF8String &dirname=GUTF8String()); + + // ----------------------------------------- + // Functions for measuring time + // ----------------------------------------- + + /** Returns a number of elapsed milliseconds. This number counts elapsed + milliseconds since a operating system dependent date. This function is + useful for timing code. */ + static unsigned long ticks(); + + /** Sleeps during the specified time expressed in milliseconds. + Other threads can run while the calling thread sleeps. */ + static void sleep(int milliseconds); + + /// Read the named variable from the environment, and converts it to UTF8. + static GUTF8String getenv(const GUTF8String &name); + +#if 0 + // ------------------------------------------- + // Functions for converting filenames and urls + // ------------------------------------------- + /** Encodes all reserved characters, so that the #filename# can be + used inside a URL. Every reserved character is encoded using escape + sequence in the form of #%XX#. The legal characters are alphanumeric and + #$-_.+!*'(),:#. + Use \Ref{decode_reserved}() to convert the URL back to the filename. */ +// static GString encode_reserved(const char * filename); + static GString encode_mbcs_reserved(const char * filename);/*MBCS*/ +#endif + +}; + + +//@} +// ------------ + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp b/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp new file mode 100644 index 00000000..81c1dd71 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp @@ -0,0 +1,1676 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GPixmap.cpp,v 1.12 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// -- Implements class PIXMAP +// Author: Leon Bottou 07/1997 + + + +#include "GPixmap.h" + +#include "GString.h" +#include "GException.h" +#include "ByteStream.h" +#include "GRect.h" +#include "GBitmap.h" +#include "GThreads.h" +#include "Arrays.h" +#include "JPEGDecoder.h" +#include <stdlib.h> +#include <math.h> +#include <assert.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +////////////////////////////////////////////////// +// ------- predefined colors +////////////////////////////////////////////////// + + +const GPixel GPixel::WHITE = { 255, 255, 255 }; +const GPixel GPixel::BLACK = { 0, 0, 0 }; +const GPixel GPixel::BLUE = { 255, 0, 0 }; +const GPixel GPixel::GREEN = { 0, 255, 0 }; +const GPixel GPixel::RED = { 0, 0, 255 }; + + +////////////////////////////////////////////////// +// ----- utilities +////////////////////////////////////////////////// + + +static const GPixel * +new_gray_ramp(int grays,GPixel *ramp) +{ + int color = 0xff0000; + int decrement = color / (grays-1); + for (int i=0; i<grays; i++) + { + int level = color >> 16; + ramp[i].b = level; + ramp[i].g = level; + ramp[i].r = level; + color -= decrement; + } + return ramp; +} + + +static inline int +mini(int x, int y) +{ + return (x < y ? x : y); +} + + +static inline int +maxi(int x, int y) +{ + return (x > y ? x : y); +} + + +static inline void +euclidian_ratio(int a, int b, int &q, int &r) +{ + q = a / b; + r = a - b*q; + if (r < 0) + { + q -= 1; + r += b; + } +} + + +////////////////////////////////////////////////// +// global lock used by some rare operations +////////////////////////////////////////////////// + +static GMonitor &pixmap_monitor() { + static GMonitor xpixmap_monitor; + return xpixmap_monitor; +} + + +////////////////////////////////////////////////// +// constructors and destructors +////////////////////////////////////////////////// + + +GPixmap::~GPixmap() +{ + delete [] pixels_data; +} + +void +GPixmap::destroy(void) +{ + delete [] pixels_data; + pixels = pixels_data = 0; +} + +GPixmap::GPixmap() +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ +} + +GPixmap::GPixmap(int nrows, int ncolumns, const GPixel *filler) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(nrows, ncolumns, filler); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(ByteStream &bs) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(bs); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GBitmap &ref) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref, 0); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GBitmap &ref, const GRect &rect) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref, rect, 0); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GPixmap &ref) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GPixmap &ref, const GRect &rect) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref, rect); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + + + +////////////////////////////////////////////////// +// Initialization +////////////////////////////////////////////////// + + +void +GPixmap::init(int arows, int acolumns, const GPixel *filler) +{ + destroy(); + nrows = arows; + ncolumns = acolumns; + nrowsize = acolumns; + int npix = nrows * nrowsize; + if (npix > 0) + { + pixels = pixels_data = new GPixel[npix]; + if (filler) + { + while (--npix>=0) + pixels_data[npix] = *filler; + } + } +} + + +void +GPixmap::init(const GBitmap &ref, const GPixel *userramp) +{ + init(ref.rows(), ref.columns(), 0); + GPixel *xramp; + GPBuffer<GPixel> gxramp(xramp); + if (nrows>0 && ncolumns>0) + { + // Create pixel ramp + const GPixel *ramp = userramp; + if (!userramp) + { + gxramp.resize(256); + gxramp.clear(); + ramp = new_gray_ramp(ref.get_grays(),xramp); + } + // Copy pixels + for (int y=0; y<nrows; y++) + { + GPixel *dst = (*this)[y]; + const unsigned char *src = ref[y]; + for (int x=0; x<ncolumns; x++) + dst[x] = ramp[ src[x] ]; + } + // Free ramp +// if (!userramp) +// delete [] (GPixel*)ramp; + } +} + + +void +GPixmap::init(const GBitmap &ref, const GRect &rect, const GPixel *userramp) +{ + init(rect.height(), rect.width(), 0); + // compute destination rectangle + GRect rect2(0, 0, ref.columns(), ref.rows() ); + rect2.intersect(rect2, rect); + rect2.translate(-rect.xmin, -rect.ymin); + // copy bits + if (! rect2.isempty()) + { + GPixel *xramp; + GPBuffer<GPixel> gxramp(xramp); + // allocate ramp + const GPixel *ramp = userramp; + if (!userramp) + { + gxramp.resize(256); + gxramp.clear(); + ramp = new_gray_ramp(ref.get_grays(),xramp); + } + // copy pixels + for (int y=rect2.ymin; y<rect2.ymax; y++) + { + GPixel *dst = (*this)[y]; + const unsigned char *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = ramp[ src[x] ]; + } + // free ramp +// if (!userramp) +// delete [] (GPixel*) ramp; + } +} + + +void +GPixmap::init(const GPixmap &ref) +{ + init(ref.rows(), ref.columns(), 0); + if (nrows>0 && ncolumns>0) + { + for (int y=0; y<nrows; y++) + { + GPixel *dst = (*this)[y]; + const GPixel *src = ref[y]; + for (int x=0; x<ncolumns; x++) + dst[x] = src[x]; + } + } +} + + +void +GPixmap::init(const GPixmap &ref, const GRect &rect) +{ + init(rect.height(), rect.width(), 0); + // compute destination rectangle + GRect rect2(0, 0, ref.columns(), ref.rows() ); + rect2.intersect(rect2, rect); + rect2.translate(-rect.xmin, -rect.ymin); + // copy bits + if (! rect2.isempty()) + { + for (int y=rect2.ymin; y<rect2.ymax; y++) + { + GPixel *dst = (*this)[y]; + const GPixel *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = src[x]; + } + } +} + + +void +GPixmap::donate_data(GPixel *data, int w, int h) +{ + destroy(); + nrows = h; + ncolumns = w; + nrowsize = w; + pixels_data=pixels=data; +} + + +GPixel * +GPixmap::take_data(size_t &offset) +{ + GPixel *ret = pixels_data; + pixels_data = 0; + offset = 0; + return ret; +} + + + +////////////////////////////////////////////////// +// Save and load ppm files +////////////////////////////////////////////////// + + +static unsigned int +read_integer(char &c, ByteStream &bs) +{ + unsigned int x = 0; + // eat blank before integer + while (c==' ' || c=='\t' || c=='\r' || c=='\n' || c=='#') + { + if (c=='#') + do { } while (bs.read(&c,1) && c!='\n' && c!='\r'); + c = 0; + bs.read(&c, 1); + } + // check integer + if (c<'0' || c>'9') + G_THROW( ERR_MSG("GPixmap.no_int") ); + // eat integer + while (c>='0' && c<='9') + { + x = x*10 + c - '0'; + c = 0; + bs.read(&c, 1); + } + return x; +} + + +void +GPixmap::init(ByteStream &bs) +{ + // Read header + int raw = 0; + char magic[2]; + magic[0] = magic[1] = 0; + bs.readall((void*)magic, sizeof(magic)); + if (magic[0]=='P' && magic[1]=='3') + { + raw = 0; + }else if (magic[0]=='P' && magic[1]=='6') + { + raw = 1; + }else + { +#ifdef NEED_JPEG_DECODER + bs.seek(0L); + JPEGDecoder::decode(bs,*this); + return; +#else // NEED_JPEG_DECODER + + G_THROW( ERR_MSG("GPixmap.unk_PPM") ); +#endif // NEED_JPEG_DECODER + } + // Read image size + char lookahead = '\n'; + int acolumns = read_integer(lookahead, bs); + int arows = read_integer(lookahead, bs); + int maxval = read_integer(lookahead, bs); + if (maxval > 255) + G_THROW("Cannot read PPM with depth greater than 24 bits."); + init(arows, acolumns, 0); + // Read image data + if (raw) + { + GTArray<unsigned char> line(ncolumns*3); + for (int y=nrows-1; y>=0; y--) + { + GPixel *p = (*this)[y]; + unsigned char *rgb = &line[0]; + if ( bs.readall((void*)rgb, ncolumns*3) < (size_t)(ncolumns*3)) + G_THROW( ByteStream::EndOfFile ); + for (int x=0; x<ncolumns; x+=1, rgb+=3) + { + p[x].r = rgb[0]; + p[x].g = rgb[1]; + p[x].b = rgb[2]; + } + } + } + else + { + for (int y=nrows-1; y>=0; y--) + { + GPixel *p = (*this)[y]; + for (int x=0; x<ncolumns; x++) + { + p[x].r = read_integer(lookahead, bs); + p[x].g = read_integer(lookahead, bs); + p[x].b = read_integer(lookahead, bs); + } + } + } + // Process small values of maxval + if (maxval>0 && maxval<255) + { + char table[256]; + for (int i=0; i<256; i++) + table[i] = (i<maxval ? (255*i + maxval/2) / maxval : 255); + for (int y=0; y<nrows; y++) + { + GPixel *p = (*this)[y]; + for (int x=0; x<ncolumns; x++) + { + p[x].r = table[p[x].r]; + p[x].g = table[p[x].g]; + p[x].b = table[p[x].b]; + } + } + } +} + + +void +GPixmap::save_ppm(ByteStream &bs, int raw) const +{ + GUTF8String head; + head.format("P%c\n%d %d\n255\n", (raw ? '6' : '3'), ncolumns, nrows); + bs.writall((void*)(const char *)head, head.length()); + if (raw) + { + int rowsize = ncolumns+ncolumns+ncolumns; + GTArray<unsigned char> xrgb(rowsize); + for (int y=nrows-1; y>=0; y--) + { + const GPixel *p = (*this)[y]; + unsigned char *d = xrgb; + for (int x=0; x<ncolumns; x++) + { + *d++ = p[x].r; + *d++ = p[x].g; + *d++ = p[x].b; + } + bs.writall((void*)(unsigned char*)xrgb, ncolumns * 3); + } + } + else + { + for (int y=nrows-1; y>=0; y--) + { + const GPixel *p = (*this)[y]; + unsigned char eol='\n'; + for (int x=0; x<ncolumns; ) + { + head.format("%d %d %d ", p[x].r, p[x].g, p[x].b); + bs.writall((void*)(const char *)head, head.length()); + x += 1; + if (x==ncolumns || (x&0x7)==0) + bs.write((void*)&eol, 1); + } + } + } +} + + + + +////////////////////////////////////////////////// +// Color correction +////////////////////////////////////////////////// + + +static void +color_correction_table(double gamma, unsigned char gtable[256] ) +{ + // Check argument + if (gamma<0.1 || gamma>10.0) + G_THROW( ERR_MSG("GPixmap.bad_param") ); + if (gamma<1.001 && gamma>0.999) + { + // Trivial correction + for (int i=0; i<256; i++) + gtable[i] = i; + } + else + { + // Must compute the correction + for (int i=0; i<256; i++) + { + double x = (double)(i)/255.0; +#ifdef BEZIERGAMMA + double t = ( sqrt(1.0+(gamma*gamma-1.0)*x) - 1.0 ) / (gamma - 1.0); + x = ( (1.0 - gamma)*t + 2.0 * gamma ) * t / (gamma + 1.0); +#else + x = pow(x, 1.0/gamma); +#endif + gtable[i] = (int) floor(255.0 * x + 0.5); + } + // Make sure that min and max values are exactly black or white + gtable[0] = 0; + gtable[255] = 255; + } +} + +static void +color_correction_table_cache(double gamma, unsigned char gtable[256] ) +{ + // Compute color correction table + if (gamma<1.001 && gamma>0.999) + { + color_correction_table(gamma, gtable); + } + else + { + static double lgamma = -1.0; + static unsigned char ctable[256]; + GMonitorLock lock(&pixmap_monitor()); + if (gamma != lgamma) + { + color_correction_table(gamma, ctable); + lgamma = gamma; + } + memcpy(gtable, ctable, 256*sizeof(unsigned char)); + } +} + +void +GPixmap::color_correct(double gamma_correction) +{ + // Trivial corrections + if (gamma_correction>0.999 && gamma_correction<1.001) + return; + // Compute correction table + unsigned char gtable[256]; + color_correction_table_cache(gamma_correction, gtable); + // Perform correction + for (int y=0; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = gtable[ pix->r ]; + pix->g = gtable[ pix->g ]; + pix->b = gtable[ pix->b ]; + } + } +} + + +void +GPixmap::color_correct(double gamma_correction, GPixel *pix, int npixels) +{ + // Trivial corrections + if (gamma_correction>0.999 && gamma_correction<1.001) + return; + // Compute correction table + unsigned char gtable[256]; + color_correction_table_cache(gamma_correction, gtable); + // Perform correction + while (--npixels>=0) + { + pix->r = gtable[pix->r]; + pix->g = gtable[pix->g]; + pix->b = gtable[pix->b]; + pix++; + } +} + + + +////////////////////////////////////////////////// +// Dithering +////////////////////////////////////////////////// + + +void +GPixmap::ordered_666_dither(int xmin, int ymin) +{ + static unsigned char quantize[256+0x33+0x33]; + static unsigned char *quant = quantize + 0x33; + static char dither_ok = 0; + static short dither[16][16] = + { + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } + }; + // Prepare tables + if (!dither_ok) + { + int i, j; + for (i=0; i<16; i++) + for (j=0; j<16; j++) + dither[i][j] = ((255 - 2*dither[i][j]) * 0x33) / 512; + j = -0x33; + for (i=0x19; i<256; i+=0x33) + while (j <= i) + quant[j++] = i-0x19; + assert(i-0x19 == 0xff); + while (j< 256+0x33) + quant[j++] = i-0x19; + dither_ok = 1; + } + // Go dithering + for (int y=0; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ]; + pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ]; + pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ]; + } + } +} + +void +GPixmap::ordered_32k_dither(int xmin, int ymin) +{ + static unsigned char quantize[256+8+8]; + static unsigned char *quant = quantize + 8; + static char dither_ok = 0; + static short dither[16][16] = + { + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } + }; + // Prepare tables + if (!dither_ok) + { + int i, j; + for (i=0; i<16; i++) + for (j=0; j<16; j++) + dither[i][j] = ((255 - 2*dither[i][j]) * 8) / 512; + j = -8; + for (i=3; i<256; i+=8) + while (j <= i) + quant[j++] = i; + while (j<256+8) + quant[j++] = 0xff; + dither_ok = 1; + } + // Go dithering + for (int y=0; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ]; + pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ]; + pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ]; + } + } +} + + +////////////////////////////////////////////////// +// Upsample Downsample +////////////////////////////////////////////////// + + +void +GPixmap::downsample(const GPixmap *src, int factor, const GRect *pdr) +{ + // check arguments + GRect rect(0, 0, (src->columns()+factor-1)/factor, (src->rows()+factor-1)/factor); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow1") ); + rect = *pdr; + } + + // precompute inverse map + static int invmap[256]; + static int invmapok = 0; + if (! invmapok) + { + invmapok = 1; + for (int i=1; i<(int)(sizeof(invmap)/sizeof(int)); i++) + invmap[i] = 0x10000 / i; + } + + // initialise pixmap + init(rect.height(), rect.width(), 0); + + // determine starting and ending points in source rectangle + int sy = rect.ymin * factor; + int sxz = rect.xmin * factor; + + + // loop over source rows + const GPixel *sptr = (*src)[sy]; + GPixel *dptr = (*this)[0]; + for (int y=0; y<nrows; y++) + { + int sx = sxz; + // loop over source columns + for (int x=0; x<ncolumns; x++) + { + int r=0, g=0, b=0, s=0; + // compute average bounds + const GPixel *ksptr = sptr; + int lsy = sy + factor; + if (lsy > (int)src->rows()) + lsy = (int)src->rows(); + int lsx = sx + factor; + if (lsx > (int)src->columns()) + lsx = (int)src->columns(); + // compute average + for (int rsy=sy; rsy<lsy; rsy++) + { + for (int rsx = sx; rsx<lsx; rsx++) + { + r += ksptr[rsx].r; + g += ksptr[rsx].g; + b += ksptr[rsx].b; + s += 1; + } + ksptr += src->rowsize(); + } + // set pixel color + if (s >= (int)(sizeof(invmap)/sizeof(int))) + { + dptr[x].r = r / s; + dptr[x].g = g / s; + dptr[x].b = b / s; + } + else + { + dptr[x].r = (r*invmap[s] + 0x8000) >> 16; + dptr[x].g = (g*invmap[s] + 0x8000) >> 16; + dptr[x].b = (b*invmap[s] + 0x8000) >> 16; + } + // next column + sx = sx + factor; + } + // next row + sy = sy + factor; + sptr = sptr + factor * src->rowsize(); + dptr = dptr + rowsize(); + } +} + +void +GPixmap::upsample(const GPixmap *src, int factor, const GRect *pdr) +{ + // check arguments + GRect rect(0, 0, src->columns()*factor, src->rows()*factor); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow2") ); + rect = *pdr; + } + // initialise pixmap + init(rect.height(), rect.width(), 0); + // compute starting point in source rectangle + int sy, sy1, sxz, sx1z; + euclidian_ratio(rect.ymin, factor, sy, sy1); + euclidian_ratio(rect.xmin, factor, sxz, sx1z); + // loop over rows + const GPixel *sptr = (*src)[sy]; + GPixel *dptr = (*this)[0]; + for (int y=0; y<nrows; y++) + { + // loop over columns + int sx = sxz; + int sx1 = sx1z; + for (int x=0; x<ncolumns; x++) + { + dptr[x] = sptr[sx]; + // next column + if (++sx1 >= factor) + { + sx1 = 0; + sx += 1; + } + } + // next row + dptr += rowsize(); + if (++sy1 >= factor) + { + sy1 = 0; + sptr += src->rowsize(); + } + } +} + + +static inline void +downsample_4x4_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd) +{ + const GPixel *x = s; + const GPixel *y = x + sadd; + d[0].b = ( 11*x[0].b + 2*(x[1].b + y[0].b ) + y[1].b + 8) >> 4; + d[0].g = ( 11*x[0].g + 2*(x[1].g + y[0].g ) + y[1].g + 8) >> 4; + d[0].r = ( 11*x[0].r + 2*(x[1].r + y[0].r ) + y[1].r + 8) >> 4; + d[1].b = ( 7*(x[1].b + x[2].b) + y[1].b + y[2].b + 8 ) >> 4; + d[1].g = ( 7*(x[1].g + x[2].g) + y[1].g + y[2].g + 8 ) >> 4; + d[1].r = ( 7*(x[1].r + x[2].r) + y[1].r + y[2].r + 8 ) >> 4; + d[2].b = ( 11*x[3].b + 2*(x[2].b + y[3].b ) + y[2].b + 8) >> 4; + d[2].g = ( 11*x[3].g + 2*(x[2].g + y[3].g ) + y[2].g + 8) >> 4; + d[2].r = ( 11*x[3].r + 2*(x[2].r + y[3].r ) + y[2].r + 8) >> 4; + d = d + dadd; + x = x + sadd + sadd; + d[0].b = ( 7*(x[0].b + y[0].b) + x[1].b + y[1].b + 8 ) >> 4; + d[0].g = ( 7*(x[0].g + y[0].g) + x[1].g + y[1].g + 8 ) >> 4; + d[0].r = ( 7*(x[0].r + y[0].r) + x[1].r + y[1].r + 8 ) >> 4; + d[1].b = ( x[2].b + y[2].b + x[1].b + y[1].b + 2 ) >> 2; + d[1].g = ( x[2].g + y[2].g + x[1].g + y[1].g + 2 ) >> 2; + d[1].r = ( x[2].r + y[2].r + x[1].r + y[1].r + 2 ) >> 2; + d[2].b = ( 7*(x[3].b + y[3].b) + x[2].b + y[2].b + 8 ) >> 4; + d[2].g = ( 7*(x[3].g + y[3].g) + x[2].g + y[2].g + 8 ) >> 4; + d[2].r = ( 7*(x[3].r + y[3].r) + x[2].r + y[2].r + 8 ) >> 4; + d = d + dadd; + y = y + sadd + sadd; + d[0].b = ( 11*y[0].b + 2*(y[1].b + x[0].b ) + x[1].b + 8) >> 4; + d[0].g = ( 11*y[0].g + 2*(y[1].g + x[0].g ) + x[1].g + 8) >> 4; + d[0].r = ( 11*y[0].r + 2*(y[1].r + x[0].r ) + x[1].r + 8) >> 4; + d[1].b = ( 7*(y[1].b + y[2].b) + x[1].b + x[2].b + 8 ) >> 4; + d[1].g = ( 7*(y[1].g + y[2].g) + x[1].g + x[2].g + 8 ) >> 4; + d[1].r = ( 7*(y[1].r + y[2].r) + x[1].r + x[2].r + 8 ) >> 4; + d[2].b = ( 11*y[3].b + 2*(y[2].b + x[3].b ) + x[2].b + 8) >> 4; + d[2].g = ( 11*y[3].g + 2*(y[2].g + x[3].g ) + x[2].g + 8) >> 4; + d[2].r = ( 11*y[3].r + 2*(y[2].r + x[3].r ) + x[2].r + 8) >> 4; +} + + +static inline void +upsample_2x2_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd) +{ + const GPixel *x = s; + const GPixel *y = x + sadd; + d[0] = x[0]; + d[1].b = (x[0].b + x[1].b + 1) >> 1; + d[1].g = (x[0].g + x[1].g + 1) >> 1; + d[1].r = (x[0].r + x[1].r + 1) >> 1; + d[2] = x[1]; + d = d + dadd; + d[0].b = (x[0].b + y[0].b + 1) >> 1; + d[0].g = (x[0].g + y[0].g + 1) >> 1; + d[0].r = (x[0].r + y[0].r + 1) >> 1; + d[1].b = (x[0].b + y[0].b + x[1].b + y[1].b + 2) >> 2; + d[1].g = (x[0].g + y[0].g + x[1].g + y[1].g + 2) >> 2; + d[1].r = (x[0].r + y[0].r + x[1].r + y[1].r + 2) >> 2; + d[2].b = (x[1].b + y[1].b + 1) >> 1; + d[2].g = (x[1].g + y[1].g + 1) >> 1; + d[2].r = (x[1].r + y[1].r + 1) >> 1; + d = d + dadd; + d[0] = y[0]; + d[1].b = (y[0].b + y[1].b + 1) >> 1; + d[1].g = (y[0].g + y[1].g + 1) >> 1; + d[1].r = (y[0].r + y[1].r + 1) >> 1; + d[2] = y[1]; +} + + +static inline void +copy_to_partial(int w, int h, + const GPixel *s, int sadd, + GPixel *d, int dadd, int xmin, int xmax, int ymin, int ymax) +{ + int y = 0; + while (y<ymin && y<h) + { + y += 1; + s += sadd; + d += dadd; + } + while (y<ymax && y<h) + { + int x = (xmin>0 ? xmin : 0); + while (x<w && x<xmax) + { + d[x] = s[x]; + x++; + } + y += 1; + s += sadd; + d += dadd; + } +} + + +static inline void +copy_line(const GPixel *s, int smin, int smax, + GPixel *d, int dmin, int dmax) +{ + int x = dmin; + while (x < smin) + { + d[x] = s[smin]; + x++; + } + while (x < dmax && x < smax) + { + d[x] = s[x]; + x++; + } + while (x < dmax) + { + d[x] = s[smax]; + x++; + } +} + + +static inline void +copy_from_partial(int w, int h, + const GPixel *s, int sadd, int xmin, int xmax, int ymin, int ymax, + GPixel *d, int dadd) +{ + int y = 0; + s += (ymin>0 ? sadd * ymin : 0); + while (y<ymin && y<h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + d += dadd; + } + while (y<ymax && y<h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + s += sadd; + d += dadd; + } + s -= sadd; + while (y < h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + d += dadd; + } +} + + + + + +void +GPixmap::downsample43(const GPixmap *src, const GRect *pdr) +{ + // check arguments + int srcwidth = src->columns(); + int srcheight = src->rows(); + int destwidth = (srcwidth * 3 + 3 ) / 4; + int destheight = (srcheight * 3 + 3) / 4; + GRect rect(0, 0, destwidth, destheight); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow3") ); + rect = *pdr; + destwidth = rect.width(); + destheight = rect.height(); + } + // initialize pixmap + init(destheight, destwidth, 0); + + // compute bounds + int dxz, dy; // location of bottomleft block in destination image + int sxz, sy; // location of bottomleft block in source image + euclidian_ratio(rect.ymin, 3, sy, dy); + euclidian_ratio(rect.xmin, 3, sxz, dxz); + sxz = 4 * sxz; + sy = 4 * sy; + dxz = - dxz; + dy = - dy; + + // prepare variables + int sadd = src->rowsize(); + int dadd = this->rowsize(); + const GPixel *sptr = (*src)[0] + sy * sadd; + GPixel *dptr = (*this)[0] + dy * dadd; + int s4add = 4 * sadd; + int d3add = 3 * dadd; + + // iterate over row blocks + while (dy < destheight) + { + int sx = sxz; + int dx = dxz; + // iterate over column blocks + while (dx < destwidth) + { + GPixel xin[16], xout[9]; + + if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight) + { + if (sx+4<=srcwidth && sy+4<=srcheight) + { + downsample_4x4_to_3x3(sptr+sx, sadd, dptr+dx, dadd); + } + else + { + copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4); + downsample_4x4_to_3x3(xin, 4, dptr+dx, dadd); + } + } + else + { + if (sx+4<=srcwidth && sy+4<=srcheight) + { + downsample_4x4_to_3x3(sptr+sx, sadd, xout, 3); + copy_to_partial(3,3, xout, 3, dptr+dx, dadd,-dx,destwidth-dx,-dy,destheight-dy); + } + else + { + copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4); + downsample_4x4_to_3x3(xin, 4, xout, 3); + copy_to_partial(3,3, xout,3, dptr+dx,dadd,-dx,destwidth-dx,-dy,destheight-dy); + } + } + // next column + dx += 3; + sx += 4; + } + // next row + dy += 3; + dptr += d3add; + sy += 4; + sptr += s4add; + } +} + + +void +GPixmap::upsample23(const GPixmap *src, const GRect *pdr) +{ + // check arguments + int srcwidth = src->columns(); + int srcheight = src->rows(); + int destwidth = (srcwidth * 3 + 1 ) / 2; + int destheight = (srcheight * 3 + 1) / 2; + GRect rect(0, 0, destwidth, destheight); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow4") ); + rect = *pdr; + destwidth = rect.width(); + destheight = rect.height(); + } + // initialize pixmap + init(destheight, destwidth, 0); + + // compute bounds + int dxz, dy; // location of bottomleft block in destination image + int sxz, sy; // location of bottomleft block in source image + euclidian_ratio(rect.ymin, 3, sy, dy); + euclidian_ratio(rect.xmin, 3, sxz, dxz); + sxz = 2 * sxz; + sy = 2 * sy; + dxz = - dxz; + dy = - dy; + + // prepare variables + int sadd = src->rowsize(); + int dadd = this->rowsize(); + const GPixel *sptr = (*src)[0] + sy * sadd; + GPixel *dptr = (*this)[0] + dy * dadd; + int s2add = 2 * sadd; + int d3add = 3 * dadd; + + // iterate over row blocks + while (dy < destheight) + { + int sx = sxz; + int dx = dxz; + // iterate over column blocks + while (dx < destwidth) + { + GPixel xin[4], xout[9]; + + if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight) + { + if (sx+2<=srcwidth && sy+2<=srcheight) + { + upsample_2x2_to_3x3( sptr+sx, sadd, dptr+dx, dadd); + } + else + { + copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2); + upsample_2x2_to_3x3(xin, 2, dptr+dx, dadd); + } + } + else + { + if (sx+2<=srcwidth && sy+2<=srcheight) + { + upsample_2x2_to_3x3( sptr+sx, sadd, xout, 3); + copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy); + } + else + { + copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2); + upsample_2x2_to_3x3(xin, 2, xout, 3); + copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy); + } + } + // next column + dx += 3; + sx += 2; + } + // next row + dy += 3; + dptr += d3add; + sy += 2; + sptr += s2add; + } +} + + +////////////////////////////////////////////////// +// Blitting and attenuating +////////////////////////////////////////////////// + + +static unsigned char clip[512]; +static bool clipok = false; + +static void +compute_clip() +{ + clipok = true; + for (unsigned int i=0; i<sizeof(clip); i++) + clip[i] = (i<256 ? i : 255); +} + + +void +GPixmap::attenuate(const GBitmap *bm, int xpos, int ypos) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=0; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = 0; + dst[x].g = 0; + dst[x].r = 0; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b -= (dst[x].b * level) >> 16; + dst[x].g -= (dst[x].g * level) >> 16; + dst[x].r -= (dst[x].r * level) >> 16; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + } +} + + +void +GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixel *color) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + if (!clipok) compute_clip(); + if (!color) return; + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + unsigned char gr = color->r; + unsigned char gg = color->g; + unsigned char gb = color->b; + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = clip[dst[x].b + gb]; + dst[x].g = clip[dst[x].g + gg]; + dst[x].r = clip[dst[x].r + gr]; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b = clip[dst[x].b + ((gb * level) >> 16)]; + dst[x].g = clip[dst[x].g + ((gg * level) >> 16)]; + dst[x].r = clip[dst[x].r + ((gr * level) >> 16)]; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + } +} + + +void +GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixmap *color) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + if (!color) G_THROW( ERR_MSG("GPixmap.null_color") ); + if (!clipok) compute_clip(); + if (bm->rows()!=color->rows() || bm->columns()!=color->columns()) + G_THROW( ERR_MSG("GPixmap.diff_size") ); + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = clip[dst[x].b + src2[x].b]; + dst[x].g = clip[dst[x].g + src2[x].g]; + dst[x].r = clip[dst[x].r + src2[x].r]; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b = clip[dst[x].b + ((src2[x].b * level) >> 16)]; + dst[x].g = clip[dst[x].g + ((src2[x].g * level) >> 16)]; + dst[x].r = clip[dst[x].r + ((src2[x].r * level) >> 16)]; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + src2 += color->rowsize(); + } +} + + + +void +GPixmap::blend(const GBitmap *bm, int xpos, int ypos, const GPixmap *color) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + if (!color) G_THROW( ERR_MSG("GPixmap.null_color") ); + if (!clipok) compute_clip(); + if (bm->rows()!=color->rows() || bm->columns()!=color->columns()) + G_THROW( ERR_MSG("GPixmap.diff_size") ); + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = src2[x].b; + dst[x].g = src2[x].g; + dst[x].r = src2[x].r; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b -= (((int)dst[x].b - (int)src2[x].b) * level) >> 16; + dst[x].g -= (((int)dst[x].g - (int)src2[x].g) * level) >> 16; + dst[x].r -= (((int)dst[x].r - (int)src2[x].r) * level) >> 16; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + src2 += color->rowsize(); + } +} + + + + +void +GPixmap::stencil(const GBitmap *bm, + const GPixmap *pm, int pms, const GRect *pmr, + double corr) +{ + // Check arguments + GRect rect(0, 0, pm->columns()*pms, pm->rows()*pms); + if (pmr != 0) + { + if (pmr->xmin < rect.xmin || + pmr->ymin < rect.ymin || + pmr->xmax > rect.xmax || + pmr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow5") ); + rect = *pmr; + } + // Compute number of rows + int xrows = nrows; + if ((int)bm->rows() < xrows) + xrows = bm->rows(); + if (rect.height() < xrows) + xrows = rect.height(); + // Compute number of columns + int xcolumns = ncolumns; + if ((int)bm->columns() < xcolumns) + xcolumns = bm->columns(); + if (rect.width() < xcolumns) + xcolumns = rect.width(); + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Prepare color correction table + unsigned char gtable[256]; + color_correction_table_cache(corr, gtable); + // Compute starting point in blown up foreground pixmap + int fgy, fgy1, fgxz, fgx1z; + euclidian_ratio(rect.ymin, pms, fgy, fgy1); + euclidian_ratio(rect.xmin, pms, fgxz, fgx1z); + const GPixel *fg = (*pm)[fgy]; + const unsigned char *src = (*bm)[0]; + GPixel *dst = (*this)[0]; + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + int fgx = fgxz; + int fgx1 = fgx1z; + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = gtable[fg[fgx].b]; + dst[x].g = gtable[fg[fgx].g]; + dst[x].r = gtable[fg[fgx].r]; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b -= (((int)dst[x].b - (int)gtable[fg[fgx].b]) * level) >> 16; + dst[x].g -= (((int)dst[x].g - (int)gtable[fg[fgx].g]) * level) >> 16; + dst[x].r -= (((int)dst[x].r - (int)gtable[fg[fgx].r]) * level) >> 16; + } + } + // Next column + if (++fgx1 >= pms) + { + fgx1 = 0; + fgx += 1; + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + if (++fgy1 >= pms) + { + fgy1 = 0; + fg += pm->rowsize(); + } + } +} + +GP<GPixmap> GPixmap::rotate(int count) +{ + GP<GPixmap> newpixmap(this); + if((count %= 4)) + { + if( count&0x01) + newpixmap = new GPixmap(ncolumns, nrows); + else + newpixmap = new GPixmap(nrows, ncolumns); + + GPixmap &dpixmap = *newpixmap; + + GMonitorLock lock(&pixmap_monitor()); + switch(count) + { + case 1: //// rotate 90 counter clockwise + { + int lastrow = dpixmap.rows()-1; + + for(int y=0; y<nrows; y++) + { + const GPixel *r=operator [] (y); + for(int x=0,xnew=lastrow; xnew>=0; x++,xnew--) + { + dpixmap[xnew][y] = r[x]; + } + } + } + break; + case 2: //// rotate 180 counter clockwise + { + int lastrow = dpixmap.rows()-1; + int lastcolumn = dpixmap.columns()-1; + + for(int y=0,ynew=lastrow; ynew>=0; y++,ynew--) + { + const GPixel *r=operator [] (y); + GPixel *d=dpixmap[ynew]; + for(int xnew=lastcolumn; xnew>=0; r++,xnew--) + { + d[xnew] = *r; + } + } + } + break; + case 3: //// rotate 270 counter clockwise + { + int lastcolumn = dpixmap.columns()-1; + + for(int y=0,ynew=lastcolumn; ynew>=0; y++,ynew--) + { + const GPixel *r=operator [] (y); + for(int x=0; x<ncolumns; x++) + { + dpixmap[x][ynew] = r[x]; + } + } + } + break; + } + } + return newpixmap; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GPixmap.h b/kviewshell/plugins/djvu/libdjvu/GPixmap.h new file mode 100644 index 00000000..32d51c7e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GPixmap.h @@ -0,0 +1,531 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GPixmap.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GPIXMAP_H_ +#define _GPIXMAP_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GPixmap.h + + Files #"GPixmap.h"# and #"GPixmap.cpp"# implement class \Ref{GPixmap}. + Instances of this class represent color images. Each RGB pixel is + represented by structure \Ref{GPixel}. The ``bottom left'' coordinate system + is used consistently in the DjVu library. Line zero of a GPixmap is the + bottom line in the color image. Pixels are organized from left to right + within each line. + + {\bf ToDo} --- More sophisticated color correction schemes. + + @memo + Generic support for color images. + @author + L\'eon Bottou <leonb@research.att.com> + @version + #$Id: GPixmap.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class GBitmap; +class GRect; +class ByteStream; + + +/** Color pixel as a RGB triple. + The colors are represented using three bytes named #r#, #g# and #b#. The + value of these bytes represent additive amounts of light. Color white is + represented by setting all three bytes to #255#. Color black is + represented by setting all three bytes to #0#. This convention should not + be confused with the convention adopted for class \Ref{GBitmap} where the + pixel values represent an ink level. */ + +struct GPixel +{ + /** Blue component. */ + unsigned char b; + /** Green component. */ + unsigned char g; + /** Red component. */ + unsigned char r; + /** Returns true iff colors are identical. */ + friend int operator==(const GPixel & p1, const GPixel & p2); + /** Returns true iff colors are different. */ + friend int operator!=(const GPixel & p1, const GPixel & p2); + /** Returns a hash code for the color. */ + friend unsigned int hash(const GPixel &p); + /** @name Predefined colors. */ + //@{ + /// GPixel::WHITE is initialized to #rgb:255/255/255#. + static const GPixel WHITE; + /// GPixel::BLACK is initialized to #rgb:0/0/0#. + static const GPixel BLACK; + /// GPixel::BLUE is initialized to #rgb:0/0/255#. + static const GPixel BLUE; + /// GPixel::GREEN is initialized to #rgb:0/255/0#. + static const GPixel GREEN; + /// GPixel::RED is initialized to #rgb:255/0/0#. + static const GPixel RED; + //@} +}; + + +/** RGB Color images. + Instances of class #GPixmap# represent color images as a two dimensional + array of pixels \Ref{GPixel}. The bracket operator returns a pointer to + the pixels composing one line of the image. This pointer can be used as + an array to read or write the pixels of this particular line. Following + the general convention of the DjVu Reference Library, line zero is always + the bottom line of the image. + */ + +class GPixmap : public GPEnabled +{ +protected: + GPixmap(void); + GPixmap(int nrows, int ncolumns, const GPixel *filler=0); + GPixmap(const GBitmap &ref); + GPixmap(const GBitmap &ref, const GRect &rect); + GPixmap(const GPixmap &ref); + GPixmap(const GPixmap &ref, const GRect &rect); + GPixmap(ByteStream &ref); + +public: + /// Virtual destructor. + virtual ~GPixmap(); + + void destroy(void); + /** @name Construction. */ + //@{ + /** Creates an empty GBitmap object. The returned GPixmap has zero rows + and zero columns. Use function \Ref{init} to change the size of the + image. */ + static GP<GPixmap> create(void) {return new GPixmap();} + + /** Creates a GPixmap with #nrows# rows and #ncolumns# columns. When the + optional argument #filler# is specified, all pixels are initialized + with the corresponding color. */ + static GP<GPixmap> create( + const int nrows, const int ncolumns, const GPixel *filler=0) + { return new GPixmap(nrows,ncolumns,filler); } + + /** Creates a GPixmap by copying the gray level image #ref#. + The constructed GPixmap has the same size as #ref#. The pixels + are initialized with shades of grays copied from #ref#. */ + static GP<GPixmap> create(const GBitmap &ref) + { return new GPixmap(ref); } + + /** Creates a GPixmap by copying the rectangle #rect# of the gray level + image #ref#. The constructed GPixmap has the same size as rectangle + #rect#. The pixels are initialized with shades of grays converted from + the ink levels represented in #ref#. This conversion depends on the + number of gray levels in #ref#. */ + static GP<GPixmap> create(const GBitmap &ref, const GRect &rect) + { return new GPixmap(ref,rect); } + + /** Copy constructors. Creates a GPixmap by replicating the size and the + contents of GPixmap #ref#. */ + static GP<GPixmap> create(const GPixmap &ref) + { return new GPixmap(ref); } + + /** Creates a GPixmap by copying the rectangle #rect# of the color image #ref#. + The constructed GPixmap has the same size as rectangle #rect#. + The pixels are initialized with colors copied from #ref#. */ + static GP<GPixmap> create(const GPixmap &ref, const GRect &rect) + { return new GPixmap(ref,rect); } + + /** Creates a GPixmap by reading PPM data from ByteStream #ref#. + See \Ref{PNM and RLE file formats} for more information. */ + static GP<GPixmap> create(ByteStream &ref) + { return new GPixmap(ref); } + + //@} + + /** @name Initialization. */ + //@{ + /** Resets the GPixmap to #nrows# rows and #ncolumns# columns. When the + optional argument #filler# is specified, all pixels are initialized with + the corresponding color. The previous content of the GPixmap is discarded. */ + void init(int nrows, int ncolumns, const GPixel *filler=0); + /** Resets the GPixmap by copying the size and the contents of the color + image #ref#. The previous content of the GPixmap is discarded. */ + void init(const GPixmap &ref); + /** Resets the GPixmap by copying the rectangle #rect# of the color image #ref#. + The previous content of the GPixmap is discarded. */ + void init(const GPixmap &ref, const GRect &rect); + /** Resets the GPixmap by copying the size and the contents of the gray + level image #ref#. The optional argument #ramp# is an array of 256 + pixel values used for mapping the gray levels to color values. + Setting #ramp# to zero selects a linear ramp of shades of gray. */ + void init(const GBitmap &ref, const GPixel *ramp=0); + /** Resets the GPixmap by copying the rectangle #rect# of the gray level + image #ref#. The optional argument #ramp# is an array of 256 pixel + values used for mapping the gray levels to color values. Setting #ramp# + to zero selects a linear ramp computed according to the maximal number + of gray levels in #ref#. */ + void init(const GBitmap &ref, const GRect &rect, const GPixel *ramp=0); + /** Resets the GPixmap by reading PPM data from ByteStream #ref#. See + \Ref{PNM and RLE file formats} for more information. */ + void init(ByteStream &ref); + /** Resets the GPixmap by copying the gray level image #ref#. The pixels + are initialized with shades of grays copied from #ref#. */ + GPixmap& operator=(const GBitmap &ref); + /** Copy operator. Resets the GPixmap by copying the size and the contents + of the color image #ref#. The previous content of the GPixmap is + discarded. */ + GPixmap& operator=(const GPixmap &ref); + //@} + + /** @name Accessing pixels. */ + //@{ + /** Returns the number of rows (the image height). */ + unsigned int rows() const; + /** Returns the number of columns (the image width). */ + unsigned int columns() const; + /** Returns a constant pointer to the first GPixel in row #row#. This + pointer can be used as an array to read the row elements. */ + const GPixel * operator[] (int row) const; + /** Returns a pointer to the first GPixel in row #row#. This pointer can be + used as an array to read or write the row elements. */ + GPixel * operator[] (int row); + /** Returns the length (in pixels) of a row in memory. This number is equal + to the difference between pointers to pixels located in the same column + in consecutive rows. This difference may be larger than the number of + columns in the image. */ + unsigned int rowsize() const; + //@} + + /** @name Resampling images. */ + //@{ + /** Resets this GPixmap with a subsampled segment of color image #src#. + This function conceptually rescales image #src# by a factor #1:factor#, + and copies rectangle #rect# of the subsampled image into the current GPixmap. + The full subsampled image is copied if #rect# is a null pointer. + Both operations are however performed together for efficiency reasons. + Subsampling works by averaging the colors of the source pixels located + in small squares of size #factor# times #factor#. */ + void downsample(const GPixmap *src, int factor, const GRect *rect=0); + /** Resets this GPixmap with a oversampled segment of color image #src#. + This function conceptually rescales image #src# by a factor #factor:1#, + and copies rectangle #rect# of the oversampled image into the current + GPixmap. The full oversampled image is copied if #rect# is a null + pointer. Both operations are however performed together for efficiency + reasons. Oversampling works by replicating the color of the source + pixels into squares of size #factor# times #factor#. */ + void upsample(const GPixmap *src, int factor, const GRect *rect=0); + /** Resets this GPixmap with a rescaled segment of #src# (zoom 75%). This + function conceptually rescales image #src# by a factor #3:4#, and copies + rectangle #rect# of the rescaled image into the current GPixmap. The + full rescaled image is copied if #rect# is a null pointer. Both + operations are however performed together for efficiency reasons. This + function has been superseded by class \Ref{GPixmapScaler}. */ + void downsample43(const GPixmap *src, const GRect *rect=0); + /** Resets this GPixmap with a rescaled segment of #src# (zoom 150%). This + function conceptually rescales image #src# by a factor #3:2# and copies + rectangle #rect# of the rescaled image into the current GPixmap. The + full rescaled image is copied if #rect# is a null pointer. Both + operations are however performed together for efficiency reasons. This + function has been superseded by class \Ref{GPixmapScaler}. */ + void upsample23(const GPixmap *src, const GRect *rect=0); + //@} + + /** @name Blitting and applying stencils. + These function is essential for rendering DjVu images. The elementary + functions are \Ref{attenuate} and \Ref{blit}. The combined functions + \Ref{blend} and \Ref{stencil} should be viewed as optimizations. */ + //@{ + /** Attenuates the color image in preparation for a blit. + Bitmap #bm# is positionned at location #x#,#y# over this color image. + The matching color image pixels are then multiplied by #1.0-Alpha# where + #Alpha# denotes the gray value, in range #[0,1]#, represented by the + corresponding pixel of bitmap #bm#. */ + void attenuate(const GBitmap *bm, int x, int y); + /** Blits solid color #color# through transparency mask #bm#. + Bitmap #bm# is positionned at location #x#,#y# over this color image. + The matching color image pixels are then modified by adding color + #color# multiplied by #Alpha#, where #Alpha# denotes the gray value, in + range #[0,1]#, represented by the corresponding pixel of bitmap #bm#. */ + void blit(const GBitmap *bm, int x, int y, const GPixel *color); + /** Blits pixmap #color# through transparency mask #bm#. + Bitmap #bm# is positionned at location #x#,#y# over this color image. + The matching color image pixels are then modified by adding the + corresponding pixel color in pixmap #color#, multiplied by #Alpha#, + where #Alpha# denotes the gray value, in range #[0,1]#, represented by + the corresponding pixel of bitmap #bm#. */ + void blit(const GBitmap *bm, int x, int y, const GPixmap *color); + /** Performs alpha blending. This function is similar to first calling + \Ref{attenuate} with alpha map #bm# and then calling \Ref{blit} with + alpha map #bm# and color map #color#. Both operations are performed + together for efficiency reasons. */ + void blend(const GBitmap *bm, int x, int y, const GPixmap *color); + /** Resample color pixmap and performs color corrected alpha blending. This + function conceptually computes an intermediate color image by first + upsampling the GPixmap #pm# by a factor #pms:1# (see \Ref{upsample}), + extracting the sub-image designated by rectangle #pmr# and applying + color correction #corr# (see \Ref{color_correct}). This intermediate + color image is then blended into this pixel map according to the alpha + map #bm# (see \Ref{blend}). */ + void stencil(const GBitmap *bm, + const GPixmap *pm, int pms, + const GRect *pmr, double corr=1.0); + //@} + + /** @name Manipulating colors. */ + //@{ + /** Dithers the image to 216 colors. This function applies an ordered + dithering algorithm to reduce the image to 216 predefined colors. These + predefined colors are located on a color cube of 6x6x6 colors: the color + RGB coordinates can only take the following values: #0#, #51#, #102#, + #163#, #214# or #255#. This is useful for displaying images on a device + supporting a maximum of 256 colors. Arguments #xmin# and #ymin# control + the position of the dithering grids. This is useful for dithering tiled + images. Arguments #xmin# and #ymin# must be the position of the bottom + left corner of the tile contained in this GPixmap. Properly setting + these arguments eliminates dithering artifacts on the tile + boundaries. */ + void ordered_666_dither(int xmin=0, int ymin=0); + /** Dithers the image to 32768 colors. This function applies an ordered + dithering algorithm to reduce the image to 32768 predefined colors. + These predefined colors are located on a color cube of 32x32x32 colors: + the color RGB coordinates can only take values in which the three least + significant bits are set to #1#. This is useful for displaying images + with less than 24 bits per pixel. Arguments #xmin# and #ymin# control + the position of the dithering grids. This is useful for dithering tiled + images. Arguments #xmin# and #ymin# must be the position of the bottom + left corner of the tile contained in this GPixmap. Properly setting + these arguments eliminates dithering artifacts on the tile + boundaries. */ + void ordered_32k_dither(int xmin=0, int ymin=0); + /** Applies a luminance gamma correction factor of #corr#. Values greater than + #1.0# make the image brighter. Values smaller than #1.0# make the image + darker. The documentation of program \Ref{ppmcoco} explains how to + properly use this function. */ + void color_correct(double corr); + /** Applies a luminance gamma correction to an array of pixels. + This function is {\em static} and does not modify this pixmap. */ + static void color_correct(double corr, GPixel *pixels, int npixels); + + //@} + + /** @name Miscellaneous. */ + //@{ + /** Returns the number of bytes allocated for this image. */ + inline unsigned int get_memory_usage() const; + /** Saves the image into ByteStream #bs# using the PPM format. + Argument #raw# selects the ``Raw PPM'' (1) or the ``Ascii PPM'' (0) format. + See \Ref{PNM and RLE file formats} for more information. */ + void save_ppm(ByteStream &bs, int raw=1) const; + //@} + + /** @name Stealing or borrowing the memory buffer (advanced). */ + //@{ + /** Steals the memory buffer of a GPixmap. This function returns the + address of the memory buffer allocated by this GPixmap object. The + offset of the first pixel in the bottom line is written into variable + #offset#. Other lines can be accessed using pointer arithmetic (see + \Ref{rowsize}). The GPixmap object no longer ``owns'' the buffer: you + must explicitly de-allocate the buffer using #operator delete []#. This + de-allocation should take place after the destruction or the + re-initialization of the GPixmap object. This function will return a + null pointer if the GPixmap object does not ``own'' the buffer in the + first place. */ + GPixel *take_data(size_t &offset); + /** Initializes this GPixmap by borrowing a memory segment. The GPixmap + then directly addresses the memory buffer #data# provided by the user. + This buffer must be large enough to hold #w*h# GPixels. The GPixmap + object does not ``own'' the buffer: you must explicitly de-allocate the + buffer using #operator delete []#. This de-allocation should take place + after the destruction or the re-initialization of the GPixmap object. */ + inline void borrow_data(GPixel &data, int w, int h); + /// Identical to the above, but GPixmap will do the delete []. + void donate_data(GPixel *data, int w, int h); + + /** Rotates pixmap by 90, 180 or 270 degrees anticlockwise + and returns a new pixmap, input pixmap is not changed. + count can be 1, 2, or 3 for 90, 180, 270 degree rotation. + It returns the same pixmap if not rotated. */ + GP<GPixmap> rotate(int count=0); + + //@} + + // Please ignore these two functions. Their only purpose is to allow + // DjVu viewer compile w/o errors. eaf. + // Is this still useful ?. lyb. + int get_grays(void) const { return 256; }; + void set_grays(int) {};\ + +protected: + // data + unsigned short nrows; + unsigned short ncolumns; + unsigned short nrowsize; + GPixel *pixels; + GPixel *pixels_data; + friend class DjVu_PixImage; +}; + +//@} + +// INLINE -------------------------- + + +inline int +operator==(const GPixel & p1, const GPixel & p2) +{ + return p1.r==p2.r && p1.g==p2.g && p1.b==p2.b; +} + +inline int +operator!=(const GPixel & p1, const GPixel & p2) +{ + return p1.r!=p2.r || p1.g!=p2.g || p1.b!=p2.b; +} + +inline unsigned int +hash(const GPixel &p) +{ + unsigned int x = (p.b<<16)|(p.g<<8)|(p.r); + return x ^ (p.b<<4) ^ (p.r<<12); +} + +inline unsigned int +GPixmap::rows() const +{ + return nrows; +} + +inline unsigned int +GPixmap::columns() const +{ + return ncolumns; +} + +inline unsigned int +GPixmap::rowsize() const +{ + return nrowsize; +} + +inline GPixel * +GPixmap::operator[](int row) +{ + if (row<0 || row>=nrows || !pixels) return 0; + return &pixels[row * nrowsize]; +} + +inline const GPixel * +GPixmap::operator[](int row) const +{ + if (row<0 || row>=nrows) return 0; + return &pixels[row * nrowsize]; +} + +inline GPixmap & +GPixmap::operator=(const GBitmap &ref) +{ + init(ref); + return *this; +} + +inline GPixmap & +GPixmap::operator=(const GPixmap &ref) +{ + init(ref); + return *this; +} + +inline void +GPixmap::borrow_data(GPixel &data, int w, int h) +{ + donate_data(&data,w,h); + pixels_data=0; +} + +////////////////////////////////////////////////// +// Memory usage +////////////////////////////////////////////////// + + +inline unsigned int +GPixmap::get_memory_usage() const +{ + return sizeof(GPixmap)+(nrows * ncolumns * sizeof(GPixel)); +} + +// --------------------------------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/GRect.cpp b/kviewshell/plugins/djvu/libdjvu/GRect.cpp new file mode 100644 index 00000000..1ac0a87c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GRect.cpp @@ -0,0 +1,458 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GRect.cpp,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// -- Implementation of class GRect and GRectMapper +// - Author: Leon Bottou, 05/1997 + + +#include "GRect.h" +#include "GException.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +// -- Local utilities + +static inline int +imin(int x, int y) +{ + if (x < y) + return x; + else + return y; +} + +static inline int +imax(int x, int y) +{ + if (x > y) + return x; + else + return y; +} + +static inline void +iswap(int &x, int &y) +{ + int tmp = x; x = y; y = tmp; +} + +// -- Class GRect + +int +operator==(const GRect & r1, const GRect & r2) +{ + int isempty1 = r1.isempty(); + int isempty2 = r2.isempty(); + if (isempty1 || isempty2) + if (isempty1 && isempty2) + return 1; + if ( r1.xmin==r2.xmin && r1.xmax==r2.xmax + && r1.ymin==r2.ymin && r1.ymax==r2.ymax ) + return 1; + return 0; +} + +int +GRect::inflate(int dx, int dy) +{ + xmin -= dx; + xmax += dx; + ymin -= dy; + ymax += dy; + if (! isempty()) + return 1; + xmin = ymin = xmax = ymax = 0; + return 0; +} + +int +GRect::translate(int dx, int dy) +{ + xmin += dx; + xmax += dx; + ymin += dy; + ymax += dy; + if (! isempty()) + return 1; + xmin = ymin = xmax = ymax = 0; + return 0; +} + +int +GRect::intersect(const GRect &rect1, const GRect &rect2) +{ + xmin = imax(rect1.xmin, rect2.xmin); + xmax = imin(rect1.xmax, rect2.xmax); + ymin = imax(rect1.ymin, rect2.ymin); + ymax = imin(rect1.ymax, rect2.ymax); + if (! isempty()) + return 1; + xmin = ymin = xmax = ymax = 0; + return 0; +} + +int +GRect::recthull(const GRect &rect1, const GRect &rect2) +{ + if (rect1.isempty()) + { + xmin = rect2.xmin; + xmax = rect2.xmax; + ymin = rect2.ymin; + ymax = rect2.ymax; + return !isempty(); + } + if (rect2.isempty()) + { + xmin = rect1.xmin; + xmax = rect1.xmax; + ymin = rect1.ymin; + ymax = rect1.ymax; + return !isempty(); + } + xmin = imin(rect1.xmin, rect2.xmin); + xmax = imax(rect1.xmax, rect2.xmax); + ymin = imin(rect1.ymin, rect2.ymin); + ymax = imax(rect1.ymax, rect2.ymax); + return 1; +} + +int +GRect::contains(const GRect & rect) const +{ + GRect tmp_rect; + tmp_rect.intersect(*this, rect); + return tmp_rect==rect; +} + +void +GRect::scale(float factor) +{ + xmin = (int)(((float)xmin) * factor); + ymin = (int)(((float)ymin) * factor); + xmax = (int)(((float)xmax) * factor); + ymax = (int)(((float)ymax) * factor); +} + +void +GRect::scale(float xfactor, float yfactor) +{ + xmin = (int)(((float)xmin) * xfactor); + ymin = (int)(((float)ymin) * yfactor); + xmax = (int)(((float)xmax) * xfactor); + ymax = (int)(((float)ymax) * yfactor); +} +// -- Class GRatio + + +inline +GRectMapper::GRatio::GRatio() + : p(0), q(1) +{ +} + +inline +GRectMapper::GRatio::GRatio(int p, int q) + : p(p), q(q) +{ + if (q == 0) + G_THROW( ERR_MSG("GRect.div_zero") ); + if (p == 0) + q = 1; + if (q < 0) + { + p = -p; + q = -q; + } + int gcd = 1; + int g1 = p; + int g2 = q; + if (g1 > g2) + { + gcd = g1; + g1 = g2; + g2 = gcd; + } + while (g1 > 0) + { + gcd = g1; + g1 = g2 % g1; + g2 = gcd; + } + p /= gcd; + q /= gcd; +} + + +#ifdef HAVE_LONG_LONG_INT +#define llint_t long long int +#else +#define llint_t long int +#endif + +inline int +operator*(int n, GRectMapper::GRatio r ) +{ + /* [LB] -- This computation is carried out with integers and + rational numbers because it must be exact. Some lizard changed + it to double and this is wrong. I suspect they did so because + they encountered overflow issues. Let's use long long ints. */ + llint_t x = (llint_t) n * (llint_t) r.p; + if (x >= 0) + return ((r.q/2 + x) / r.q); + else + return - ((r.q/2 - x) / r.q); +} + +inline int +operator/(int n, GRectMapper::GRatio r ) +{ + /* [LB] -- See comment in operator*() above. */ + llint_t x = (llint_t) n * (llint_t) r.q; + if (x >= 0) + return ((r.p/2 + x) / r.p); + else + return - ((r.p/2 - x) / r.p); +} + + +// -- Class GRectMapper + +#define MIRRORX 1 +#define MIRRORY 2 +#define SWAPXY 4 + + +GRectMapper::GRectMapper() +: rectFrom(0,0,1,1), + rectTo(0,0,1,1), + code(0) +{ + +} + +void +GRectMapper::clear() +{ + rectFrom = GRect(0,0,1,1); + rectTo = GRect(0,0,1,1); + code = 0; +} + +void +GRectMapper::set_input(const GRect &rect) +{ + if (rect.isempty()) + G_THROW( ERR_MSG("GRect.empty_rect1") ); + rectFrom = rect; + if (code & SWAPXY) + { + iswap(rectFrom.xmin, rectFrom.ymin); + iswap(rectFrom.xmax, rectFrom.ymax); + } + rw = rh = GRatio(); +} + +void +GRectMapper::set_output(const GRect &rect) +{ + if (rect.isempty()) + G_THROW( ERR_MSG("GRect.empty_rect2") ); + rectTo = rect; + rw = rh = GRatio(); +} + +void +GRectMapper::rotate(int count) +{ + int oldcode = code; + switch (count & 0x3) + { + case 1: + code ^= (code & SWAPXY) ? MIRRORY : MIRRORX; + code ^= SWAPXY; + break; + case 2: + code ^= (MIRRORX|MIRRORY); + break; + case 3: + code ^= (code & SWAPXY) ? MIRRORX : MIRRORY; + code ^= SWAPXY; + break; + } + if ((oldcode ^ code) & SWAPXY) + { + iswap(rectFrom.xmin, rectFrom.ymin); + iswap(rectFrom.xmax, rectFrom.ymax); + rw = rh = GRatio(); + } +} + +void +GRectMapper::mirrorx() +{ + code ^= MIRRORX; +} + +void +GRectMapper::mirrory() +{ + code ^= MIRRORY; +} + +void +GRectMapper::precalc() +{ + if (rectTo.isempty() || rectFrom.isempty()) + G_THROW( ERR_MSG("GRect.empty_rect3") ); + rw = GRatio(rectTo.width(), rectFrom.width()); + rh = GRatio(rectTo.height(), rectFrom.height()); +} + +void +GRectMapper::map(int &x, int &y) +{ + int mx = x; + int my = y; + // precalc + if (! (rw.p && rh.p)) + precalc(); + // swap and mirror + if (code & SWAPXY) + iswap(mx,my); + if (code & MIRRORX) + mx = rectFrom.xmin + rectFrom.xmax - mx; + if (code & MIRRORY) + my = rectFrom.ymin + rectFrom.ymax - my; + // scale and translate + x = rectTo.xmin + (mx - rectFrom.xmin) * rw; + y = rectTo.ymin + (my - rectFrom.ymin) * rh; +} + +void +GRectMapper::unmap(int &x, int &y) +{ + // precalc + if (! (rw.p && rh.p)) + precalc(); + // scale and translate + int mx = rectFrom.xmin + (x - rectTo.xmin) / rw; + int my = rectFrom.ymin + (y - rectTo.ymin) / rh; + // mirror and swap + if (code & MIRRORX) + mx = rectFrom.xmin + rectFrom.xmax - mx; + if (code & MIRRORY) + my = rectFrom.ymin + rectFrom.ymax - my; + if (code & SWAPXY) + iswap(mx,my); + x = mx; + y = my; +} + +void +GRectMapper::map(GRect &rect) +{ + map(rect.xmin, rect.ymin); + map(rect.xmax, rect.ymax); + if (rect.xmin >= rect.xmax) + iswap(rect.xmin, rect.xmax); + if (rect.ymin >= rect.ymax) + iswap(rect.ymin, rect.ymax); +} + +void +GRectMapper::unmap(GRect &rect) +{ + unmap(rect.xmin, rect.ymin); + unmap(rect.xmax, rect.ymax); + if (rect.xmin >= rect.xmax) + iswap(rect.xmin, rect.xmax); + if (rect.ymin >= rect.ymax) + iswap(rect.ymin, rect.ymax); +} + +GRect +GRectMapper::get_input() +{ + return rectFrom; +} + +GRect +GRectMapper::get_output() +{ + return rectTo; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GRect.h b/kviewshell/plugins/djvu/libdjvu/GRect.h new file mode 100644 index 00000000..1a86f80d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GRect.h @@ -0,0 +1,407 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GRect.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GRECT_H_ +#define _GRECT_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name GRect.h + Files #"GRect.h"# and #"GRect.cpp"# implement basic operations on + rectangles. Class \Ref{GRect} is used to represent rectangles. Class + \Ref{GRectMapper} represent the correspondence between points relative to + given rectangles. Class \Ref{GRatio} is used to represent scaling factors + as rational numbers. + @memo + Rectangle manipulation class. + @author + L\'eon Bottou <leonb@research.att.com> -- initial implementation. + @version + #$Id: GRect.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** @name Point Coordinates vs. Pixel Coordinates + + The DjVu technology relies on the accurate superposition of images at + different resolutions. Such an accuracy cannot be reached with the usual + assumption that pixels are small enough to be considered infinitesimally + small. We must distinguish very precisely ``points'' and ``pixels''. + This distinction is essential for performing scaling operations. + + The pixels of an image are identified by ``pixel coordinates''. The + bottom-left corner pixel has coordinates #(0,0)# and the top-right corner + pixel has coordinates #(w-1,h-1)# where #w# and #h# are the image size. + Pixel coordinates are necessarily integers since pixels never overlap. + + An infinitesimally small point is identified by its ``point coordinates''. + There may be fractional point coordinates, although this library does not + make use of them. Points with integer coordinates are located {\em on the + corners of each pixel}. They are not located on the pixel centers. The + center of the pixel with pixel coordinates #(i,j)# is located at point + coordinates #(i+1/2,j+1/2)#. In other words, the pixel #(i,j)# extends + from point #(i,j)# to point #(i+1,j+1)#. + + Therefore, the point located on the bottom left corner of an image has + coordinates #(0,0)#. This point is in fact the bottom left corner of the + bottom left pixel of the image. The point located on the top right corner + of an image has coordinates #(w,h)# where #w# and #h# are the image size. + This is in fact the top right corner of pixel #(w-1,h-1)# which is the + image pixel with the highest coordinates. +*/ +//@{ +//@} + + + +/** Rectangle class. Each instance of this class represents a rectangle whose + sides are parallel to the axis. Such a rectangle represents all the points + whose coordinates lies between well defined minimal and maximal values. + Member functions can combine several rectangles by computing the + intersection of rectangles (\Ref{intersect}) or the smallest rectangle + enclosing two rectangles (\Ref{recthull}). */ + +class GRect +{ +public: + /** #OrientationBits# defines 3 mutually exclusive + bits to indicate the image orientation. + + There are four possible rotation values for an image + which are 0 degrees, 90 degrees, 180 degrees, and 270 degrees. + In addition the image can be mirrored backwards in any of these + orientations, giving a possible of 8 orientations. To sanely deal + with these orientations, we have defined 3 mutually exclusive + bits. These are BOTTOM_UP, MIRROR, and ROTATE90_CW. + */ + enum OrientationBits + { + BOTTOM_UP=0x1, /* Upside down */ + MIRROR=0x2, /* Written backwards. (right to left) */ + ROTATE90_CW=0x4 /* rotated 90 degrees */ + }; + + /** #Orientations# defines all 8 possible orientations, using + the three \Ref{OrientationBits}. + \begin{itemize} + \item {\em TDLRNR} for Top Down, Left to Right, No Rotation. + \item {\em BULRNR} for Bottom Up, Left to Right, No Rotation. + \item {\em TDRLNR} for Top Down, Right to Left, No Rotation. + \item {\em BURLNR} for Bottom Up, Right to Left, No Rotation. + \item {\em TDLRCW} for Top Down, Left to Right, 90 degree CW rotation. + \item {\em BULRCW} for Bottom Up, Left to Right, 90 degree CW rotation. + \item {\em TDRLCW} for Top Down, Right to Left, 90 degree CW rotation. + \item {\em BURLCW} for Bottom Up, Right to Left, 90 degree CW rotation. + \end{itemize} + */ + enum Orientations + { + TDLRNR=0, /* normal orientation */ + BULRNR=BOTTOM_UP, /* upside down */ + TDRLNR=MIRROR, /* backwards (right to left) */ + BURLNR=MIRROR|BOTTOM_UP, /* rotate 180 */ + TDLRCW=ROTATE90_CW, /* rotated 90 */ + BULRCW=ROTATE90_CW|BOTTOM_UP, /* backwards and rotate 180 */ + TDRLCW=ROTATE90_CW|MIRROR, /* backwards and rotate 90 */ + BURLCW=ROTATE90_CW|MIRROR|BOTTOM_UP /* rotate 270 */ + }; + + static Orientations + rotate(const int angle,Orientations orientation) + { + for(int a=(((angle)%360)+405)%360;a>90;a-=90) + orientation=(Orientations)((int)orientation^(int)(orientation&ROTATE90_CW)?BURLCW:TDLRCW); + return orientation; + } + + static int + findangle(const Orientations orientation) + { + int a=270; + while(a&&(rotate(a,BURLNR)!=orientation)&&(rotate(a,TDRLNR)!=orientation)) + a-=90; + return a; + } + + /** Constructs an empty rectangle */ + GRect(); + /** Constructs a rectangle given its minimal coordinates #xmin# and #ymin#, + and its measurements #width# and #height#. Setting #width# or #height# to zero + produces an empty rectangle. */ + GRect(int xmin, int ymin, unsigned int width=0, unsigned int height=0); + /** Returns the rectangle width. */ + int width() const; + /** Returns the rectangle height. */ + int height() const; + /** Returns the area of the rectangle. */ + int area() const; + /** Returns true if the rectangle is empty. */ + int isempty() const; + /** Returns true if the rectangle contains pixel (#x#,#y#). A rectangle + contains all pixels with horizontal pixel coordinates in range #xmin# + (inclusive) to #xmax# (exclusive) and vertical coordinates #ymin# + (inclusive) to #ymax# (exclusive). */ + int contains(int x, int y) const; + /** Returns true if this rectangle contains the passed rectangle #rect#. + The function basically checks, that the intersection of this rectangle + with #rect# is #rect#. */ + int contains(const GRect & rect) const; + /** Returns true if rectangles #r1# and #r2# are equal. */ + friend int operator==(const GRect & r1, const GRect & r2); + /** Returns true if rectangles #r1# and #r2# are not equal. */ + friend int operator!=(const GRect & r1, const GRect & r2); + /** Resets the rectangle to the empty rectangle */ + void clear(); + /** Fatten the rectangle. Both vertical sides of the rectangle are pushed + apart by #dx# units. Both horizontal sides of the rectangle are pushed + apart by #dy# units. Setting arguments #dx# (resp. #dy#) to a negative + value reduces the rectangle horizontal (resp. vertical) size. */ + int inflate(int dx, int dy); + /** Translate the rectangle. The new rectangle is composed of all the points + of the old rectangle translated by #dx# units horizontally and #dy# + units vertically. */ + int translate(int dx, int dy); + /** Sets the rectangle to the intersection of rectangles #rect1# and #rect2#. + This function returns true if the intersection rectangle is not empty. */ + int intersect(const GRect &rect1, const GRect &rect2); + /** Sets the rectangle to the smallest rectangle containing the points of + both rectangles #rect1# and #rect2#. This function returns true if the + created rectangle is not empty. */ + int recthull(const GRect &rect1, const GRect &rect2); + /** Multiplies xmin, ymin, xmax, ymax by factor and scales the rectangle*/ + void scale(float factor); + /** Multiplies xmin, xmax by xfactor and ymin, ymax by yfactor and scales the rectangle*/ + void scale(float xfactor, float yfactor); + /** Minimal horizontal point coordinate of the rectangle. */ + int xmin; + /** Minimal vertical point coordinate of the rectangle. */ + int ymin; + /** Maximal horizontal point coordinate of the rectangle. */ + int xmax; + /** Maximal vertical point coordinate of the rectangle. */ + int ymax; +}; + + +/** Maps points from one rectangle to another rectangle. This class + represents a relation between the points of two rectangles. Given the + coordinates of a point in the first rectangle (input rectangle), function + \Ref{map} computes the coordinates of the corresponding point in the + second rectangle (the output rectangle). This function actually implements + an affine transform which maps the corners of the first rectangle onto the + matching corners of the second rectangle. The scaling operation is + performed using integer fraction arithmetic in order to maximize + accuracy. */ +class GRectMapper +{ +public: + /** Constructs a rectangle mapper. */ + GRectMapper(); + /** Resets the rectangle mapper state. Both the input rectangle + and the output rectangle are marked as undefined. */ + void clear(); + /** Sets the input rectangle. */ + void set_input(const GRect &rect); + /** Returns the input rectangle. */ + GRect get_input(); + /** Sets the output rectangle. */ + void set_output(const GRect &rect); + /** Returns the output rectangle. */ + GRect get_output(); + /** Composes the affine transform with a rotation of #count# quarter turns + counter-clockwise. This operation essentially is a modification of the + match between the corners of the input rectangle and the corners of the + output rectangle. */ + void rotate(int count=1); + /** Composes the affine transform with a symmetry with respect to the + vertical line crossing the center of the output rectangle. This + operation essentially is a modification of the match between the corners + of the input rectangle and the corners of the output rectangle. */ + void mirrorx(); + /** Composes the affine transform with a symmetry with respect to the + horizontal line crossing the center of the output rectangle. This + operation essentially is a modification of the match between the corners + of the input rectangle and the corners of the output rectangle. */ + void mirrory(); + /** Maps a point according to the affine transform. Variables #x# and #y# + initially contain the coordinates of a point. This operation overwrites + these variables with the coordinates of a second point located in the + same position relative to the corners of the output rectangle as the + first point relative to the matching corners of the input rectangle. + Coordinates are rounded to the nearest integer. */ + void map(int &x, int &y); + /** Maps a rectangle according to the affine transform. This operation + consists in mapping the rectangle corners and reordering the corners in + the canonical rectangle representation. Variable #rect# is overwritten + with the new rectangle coordinates. */ + void map(GRect &rect); + /** Maps a point according to the inverse of the affine transform. + Variables #x# and #y# initially contain the coordinates of a point. This + operation overwrites these variables with the coordinates of a second + point located in the same position relative to the corners of input + rectangle as the first point relative to the matching corners of the + input rectangle. Coordinates are rounded to the nearest integer. */ + void unmap(int &x, int &y); + /** Maps a rectangle according to the inverse of the affine transform. This + operation consists in mapping the rectangle corners and reordering the + corners in the canonical rectangle representation. Variable #rect# is + overwritten with the new rectangle coordinates. */ + void unmap(GRect &rect); +private: + // GRatio + struct GRatio { + GRatio (); + GRatio (int p, int q); + int p; + int q; + }; + // Data + GRect rectFrom; + GRect rectTo; + int code; + // Helper + void precalc(); + friend int operator*(int n, GRatio r ); + friend int operator/(int n, GRatio r ); + GRatio rw; + GRatio rh; +}; + + +//@} + + + +// ---- INLINES + +inline +GRect::GRect() +: xmin(0), ymin(0), xmax(0), ymax(0) +{ +} + +inline +GRect::GRect(int xmin, int ymin, unsigned int width, unsigned int height) +: xmin(xmin), ymin(ymin), xmax(xmin+width), ymax(ymin+height) +{ +} + +inline int +GRect::width() const +{ + return xmax - xmin; +} + +inline int +GRect::height() const +{ + return ymax - ymin; +} + +inline int +GRect::isempty() const +{ + return (xmin>=xmax || ymin>=ymax); +} + +inline int +GRect::area() const +{ + return isempty() ? 0 : (xmax-xmin)*(ymax-ymin); +} + +inline int +GRect::contains(int x, int y) const +{ + return (x>=xmin && x<xmax && y>=ymin && y<ymax); +} + +inline void +GRect::clear() +{ + xmin = xmax = ymin = ymax = 0; +} + +inline int +operator!=(const GRect & r1, const GRect & r2) +{ + return !(r1==r2); +} + +// ---- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GScaler.cpp b/kviewshell/plugins/djvu/libdjvu/GScaler.cpp new file mode 100644 index 00000000..0eeb9ebf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GScaler.cpp @@ -0,0 +1,706 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GScaler.cpp,v 1.11 2004/06/03 14:15:18 leonb Exp $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// Rescale images with fast bilinear interpolation +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "GScaler.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +//////////////////////////////////////// +// CONSTANTS + + +#define FRACBITS 4 +#define FRACSIZE (1<<FRACBITS) +#define FRACSIZE2 (FRACSIZE>>1) +#define FRACMASK (FRACSIZE-1) + + + + + + +//////////////////////////////////////// +// UTILITIES + + +static int interp_ok = 0; +static short interp[FRACSIZE][512]; + +static void +prepare_interp() +{ + if (! interp_ok) + { + interp_ok = 1; + for (int i=0; i<FRACSIZE; i++) + { + short *deltas = & interp[i][256]; + for (int j = -255; j <= 255; j++) + deltas[j] = ( j*i + FRACSIZE2 ) >> FRACBITS; + } + } +} + + +static inline int +mini(int x, int y) +{ + return (x < y ? x : y); +} + + +static inline int +maxi(int x, int y) +{ + return (x > y ? x : y); +} + + + + + + +//////////////////////////////////////// +// GSCALER + + +GScaler::GScaler() + : inw(0), inh(0), + xshift(0), yshift(0), redw(0), redh(0), + outw(0), outh(0), + gvcoord(vcoord,0), ghcoord(hcoord,0) +{ +} + + +GScaler::~GScaler() +{ +} + + +void +GScaler::set_input_size(int w, int h) +{ + inw = w; + inh = h; + if (vcoord) + { + gvcoord.resize(0); + } + if (hcoord) + { + ghcoord.resize(0); + } +} + + +void +GScaler::set_output_size(int w, int h) +{ + outw = w; + outh = h; + if (vcoord) + { + gvcoord.resize(0); + } + if (hcoord) + { + ghcoord.resize(0); + } +} + + +static void +prepare_coord(int *coord, int inmax, int outmax, int in, int out) +{ + int len = (in*FRACSIZE); + int beg = (len+out)/(2*out) - FRACSIZE2; + // Bresenham algorithm + int y = beg; + int z = out/2; + int inmaxlim = (inmax-1)*FRACSIZE; + for (int x=0; x<outmax; x++) + { + coord[x] = mini(y,inmaxlim); + z = z + len; + y = y + z / out; + z = z % out; + } + // Result must fit exactly + if (out==outmax && y!=beg+len) + G_THROW( ERR_MSG("GScaler.assertion") ); +} + + +void +GScaler::set_horz_ratio(int numer, int denom) +{ + if (! (inw>0 && inh>0 && outw>0 && outh>0)) + G_THROW( ERR_MSG("GScaler.undef_size") ); + // Implicit ratio (determined by the input/output sizes) + if (numer==0 && denom==0) { + numer = outw; + denom = inw; + } else if (numer<=0 || denom<=0) + G_THROW( ERR_MSG("GScaler.ratios") ); + // Compute horz reduction + xshift = 0; + redw = inw; + while (numer+numer < denom) { + xshift += 1; + redw = (redw + 1) >> 1; + numer = numer << 1; + } + // Compute coordinate table + if (! hcoord) + ghcoord.resize(outw); + prepare_coord(hcoord, redw, outw, denom, numer); +} + + +void +GScaler::set_vert_ratio(int numer, int denom) +{ + if (! (inw>0 && inh>0 && outw>0 && outh>0)) + G_THROW( ERR_MSG("GScaler.undef_size") ); + // Implicit ratio (determined by the input/output sizes) + if (numer==0 && denom==0) { + numer = outh; + denom = inh; + } else if (numer<=0 || denom<=0) + G_THROW( ERR_MSG("GScaler.ratios") ); + // Compute horz reduction + yshift = 0; + redh = inh; + while (numer+numer < denom) { + yshift += 1; + redh = (redh + 1) >> 1; + numer = numer << 1; + } + // Compute coordinate table + if (! vcoord) + { + gvcoord.resize(outh); + } + prepare_coord(vcoord, redh, outh, denom, numer); +} + + +void +GScaler::make_rectangles(const GRect &desired, GRect &red, GRect &inp) +{ + // Parameter validation + if (desired.xmin<0 || desired.ymin<0 || + desired.xmax>outw || desired.ymax>outh ) + G_THROW( ERR_MSG("GScaler.too_big") ); + // Compute ratio (if not done yet) + if (!vcoord) + set_vert_ratio(0,0); + if (!hcoord) + set_horz_ratio(0,0); + // Compute reduced bounds + red.xmin = (hcoord[desired.xmin]) >> FRACBITS; + red.ymin = (vcoord[desired.ymin]) >> FRACBITS; + red.xmax = (hcoord[desired.xmax-1]+FRACSIZE-1) >> FRACBITS; + red.ymax = (vcoord[desired.ymax-1]+FRACSIZE-1) >> FRACBITS; + // Borders + red.xmin = maxi(red.xmin, 0); + red.xmax = mini(red.xmax+1, redw); + red.ymin = maxi(red.ymin, 0); + red.ymax = mini(red.ymax+1, redh); + // Input + inp.xmin = maxi(red.xmin<<xshift, 0); + inp.xmax = mini(red.xmax<<xshift, inw); + inp.ymin = maxi(red.ymin<<yshift, 0); + inp.ymax = mini(red.ymax<<yshift, inh); +} + + +void +GScaler::get_input_rect( const GRect &desired_output, GRect &required_input ) +{ + GRect red; + make_rectangles(desired_output, red, required_input); +} + + + + + + +//////////////////////////////////////// +// GBITMAPSCALER + + +GBitmapScaler::GBitmapScaler() + : glbuffer(lbuffer,0), gconv(conv,0), gp1(p1,0), gp2(p2,0) +{ +} + + +GBitmapScaler::GBitmapScaler(int inw, int inh, int outw, int outh) + : glbuffer(lbuffer,0), gconv(conv,0), gp1(p1,0), gp2(p2,0) +{ + set_input_size(inw, inh); + set_output_size(outw, outh); +} + + +GBitmapScaler::~GBitmapScaler() +{ +} + + +unsigned char * +GBitmapScaler::get_line(int fy, + const GRect &required_red, + const GRect &provided_input, + const GBitmap &input ) +{ + if (fy < required_red.ymin) + fy = required_red.ymin; + else if (fy >= required_red.ymax) + fy = required_red.ymax - 1; + // Cached line + if (fy == l2) + return p2; + if (fy == l1) + return p1; + // Shift + unsigned char *p = p1; + p1 = p2; + l1 = l2; + p2 = p; + l2 = fy; + if (xshift==0 && yshift==0) + { + // Fast mode + int dx = required_red.xmin-provided_input.xmin; + int dx1 = required_red.xmax-provided_input.xmin; + const unsigned char *inp1 = input[fy-provided_input.ymin] + dx; + while (dx++ < dx1) + *p++ = conv[*inp1++]; + return p2; + } + else + { + // Compute location of line + GRect line; + line.xmin = required_red.xmin << xshift; + line.xmax = required_red.xmax << xshift; + line.ymin = fy << yshift; + line.ymax = (fy+1) << yshift; + line.intersect(line, provided_input); + line.translate(-provided_input.xmin, -provided_input.ymin); + // Prepare variables + const unsigned char *botline = input[line.ymin]; + int rowsize = input.rowsize(); + int sw = 1<<xshift; + int div = xshift+yshift; + int rnd = 1<<(div-1); + // Compute averages + for (int x=line.xmin; x<line.xmax; x+=sw,p++) + { + int g=0, s=0; + const unsigned char *inp0 = botline + x; + int sy1 = mini(line.height(), (1<<yshift)); + for (int sy=0; sy<sy1; sy++,inp0+=rowsize) + { + const unsigned char *inp1; + const unsigned char *inp2 = inp0 + mini(x+sw, line.xmax) - x; + for (inp1=inp0; inp1<inp2; inp1++) + { + g += conv[*inp1]; + s += 1; + } + } + if (s == rnd+rnd) + *p = (g+rnd)>>div; + else + *p = (g+s/2)/s; + } + // Return + return p2; + } +} + + +void +GBitmapScaler::scale( const GRect &provided_input, const GBitmap &input, + const GRect &desired_output, GBitmap &output ) +{ + // Compute rectangles + GRect required_input; + GRect required_red; + make_rectangles(desired_output, required_red, required_input); + // Parameter validation + if (provided_input.width() != (int)input.columns() || + provided_input.height() != (int)input.rows() ) + G_THROW( ERR_MSG("GScaler.no_match") ); + if (provided_input.xmin > required_input.xmin || + provided_input.ymin > required_input.ymin || + provided_input.xmax < required_input.xmax || + provided_input.ymax < required_input.ymax ) + G_THROW( ERR_MSG("GScaler.too_small") ); + // Adjust output pixmap + if (desired_output.width() != (int)output.columns() || + desired_output.height() != (int)output.rows() ) + output.init(desired_output.height(), desired_output.width()); + output.set_grays(256); + // Prepare temp stuff + gp1.resize(0); + gp2.resize(0); + glbuffer.resize(0); + prepare_interp(); + const int bufw = required_red.width(); + glbuffer.resize(bufw+2); + gp1.resize(bufw); + gp2.resize(bufw); + l1 = l2 = -1; + // Prepare gray conversion array (conv) + gconv.resize(0); + gconv.resize(256); + int maxgray = input.get_grays()-1; + for (int i=0; i<256; i++) + { + conv[i]=(i<= maxgray) + ?(((i*255) + (maxgray>>1)) / maxgray) + :255; + } + // Loop on output lines + for (int y=desired_output.ymin; y<desired_output.ymax; y++) + { + // Perform vertical interpolation + { + int fy = vcoord[y]; + int fy1 = fy>>FRACBITS; + int fy2 = fy1+1; + const unsigned char *lower, *upper; + // Obtain upper and lower line in reduced image + lower = get_line(fy1, required_red, provided_input, input); + upper = get_line(fy2, required_red, provided_input, input); + // Compute line + unsigned char *dest = lbuffer+1; + const short *deltas = & interp[fy&FRACMASK][256]; + for(unsigned char const * const edest=(unsigned char const *)dest+bufw; + dest<edest;upper++,lower++,dest++) + { + const int l = *lower; + const int u = *upper; + *dest = l + deltas[u-l]; + } + } + // Perform horizontal interpolation + { + // Prepare for side effects + lbuffer[0] = lbuffer[1]; + lbuffer[bufw] = lbuffer[bufw]; + unsigned char *line = lbuffer+1-required_red.xmin; + unsigned char *dest = output[y-desired_output.ymin]; + // Loop horizontally + for (int x=desired_output.xmin; x<desired_output.xmax; x++) + { + int n = hcoord[x]; + const unsigned char *lower = line + (n>>FRACBITS); + const short *deltas = &interp[n&FRACMASK][256]; + int l = lower[0]; + int u = lower[1]; + *dest = l + deltas[u-l]; + dest++; + } + } + } + // Free temporaries + gp1.resize(0); + gp2.resize(0); + glbuffer.resize(0); + gconv.resize(0); +} + + + + + + +//////////////////////////////////////// +// GPIXMAPSCALER + + +GPixmapScaler::GPixmapScaler() + : glbuffer((void *&)lbuffer,0,sizeof(GPixel)), + gp1((void *&)p1,0,sizeof(GPixel)), + gp2((void *&)p2,0,sizeof(GPixel)) +{ +} + + +GPixmapScaler::GPixmapScaler(int inw, int inh, int outw, int outh) + : glbuffer((void *&)lbuffer,0,sizeof(GPixel)), + gp1((void *&)p1,0,sizeof(GPixel)), + gp2((void *&)p2,0,sizeof(GPixel)) +{ + set_input_size(inw, inh); + set_output_size(outw, outh); +} + + +GPixmapScaler::~GPixmapScaler() +{ +} + + +GPixel * +GPixmapScaler::get_line(int fy, + const GRect &required_red, + const GRect &provided_input, + const GPixmap &input ) +{ + if (fy < required_red.ymin) + fy = required_red.ymin; + else if (fy >= required_red.ymax) + fy = required_red.ymax - 1; + // Cached line + if (fy == l2) + return p2; + if (fy == l1) + return p1; + // Shift + GPixel *p=p1; + p1 = p2; + l1 = l2; + p2 = p; + l2 = fy; + // Compute location of line + GRect line; + line.xmin = required_red.xmin << xshift; + line.xmax = required_red.xmax << xshift; + line.ymin = fy << yshift; + line.ymax = (fy+1) << yshift; + line.intersect(line, provided_input); + line.translate(-provided_input.xmin, -provided_input.ymin); + // Prepare variables + const GPixel *botline = input[line.ymin]; + int rowsize = input.rowsize(); + int sw = 1<<xshift; + int div = xshift+yshift; + int rnd = 1<<(div-1); + // Compute averages + for (int x=line.xmin; x<line.xmax; x+=sw,p++) + { + int r=0, g=0, b=0, s=0; + const GPixel *inp0 = botline + x; + int sy1 = mini(line.height(), (1<<yshift)); + for (int sy=0; sy<sy1; sy++,inp0+=rowsize) + { + const GPixel *inp1; + const GPixel *inp2 = inp0 + mini(x+sw, line.xmax) - x; + for (inp1 = inp0; inp1<inp2; inp1++) + { + r += inp1->r; + g += inp1->g; + b += inp1->b; + s += 1; + } + } + if (s == rnd+rnd) + { + p->r = (r+rnd) >> div; + p->g = (g+rnd) >> div; + p->b = (b+rnd) >> div; + } + else + { + p->r = (r+s/2)/s; + p->g = (g+s/2)/s; + p->b = (b+s/2)/s; + } + } + // Return + return (GPixel *)p2; +} + + +void +GPixmapScaler::scale( const GRect &provided_input, const GPixmap &input, + const GRect &desired_output, GPixmap &output ) +{ + // Compute rectangles + GRect required_input; + GRect required_red; + make_rectangles(desired_output, required_red, required_input); + // Parameter validation + if (provided_input.width() != (int)input.columns() || + provided_input.height() != (int)input.rows() ) + G_THROW( ERR_MSG("GScaler.no_match") ); + if (provided_input.xmin > required_input.xmin || + provided_input.ymin > required_input.ymin || + provided_input.xmax < required_input.xmax || + provided_input.ymax < required_input.ymax ) + G_THROW( ERR_MSG("GScaler.too_small") ); + // Adjust output pixmap + if (desired_output.width() != (int)output.columns() || + desired_output.height() != (int)output.rows() ) + output.init(desired_output.height(), desired_output.width()); + // Prepare temp stuff + gp1.resize(0,sizeof(GPixel)); + gp2.resize(0,sizeof(GPixel)); + glbuffer.resize(0,sizeof(GPixel)); + prepare_interp(); + const int bufw = required_red.width(); + glbuffer.resize(bufw+2,sizeof(GPixel)); + if (xshift>0 || yshift>0) + { + gp1.resize(bufw,sizeof(GPixel)); + gp2.resize(bufw,sizeof(GPixel)); + l1 = l2 = -1; + } + // Loop on output lines + for (int y=desired_output.ymin; y<desired_output.ymax; y++) + { + // Perform vertical interpolation + { + int fy = vcoord[y]; + int fy1 = fy>>FRACBITS; + int fy2 = fy1+1; + const GPixel *lower, *upper; + // Obtain upper and lower line in reduced image + if (xshift>0 || yshift>0) + { + lower = get_line(fy1, required_red, provided_input, input); + upper = get_line(fy2, required_red, provided_input, input); + } + else + { + int dx = required_red.xmin-provided_input.xmin; + fy1 = maxi(fy1, required_red.ymin); + fy2 = mini(fy2, required_red.ymax-1); + lower = input[fy1-provided_input.ymin] + dx; + upper = input[fy2-provided_input.ymin] + dx; + } + // Compute line + GPixel *dest = lbuffer+1; + const short *deltas = & interp[fy&FRACMASK][256]; + for(GPixel const * const edest = (GPixel const *)dest+bufw; + dest<edest;upper++,lower++,dest++) + { + const int lower_r = lower->r; + const int delta_r = deltas[(int)upper->r - lower_r]; + dest->r = lower_r + delta_r; + const int lower_g = lower->g; + const int delta_g = deltas[(int)upper->g - lower_g]; + dest->g = lower_g + delta_g; + const int lower_b = lower->b; + const int delta_b = deltas[(int)upper->b - lower_b]; + dest->b = lower_b + delta_b; + } + } + // Perform horizontal interpolation + { + // Prepare for side effects + lbuffer[0] = lbuffer[1]; + lbuffer[bufw] = lbuffer[bufw]; + GPixel *line = lbuffer+1-required_red.xmin; + GPixel *dest = output[y-desired_output.ymin]; + // Loop horizontally + for (int x=desired_output.xmin; x<desired_output.xmax; x++,dest++) + { + const int n = hcoord[x]; + const GPixel *lower = line + (n>>FRACBITS); + const short *deltas = &interp[n&FRACMASK][256]; + const int lower_r = lower[0].r; + const int delta_r = deltas[(int)lower[1].r - lower_r]; + dest->r = lower_r + delta_r; + const int lower_g = lower[0].g; + const int delta_g = deltas[(int)lower[1].g - lower_g]; + dest->g = lower_g + delta_g; + const int lower_b = lower[0].b; + const int delta_b = deltas[(int)lower[1].b - lower_b]; + dest->b = lower_b + delta_b; + } + } + } + // Free temporaries + gp1.resize(0,sizeof(GPixel)); + gp2.resize(0,sizeof(GPixel)); + glbuffer.resize(0,sizeof(GPixel)); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GScaler.h b/kviewshell/plugins/djvu/libdjvu/GScaler.h new file mode 100644 index 00000000..4843f6d0 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GScaler.h @@ -0,0 +1,321 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GScaler.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GSCALER_H_ +#define _GSCALER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "GException.h" +#include "GRect.h" +#include "GBitmap.h" +#include "GPixmap.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name GScaler.h + + Files #"GScaler.h"# and #"GScaler.cpp"# implement a fast bilinear + interpolation scheme to rescale a \Ref{GBitmap} or a \Ref{GPixmap}. + Common setup functions are implemented by the base class \Ref{GScaler}. + The actual function for rescaling a gray level image is implemented by + class \Ref{GBitmapScaler}. The actual function for rescaling a color + image is implemented by class \Ref{GPixmapScaler}. + + {\bf Remark} --- The bilinear interpolation code relies on fixed precision + tables. It becomes suboptimal when upsampling (i.e. zooming into) an + image by a factor greater than eight. High contrast images displayed at + high magnification may contain visible jaggies. + + @memo + Rescaling images with bilinear interpolation. + @author + L\'eon Bottou <leonb@research.att.com> + @version + #$Id: GScaler.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +/** Base class for GBitmapScaler and GPixmapScaler. This base class + implements the common elements of class \Ref{GBitmapScaler} and + \Ref{GPixmapScaler}. Functions \Ref{set_input_size} and + \Ref{set_output_size} are used to specify the size of the input image and + the size of the output image. Functions \Ref{set_horz_ratio} and + \Ref{set_vert_ratio} may be used to override the scaling ratios computed + from the image sizes. You can then call function \Ref{get_input_rect} to + know which pixels of the input image are necessary to compute a specified + rectangular zone of the output image. The actual computation is then + performed by calling function #scale# in class \Ref{GBitmapScaler} and + \Ref{GPixmapScaler}. +*/ +class GScaler : public GPEnabled +{ +protected: + GScaler(); +public: + virtual ~GScaler(); + /** Sets the size of the input image. Argument #w# (resp. #h#) contains the + horizontal (resp. vertical) size of the input image. This size is used + to initialize the internal data structures of the scaler object. */ + void set_input_size(int w, int h); + /** Sets the size of the output image. Argument #w# (resp. #h#) contains the + horizontal (resp. vertical) size of the output image. This size is used + to initialize the internal data structures of the scaler object. */ + void set_output_size(int w, int h); + /** Sets the horizontal scaling ratio #numer/denom#. This function may be + used to force an exact scaling ratio. The scaling ratios are otherwise + derived from the sizes of the input and output images. */ + void set_horz_ratio(int numer, int denom); + /** Sets the vertical scaling ratio to #numer/denom#. This function may be + used to force an exact scaling ratio. The scaling ratios are otherwise + derived from the sizes of the input and output images. */ + void set_vert_ratio(int numer, int denom); + /** Computes which input pixels are required to compute specified output + pixels. Let us assume that we only need a part of the output + image. This part is defined by rectangle #desired_output#. Only a part + of the input image is necessary to compute the output pixels. Function + #get_input_rect# computes the coordinates of that part of the input + image, and stores them into rectangle #required_input#. */ + void get_input_rect( const GRect &desired_output, GRect &required_input ); +protected: + // The sizes + int inw, inh; + int xshift, yshift; + int redw, redh; + int outw, outh; + // Fixed point coordinates + int *vcoord; + GPBuffer<int> gvcoord; + int *hcoord; + GPBuffer<int> ghcoord; + // Helper + void make_rectangles(const GRect &desired, GRect &red, GRect &inp); +}; + + + +/** Fast rescaling code for gray level images. This class augments the base + class \Ref{GScaler} with a function for rescaling gray level + images. Function \Ref{GBitmapScaler::scale} computes an arbitrary segment + of the output image given the corresponding pixels in the input image. + + {\bf Example} --- The following functions returns an gray level image + (sixteen gray levels, size #nw# by #nh#) containing a rescaled version of + the input image #in#. + \begin{verbatim} + GBitmap *rescale_bitmap(const GBitmap &in, int nw, int nh) + { + int w = in.columns(); // Get input width + int h = in.raws(); // Get output width + GBitmapScaler scaler(w,h,nw,nh); // Creates bitmap scaler + GRect desired(0,0,nw,nh); // Desired output = complete bitmap + GRect provided(0,0,w,h); // Provided input = complete bitmap + GBitmap *out = new GBitmap; + scaler.scale(provided, in, desired, *out); // Rescale + out->change_grays(16); // Reduce to 16 gray levels + return out; + } + \end{verbatim} */ +class GBitmapScaler : public GScaler +{ +protected: + GBitmapScaler(void); + GBitmapScaler(int inw, int inh, int outw, int outh); +public: + /// Virtual destructor. + virtual ~GBitmapScaler(); + + /** Creates an empty GBitmapScaler. You must call functions + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size} before + calling any of the scaling functions. */ + static GP<GBitmapScaler> create(void) {return new GBitmapScaler(); } + + /** Creates a GBitmapScaler. The size of the input image is given by + #inw# and #inh#. This function internally calls + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size}. The + size of the output image is given by #outw# and #outh#. . */ + static GP<GBitmapScaler> create( + const int inw, const int inh, const int outw, const int outh) + { return new GBitmapScaler(inw,inh,outw,outh); } + + /** Computes a segment of the rescaled output image. The GBitmap object + #output# is overwritten with the segment of the output image specified + by the rectangle #desired_output#. The rectangle #provided_input# + specifies which segment of the input image is provided by the GBitmap + object #input#. An exception \Ref{GException} is thrown if the + rectangle #provided_input# is smaller then the rectangle + #required_input# returned by function \Ref{GScaler::get_input_rect}. + Note that the output image always contain 256 gray levels. You may want + to use function \Ref{GBitmap::change_grays} to reduce the number of gray + levels. */ + void scale( const GRect &provided_input, const GBitmap &input, + const GRect &desired_output, GBitmap &output ); +protected: + // Helpers + unsigned char *get_line(int, const GRect &, const GRect &, const GBitmap &); + // Temporaries + unsigned char *lbuffer; + GPBuffer<unsigned char> glbuffer; + unsigned char *conv; + GPBuffer<unsigned char> gconv; + unsigned char *p1; + GPBuffer<unsigned char> gp1; + unsigned char *p2; + GPBuffer<unsigned char> gp2; + int l1; + int l2; +}; + + +/** Fast rescaling code for color images. This class augments the base class + \Ref{GScaler} with a function for rescaling color images. Function + \Ref{GPixmapScaler::scale} computes an arbitrary segment of the output + image given the corresponding pixels in the input image. + + {\bf Example} --- The following functions returns a color image + of size #nw# by #nh# containing a rescaled version of + the input image #in#. + \begin{verbatim} + GPixmap *rescale_pixmap(const GPixmap &in, int nw, int nh) + { + int w = in.columns(); // Get input width + int h = in.raws(); // Get output width + GPixmapScaler scaler(w,h,nw,nh); // Creates bitmap scaler + GRect desired(0,0,nw,nh); // Desired output = complete image + GRect provided(0,0,w,h); // Provided input = complete image + GPixmap *out = new GPixmap; + scaler.scale(provided, in, desired, *out); // Rescale + return out; + } + \end{verbatim} + + */ +class GPixmapScaler : public GScaler +{ +protected: + GPixmapScaler(void); + GPixmapScaler(int inw, int inh, int outw, int outh); +public: + /// Virtual destructor. + virtual ~GPixmapScaler(); + + /** Creates an empty GPixmapScaler. You must call functions + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size} before + calling any of the scaling functions. */ + static GP<GPixmapScaler> create(void) {return new GPixmapScaler(); } + + /** Creates a GPixmapScaler. The size of the input image is given by + #inw# and #inh#. This function internally calls + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size}. The + size of the output image is given by #outw# and #outh#. . */ + static GP<GPixmapScaler> create( + const int inw, const int inh, const int outw, const int outh) + { return new GPixmapScaler(inw,inh,outw,outh); } + + /** Computes a segment of the rescaled output image. The pixmap #output# is + overwritten with the segment of the output image specified by the + rectangle #desired_output#. The rectangle #provided_input# specifies + which segment of the input image is provided in the pixmap #input#. An + exception \Ref{GException} is thrown if the rectangle #provided_input# + is smaller then the rectangle #required_input# returned by function + \Ref{GScaler::get_input_rect}. */ + void scale( const GRect &provided_input, const GPixmap &input, + const GRect &desired_output, GPixmap &output ); +protected: + // Helpers + GPixel *get_line(int, const GRect &, const GRect &, const GPixmap &); + // Temporaries + GPixel *lbuffer; + GPBufferBase glbuffer; + GPixel *p1; + GPBufferBase gp1; + GPixel *p2; + GPBufferBase gp2; + int l1; + int l2; +}; + + + + + +//@} + + + + +// -------- END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GSmartPointer.cpp b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.cpp new file mode 100644 index 00000000..8c17755d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.cpp @@ -0,0 +1,256 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GSmartPointer.cpp,v 1.11 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 05/1997 + +// From: Leon Bottou, 1/31/2002 +// Class GPBuffer has been added (but not documented) by Lizardtech. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + +#include <string.h> +#include "GThreads.h" +#include "GSmartPointer.h" +#include "GException.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ------ STATIC CRITICAL SECTION + +static GCriticalSection gcsCounter; + + +// ------ GPENABLED + + +GPEnabled::~GPEnabled() +{ + if (count > 0) + G_THROW( ERR_MSG("GSmartPointer.suspicious") ); +} + +void +GPEnabled::destroy() +{ + if (count >= 0) + G_THROW( ERR_MSG("GSmartPointer.suspicious") ); + delete this; +} + +void +GPEnabled::ref() +{ + gcsCounter.lock(); + count++; + gcsCounter.unlock(); +} + +void +GPEnabled::unref() +{ + gcsCounter.lock(); + if (! --count) + count = -1; + gcsCounter.unlock(); + if (count < 0) + destroy(); +} + + +// ------ GPBASE + + +GPBase& +GPBase::assign (GPEnabled *nptr) +{ + gcsCounter.lock(); + if (nptr) + { + if (nptr->count >= 0) + nptr->count++; + else + nptr = 0; + } + if (ptr) + { + GPEnabled *old = ptr; + ptr = nptr; + if (! --old->count) + old->count = -1; + gcsCounter.unlock(); + if (old->count < 0) + old->destroy(); + } + else + { + ptr = nptr; + gcsCounter.unlock(); + } + return *this; +} + +GPBase& +GPBase::assign (const GPBase &sptr) +{ + gcsCounter.lock(); + if (sptr.ptr) + { + sptr.ptr->count++; + } + if (ptr) + { + GPEnabled *old = ptr; + ptr = sptr.ptr; + if (! --old->count) + old->count = -1; + gcsCounter.unlock(); + if (old->count < 0) + old->destroy(); + } + else + { + ptr = sptr.ptr; + gcsCounter.unlock(); + } + return *this; +} + + +// ------ GPBUFFERBASE + + +void +GPBufferBase::replace(void *nptr,const size_t n) +{ + resize(0,0); + ptr=nptr; + num=n; +} + +GPBufferBase::GPBufferBase(void *&xptr,const size_t n,const size_t t) + : ptr(xptr), num(n) +{ + if (n) + xptr = ::operator new(n*t); + else + xptr = 0; +} + +GPBufferBase::~GPBufferBase() +{ + ::operator delete(ptr); +} + +void +GPBufferBase::swap(GPBufferBase &other) +{ + void * const temp_ptr=ptr; + ptr=other.ptr; + other.ptr=temp_ptr; + const size_t temp_num=num; + num=other.num; + other.num=temp_num; +} + +void +GPBufferBase::resize(const size_t n, const size_t t) +{ + if(!n && !ptr) + { + num=0; + } + else + { + const size_t s=ptr?(((num<n)?num:n)*t):0; + void *nptr; + GPBufferBase gnptr(nptr, n, t); + if(s) + { + memcpy(nptr, ptr, s); + } + swap(gnptr); + } +} + +void +GPBufferBase::set(const size_t t,const char c) +{ + if(num) + memset(ptr,c,num*t); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GSmartPointer.h b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.h new file mode 100644 index 00000000..04b79ecf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.h @@ -0,0 +1,489 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GSmartPointer.h,v 1.11 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GSMARTPOINTER_H_ +#define _GSMARTPOINTER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GSmartPointer.h + + Files #"GSmartPointer.h"# and #"GSmartPointer.cpp"# define a smart-pointer + class which automatically performs thread-safe reference counting. Class + \Ref{GP} implements smart-pointers by overloading the usual pointer + assignment and dereferencing operators. The overloaded operators maintain + the reference counters and destroy the pointed objects as soon as their + reference counter reaches zero. Transparent type conversions are provided + between smart-pointers and regular pointers. Objects referenced by + smart-pointers must be derived from class \Ref{GPEnabled}. + + @memo + Thread-Safe reference counting smart-pointers. + @author + L\'eon Bottou <leonb@research.att.com> -- initial implementation\\ + Andrei Erofeev <eaf@geocities.com> -- bug fix. + +// From: Leon Bottou, 1/31/2002 +// Class GPBuffer has been added (but not documented) by Lizardtech. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + + @version + #$Id: GSmartPointer.h,v 1.11 2003/11/07 22:08:21 leonb Exp $# + @args +*/ +//@{ + +#if defined(_MSC_VER) +// Language lawyer say MSVC6 is wrong on that one. +// Cf section 5.4.7 in november 1997 draft. +#pragma warning( disable : 4243 ) +#endif + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/* What is this innovation ? + What does it do that a GArray does not do ? */ + +class GPBufferBase +{ +public: + GPBufferBase(void *&,const size_t n,const size_t t); + void swap(GPBufferBase &p); + void resize(const size_t n,const size_t t); + void replace(void *nptr,const size_t n); + void set(const size_t t,const char c); + ~GPBufferBase(); + operator int(void) const { return ptr ? num : 0; } +private: + void *&ptr; + size_t num; +}; + +template<class TYPE> +class GPBuffer : public GPBufferBase +{ +public: + GPBuffer(TYPE *&xptr,const size_t n=0) : GPBufferBase((void *&)xptr,n,sizeof(TYPE)) {} + inline void resize(const size_t n) {GPBufferBase::resize(n,sizeof(TYPE));} + inline void clear(void) {GPBufferBase::set(sizeof(TYPE),0);} + inline void set(const char c) {GPBufferBase::set(sizeof(TYPE),c);} + inline operator int(void) const {return GPBufferBase::operator int();} +}; + +/** Base class for reference counted objects. + This is the base class for all reference counted objects. + Any instance of a subclass of #GPEnabled# can be used with + smart-pointers (see \Ref{GP}). + */ +class GPEnabled +{ +public: + /// Null constructor. + GPEnabled(); + /// Copy construcotr + GPEnabled(const GPEnabled & obj); + /// Virtual destructor. + virtual ~GPEnabled(); + /// Copy operator + GPEnabled & operator=(const GPEnabled & obj); + /** Returns the number of references to this object. This should be only + used for debugging purposes. Other uses are not thread-safe. */ + int get_count(void) const; +protected: + /// The reference counter + volatile int count; +private: + friend class GPBase; + void unref(); + void ref(); + void destroy(); +}; + + + +/** Base class for all smart-pointers. + This class implements common mechanisms for all + smart-pointers (see \Ref{GP}). There should be no need + to use this class directly. Its sole purpose consists + in reducing the template expansion overhead. +*/ + +class GPBase +{ +public: + /** Null Constructor. */ + GPBase(); + /** Copy Constructor. + Increments the reference count. + @param sptr reference to a #GPBase# object. */ + GPBase(const GPBase &sptr); + /** Construct a GPBase from a pointer. + Increments the reference count. + @param nptr pointer to a #GPEnabled# object. */ + GPBase(GPEnabled *nptr); + /** Destructor. Decrements the reference count. */ + ~GPBase(); + /** Accesses the actual pointer. */ + GPEnabled* get() const; + /** Assignment from smartpointer. + Increments the counter of the new value of the pointer. + Decrements the counter of the previous value of the pointer. */ + GPBase& assign(const GPBase &sptr); + /** Assignment from pointer. + Checks that the object is not being destroyed. + Increments the counter of the new value of the pointer. + Decrements the counter of the previous value of the pointer. */ + GPBase& assign(GPEnabled *nptr); + /** Assignment operator. */ + GPBase & operator=(const GPBase & obj); + /** Comparison operator. */ + int operator==(const GPBase & g2) const; +protected: + /** Actual pointer */ + GPEnabled *ptr; +}; + + +/** Reference counting pointer. + Class #GP<TYPE># represents a smart-pointer to an object of type #TYPE#. + Type #TYPE# must be a subclass of #GPEnabled#. This class overloads the + usual pointer assignment and dereferencing operators. The overloaded + operators maintain the reference counters and destroy the pointed object + as soon as their reference counter reaches zero. Transparent type + conversions are provided between smart-pointers and regular pointers. + + Using a smart-pointer is a convenience and not an obligation. There is no + need to use a smart-pointer to access a #GPEnabled# object. As long as + you never use a smart-pointer to access a #GPEnabled# object, its + reference counter remains zero. Since the reference counter is never + decremented from one to zero, the object is never destroyed by the + reference counting code. You can therefore choose to only use regular + pointers to access objects allocated on the stack (automatic variables) or + objects allocated dynamically. In the latter case you must explicitly + destroy the dynamically allocated object with operator #delete#. + + The first time you use a smart-pointer to access #GPEnabled# object, the + reference counter is incremented to one. Object destruction will then + happen automatically when the reference counter is decremented back to + zero (i.e. when the last smart-pointer referencing this object stops doing so). + This will happen regardless of how many regular pointers reference this object. + In other words, if you start using smart-pointers with a #GPEnabled# + object, you engage automatic mode for this object. You should only do + this with objects dynamically allocated with operator #new#. You should + never destroy the object yourself, but let the smart-pointers control the + life of the object. + + {\bf Performance considerations} --- Thread safe reference counting incurs + a significant overhead. Smart-pointer are best used with sizeable objects + for which the cost of maintaining the counters represent a small fraction + of the processing time. It is always possible to cache a smart-pointer + into a regular pointer. The cached pointer will remain valid until the + smart-pointer object is destroyed or the smart-pointer value is changed. + + {\bf Safety considerations} --- As explained above, a #GPEnabled# object + switches to automatic mode as soon as it becomes referenced by a + smart-pointer. There is no way to switch the object back to manual mode. + Suppose that you have decided to only use regular pointers with a + particular #GPEnabled# object. You therefore plan to destroy the object + explicitly when you no longer need it. When you pass a regular pointer to + this object as argument to a function, you really need to be certain that + the function implementation will not assign this pointer to a + smart-pointer. Doing so would indeed destroy the object as soon as the + function returns. The bad news is that the fact that a function assigns a + pointer argument to a smart-pointer does not necessarily appear in the + function prototype. Such a behavior must be {\em documented} with the + function public interface. As a convention, we usually write such + functions with smart-pointer arguments instead of a regular pointer + arguments. This is not enough to catch the error at compile time, but + this is a simple way to document such a behavior. We still believe that + this is a small problem in regard to the benefits of the smart-pointer. + But one has to be aware of its existence. */ + +template <class TYPE> +class GP : protected GPBase +{ +public: + /** Constructs a null smart-pointer. */ + GP(); + /** Constructs a copy of a smart-pointer. + @param sptr smart-pointer to copy. */ + GP(const GP<TYPE> &sptr); + /** Constructs a smart-pointer from a regular pointer. + The pointed object must be dynamically allocated (with operator #new#). + You should no longer explicitly destroy the object referenced by #sptr# + since the object life is now controlled by smart-pointers. + @param nptr regular pointer to a {\em dynamically allocated object}. */ + GP(TYPE *nptr); + /** Converts a smart-pointer into a regular pointer. + This is useful for caching the value of a smart-pointer for performances + purposes. The cached pointer will remain valid until the smart-pointer + is destroyed or until the smart-pointer value is changed. */ + operator TYPE* () const; + /** Assigns a regular pointer to a smart-pointer lvalue. + The pointed object must be dynamically allocated (with operator #new#). + You should no longer explicitly destroy the object referenced by #sptr# + since the object life is now controlled by smart-pointers. + @param nptr regular pointer to a {\em dynamically allocated object}. */ + GP<TYPE>& operator= (TYPE *nptr); + /** Assigns a smart-pointer to a smart-pointer lvalue. + @param sptr smart-pointer copied into this smart-pointer. */ + GP<TYPE>& operator= (const GP<TYPE> &sptr); + /** Indirection operator. + This operator provides a convenient access to the members + of a smart-pointed object. Operator #-># works with smart-pointers + exactly as with regular pointers. */ + TYPE* operator->() const; + /** Dereferencement operator. + This operator provides a convenient access to the smart-pointed object. + Operator #*# works with smart-pointers exactly as with regular pointers. */ + TYPE& operator*() const; + /** Comparison operator. + Returns true if both this smart-pointer and pointer #nptr# point to the + same object. The automatic conversion from smart-pointers to regular + pointers allows you to compare two smart-pointers as well. + @param nptr pointer to compare with. */ + int operator== (TYPE *nptr) const; + /** Comparison operator. + Returns true if this smart-pointer and pointer #nptr# point to different + objects. The automatic conversion from smart-pointers to regular + pointers allows you to compare two smart-pointers as well. + @param nptr pointer to compare with. */ + int operator!= (TYPE *nptr) const; + /** Test operator. + Returns true if the smart-pointer is null. The automatic conversion + from smart-pointers to regular pointers allows you to test whether + a smart-pointer is non-null. You can use both following constructs: + \begin{verbatim} + if (gp) { ... } + while (! gp) { ... } + \end{verbatim} */ + int operator! () const; +}; + +//@} + +// INLINE FOR GPENABLED + +inline +GPEnabled::GPEnabled() + : count(0) +{ +} + +inline +GPEnabled::GPEnabled(const GPEnabled & obj) + : count(0) +{ + +} + +inline int +GPEnabled::get_count(void) const +{ + return count; +} + +inline GPEnabled & +GPEnabled::operator=(const GPEnabled & obj) +{ + /* The copy operator should do nothing because the count should not be + changed. Subclasses of GPEnabled will call this version of the copy + operator as part of the default 'memberwise copy' strategy. */ + return *this; +} + +// INLINE FOR GPBASE + +inline +GPBase::GPBase() + : ptr(0) +{ +} + +inline +GPBase::GPBase(GPEnabled *nptr) + : ptr(0) +{ + assign(nptr); +} + +inline +GPBase::GPBase(const GPBase &sptr) +{ + if (sptr.ptr) + sptr.ptr->ref(); + ptr = sptr.ptr; +} + +inline +GPBase::~GPBase() +{ + GPEnabled *old = ptr; + ptr = 0; + if (old) + old->unref(); +} + +inline GPEnabled* +GPBase::get() const +{ + return ptr; +} + +inline GPBase & +GPBase::operator=(const GPBase & obj) +{ + return assign(obj); +} + +inline int +GPBase::operator==(const GPBase & g2) const +{ + return ptr == g2.ptr; +} + + + + +// INLINE FOR GP<TYPE> + +template <class TYPE> inline +GP<TYPE>::GP() +{ +} + +template <class TYPE> inline +GP<TYPE>::GP(TYPE *nptr) +: GPBase((GPEnabled*)nptr) +{ +} + +template <class TYPE> inline +GP<TYPE>::GP(const GP<TYPE> &sptr) +: GPBase((const GPBase&) sptr) +{ +} + +template <class TYPE> inline +GP<TYPE>::operator TYPE* () const +{ + return (TYPE*) ptr; +} + +template <class TYPE> inline TYPE* +GP<TYPE>::operator->() const +{ + return (TYPE*) ptr; +} + +template <class TYPE> inline TYPE& +GP<TYPE>::operator*() const +{ + return *(TYPE*) ptr; +} + +template <class TYPE> inline GP<TYPE>& +GP<TYPE>::operator= (TYPE *nptr) +{ + return (GP<TYPE>&)( assign(nptr) ); +} + +template <class TYPE> inline GP<TYPE>& +GP<TYPE>::operator= (const GP<TYPE> &sptr) +{ + return (GP<TYPE>&)( assign((const GPBase&)sptr) ); +} + +template <class TYPE> inline int +GP<TYPE>::operator== (TYPE *nptr) const +{ + return ( (TYPE*)ptr == nptr ); +} + +template <class TYPE> inline int +GP<TYPE>::operator!= (TYPE *nptr) const +{ + return ( (TYPE*)ptr != nptr ); +} + +template <class TYPE> inline int +GP<TYPE>::operator! () const +{ + return !ptr; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GString.cpp b/kviewshell/plugins/djvu/libdjvu/GString.cpp new file mode 100644 index 00000000..a618055e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GString.cpp @@ -0,0 +1,2811 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GString.cpp,v 1.22 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// My original implementation was very small in comparison +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. +// In my opinion, the duplication of the string classes is a failed +// attempt to use the type system to enforce coding policies. +// This could be fixed. But there are better things to do in djvulibre. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GString.h" +#include "GThreads.h" +#include "debug.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#if HAS_WCHAR +# include <locale.h> +# if !defined(AUTOCONF) || HAVE_WCHAR_H +# include <wchar.h> +# endif +# if HAS_WCTYPE +# include <wctype.h> +# endif +#endif +#include <ctype.h> + +#ifndef DO_CHANGELOCALE +#define DO_CHANGELOCALE 1 +#ifdef UNIX +#if THREADMODEL != COTHREADS +#if THREADMODEL != NOTHREADS +#undef DO_CHANGELOCALE +#define DO_CHANGELOCALE 0 +#endif +#endif +#endif +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GBaseString::~GBaseString() {} +GNativeString::~GNativeString() {} +GUTF8String::~GUTF8String() {} + +#if !HAS_MBSTATE && HAS_WCHAR +// Under some systems, wctomb() and mbtowc() are not thread +// safe. In those cases, wcrtomb and mbrtowc are preferred. +// For Solaris, wctomb() and mbtowc() are thread safe, and +// wcrtomb() and mbrtowc() don't exist. + +#define wcrtomb MYwcrtomb +#define mbrtowc MYmbrtowc +#define mbrlen MYmbrlen + +static inline int +wcrtomb(char *bytes,wchar_t w,mbstate_t *) +{ + return wctomb(bytes,w); +} + +static inline int +mbrtowc(wchar_t *w,const char *source, size_t n, mbstate_t *) +{ + return mbtowc(w,source,n); +} + +static inline size_t +mbrlen(const char *s, size_t n, mbstate_t *) +{ + return mblen(s,n); +} +#endif // !HAS_MBSTATE || HAS_WCHAR + + +GP<GStringRep> +GStringRep::upcase(void) const +{ return tocase(giswupper,gtowupper); } + +GP<GStringRep> +GStringRep::downcase(void) const +{ return tocase(giswlower,gtowlower); } + +GP<GStringRep> +GStringRep::UTF8::create(const unsigned int sz) +{ + return GStringRep::create(sz,(GStringRep::UTF8 *)0); +} + +GP<GStringRep> +GStringRep::UTF8::create(const char *s) +{ + GStringRep::UTF8 dummy; + return dummy.strdup(s); +} + +GP<GStringRep> +GStringRep::UTF8::create(const GP<GStringRep> &s1,const GP<GStringRep> &s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create( const GP<GStringRep> &s1,const char *s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create( const char *s1, const GP<GStringRep> &s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create( const char *s1,const char *s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create(const char *s,const int start,const int length) +{ + GStringRep::UTF8 dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::UTF8::create( + const unsigned short *s,const int start,const int length) +{ + GStringRep::UTF8 dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::UTF8::create( + const unsigned long *s,const int start,const int length) +{ + GStringRep::UTF8 dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::UTF8::blank(const unsigned int sz) const +{ + return GStringRep::create(sz,(GStringRep::UTF8 *)0); +} + +bool +GStringRep::UTF8::isUTF8(void) const +{ + return true; +} + +GP<GStringRep> +GStringRep::UTF8::toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const +{ + return rep?(rep->toUTF8(true)):rep; +} + +GP<GStringRep> +GStringRep::UTF8::create(const char fmt[],va_list& args) +{ + const GP<GStringRep> s(create(fmt)); + return (s?(s->vformat(args)):s); +} + +#if !HAS_WCHAR + +#define NATIVE_CREATE(x) UTF8::create( x ); + +#ifdef LC_ALL +#undef LC_ALL +#endif +#define LC_ALL 0 + +class GStringRep::ChangeLocale +{ +public: + ChangeLocale(const int,const char *) {} + ~ChangeLocale() {}; +}; + +GP<GStringRep> +GStringRep::NativeToUTF8( const char *s ) +{ + return GStringRep::UTF8::create(s); +} + +#else + +#define NATIVE_CREATE(x) Native::create( x ); + +// The declaration and implementation of GStringRep::ChangeLocale +// Not used in WinCE + +class GStringRep::ChangeLocale +{ +public: + ChangeLocale(const int category,const char locale[]); + ~ChangeLocale(); +private: + GUTF8String locale; + int category; +}; + +class GStringRep::Native : public GStringRep +{ +public: + // default constructor + Native(void); + // virtual destructor + virtual ~Native(); + + // Other virtual methods. + // Create an empty string. + virtual GP<GStringRep> blank(const unsigned int sz = 0) const; + // Append a string. + virtual GP<GStringRep> append(const GP<GStringRep> &s2) const; + // Test if Native. + virtual bool isNative(void) const; + // Convert to Native. + virtual GP<GStringRep> toNative( + const EscapeMode escape=UNKNOWN_ESCAPED) const; + // Convert to UTF8. + virtual GP<GStringRep> toUTF8(const bool nothrow=false) const; + // Convert to UTF8. + virtual GP<GStringRep> toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const; + // Compare with #s2#. + virtual int cmp(const GP<GStringRep> &s2, const int len=(-1)) const; + + // Convert strings to numbers. + virtual int toInt(void) const; + virtual long toLong( + const int pos, int &endpos, const int base=10) const; + virtual unsigned long toULong( + const int pos, int &endpos, const int base=10) const; + virtual double toDouble( + const int pos, int &endpos) const; + + // Create an empty string + static GP<GStringRep> create(const unsigned int sz = 0); + + // Create a strdup string. + static GP<GStringRep> create(const char *s); + + // Creates by appending to the current string + + // Creates with a concat operation. + static GP<GStringRep> create( + const GP<GStringRep> &s1,const GP<GStringRep> &s2); + static GP<GStringRep> create( const GP<GStringRep> &s1,const char *s2); + static GP<GStringRep> create( const char *s1, const GP<GStringRep> &s2); + static GP<GStringRep> create(const char *s1,const char *s2); + + // Create with a strdup and substr operation. + static GP<GStringRep> create( + const char *s,const int start,const int length=(-1)); + static GP<GStringRep> create( + const unsigned short *s,const int start,const int length=(-1)); + static GP<GStringRep> create( + const unsigned long *s,const int start,const int length=(-1)); + + // Create with an sprintf() + static GP<GStringRep> create_format(const char fmt[],...); + static GP<GStringRep> create(const char fmt[],va_list &args); + + virtual unsigned char *UCS4toString( + const unsigned long w,unsigned char *ptr, mbstate_t *ps=0) const; + + // Tests if a string is legally encoded in the current character set. + virtual bool is_valid(void) const; + + virtual int ncopy(wchar_t * const buf, const int buflen) const; + + friend class GBaseString; +protected: + // Return the next character and increment the source pointer. + virtual unsigned long getValidUCS4(const char *&source) const; +}; + +GP<GStringRep> +GStringRep::Native::create(const unsigned int sz) +{ + return GStringRep::create(sz,(GStringRep::Native *)0); +} + + // Create a strdup string. +GP<GStringRep> +GStringRep::Native::create(const char *s) +{ + GStringRep::Native dummy; + return dummy.strdup(s); +} + +GP<GStringRep> +GStringRep::Native::create(const GP<GStringRep> &s1,const GP<GStringRep> &s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create( const GP<GStringRep> &s1,const char *s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create( const char *s1, const GP<GStringRep> &s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create(const char *s1,const char *s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create( + const char *s,const int start,const int length) +{ + GStringRep::Native dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::Native::create( + const unsigned short *s,const int start,const int length) +{ + GStringRep::Native dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::Native::create( + const unsigned long *s,const int start,const int length) +{ + GStringRep::Native dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::Native::blank(const unsigned int sz) const +{ + return GStringRep::create(sz,(GStringRep::Native *)0); +} + +bool +GStringRep::Native::isNative(void) const +{ + return true; +} + +GP<GStringRep> +GStringRep::Native::toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const +{ + return rep?(rep->toNative(NOT_ESCAPED)):rep; +} + +GP<GStringRep> +GStringRep::Native::create(const char fmt[],va_list &args) +{ + const GP<GStringRep> s(create(fmt)); + return (s?(s->vformat(args)):s); +} + +int +GStringRep::Native::ncopy( + wchar_t * const buf, const int buflen ) const +{ + return toUTF8()->ncopy(buf,buflen); +} + +GStringRep::ChangeLocale::ChangeLocale(const int xcategory, const char xlocale[] ) + : category(xcategory) +{ +#if DO_CHANGELOCALE + // This is disabled under UNIX because + // it does not play nice with MT. + if(xlocale) + { + locale=setlocale(xcategory,0); + if(locale.length() &&(locale!=xlocale)) + { + if(locale == setlocale(category,xlocale)) + { + locale.empty(); + } + } + else + { + locale.empty(); + } + } +#endif +} + +GStringRep::ChangeLocale::~ChangeLocale() +{ +#if DO_CHANGELOCALE + if(locale.length()) + { + setlocale(category,(const char *)locale); + } +#endif +} + +GNativeString & +GNativeString::format(const char fmt[], ... ) +{ + va_list args; + va_start(args, fmt); + return init(GStringRep::Native::create(fmt,args)); +} + +// Gather the native implementations here. Not used in WinCE. + +GStringRep::Native::Native(void) {} +GStringRep::Native::~Native() {} + +GP<GStringRep> +GStringRep::Native::append(const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s2) + { + if(s2->isUTF8()) + { + G_THROW( ERR_MSG("GStringRep.appendUTF8toNative") ); + } + retval=concat(data,s2->data); + }else + { + retval=const_cast<GStringRep::Native *>(this); + } + return retval; +} + +GP<GStringRep> +GStringRep::Native::create_format(const char fmt[],...) +{ + va_list args; + va_start(args, fmt); + return create(fmt,args); +} + +unsigned char * +GStringRep::Native::UCS4toString( + const unsigned long w0,unsigned char *ptr, mbstate_t *ps) const +{ + return UCS4toNative(w0,ptr,ps); +} + +// Convert a UCS4 to a multibyte string in the value bytes. +// The data pointed to by ptr should be long enough to contain +// the results with a nill termination. (Normally 7 characters +// is enough.) +unsigned char * +GStringRep::UCS4toNative( + const unsigned long w0,unsigned char *ptr, mbstate_t *ps) +{ + unsigned short w1; + unsigned short w2=1; + for(int count=(sizeof(wchar_t)==sizeof(w1)) ? UCS4toUTF16(w0,w1,w2) : 1; + count; + --count,w1=w2) + { + // wchar_t can be either UCS4 or UCS2 + const wchar_t w=(sizeof(wchar_t) == sizeof(w1))?(wchar_t)w1:(wchar_t)w0; + int i=wcrtomb((char *)ptr,w,ps); + if(i<0) + { + break; + } + ptr[i]=0; + ptr += i; + } + ptr[0]=0; + return ptr; +} + +GP<GStringRep> +GStringRep::Native::toNative(const EscapeMode escape) const +{ + if(escape == UNKNOWN_ESCAPED) + G_THROW( ERR_MSG("GStringRep.NativeToNative") ); + return const_cast<GStringRep::Native *>(this); +} + +GP<GStringRep> +GStringRep::Native::toUTF8(const bool) const +{ + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,size*6+1); + buf[0]=0; + if(data && size) + { + size_t n=size; + const char *source=data; + mbstate_t ps; + unsigned char *ptr=buf; + //(void)mbrlen(source, n, &ps); + memset(&ps,0,sizeof(mbstate_t)); + int i=0; + if(sizeof(wchar_t) == sizeof(unsigned long)) + { + wchar_t w = 0; + for(;(n>0)&&((i=mbrtowc(&w,source,n,&ps))>=0); n-=i,source+=i) + { + ptr=UCS4toUTF8(w,ptr); + } + } + else + { + wchar_t w = 0; + for(;(n>0)&&((i=mbrtowc(&w,source,n,&ps))>=0);n-=i,source+=i) + { + unsigned short s[2]; + s[0]=w; + unsigned long w0; + if(UTF16toUCS4(w0,s,s+1)<=0) + { + source+=i; + n-=i; + if((n>0)&&((i=mbrtowc(&w,source,n,&ps))>=0)) + { + s[1]=w; + if(UTF16toUCS4(w0,s,s+2)<=0) + { + i=(-1); + break; + } + } + else + { + i=(-1); + break; + } + } + ptr=UCS4toUTF8(w0,ptr); + } + } + if(i<0) + { + gbuf.resize(0); + } + else + { + ptr[0]=0; + } + } + return GStringRep::UTF8::create((const char *)buf); +} + +GNativeString +GBaseString::UTF8ToNative( + const bool currentlocale,const EscapeMode escape) const +{ + const char *source=(*this); + GP<GStringRep> retval; + if(source && source[0]) + { +#if DO_CHANGELOCALE + GUTF8String lc_ctype(setlocale(LC_CTYPE,0)); +#endif + bool repeat; + for(repeat=!currentlocale;;repeat=false) + { + retval=(*this)->toNative((GStringRep::EscapeMode)escape); +#if DO_CHANGELOCALE + if (!repeat || retval || (lc_ctype == setlocale(LC_CTYPE,""))) +#endif + break; + } +#if DO_CHANGELOCALE + if(!repeat) + { + setlocale(LC_CTYPE,(const char *)lc_ctype); + } +#endif + } + return GNativeString(retval); +} + +/*MBCS*/ +GNativeString +GBaseString::getUTF82Native( const EscapeMode escape ) const +{ //MBCS cvt + GNativeString retval; + + // We don't want to convert this if it + // already is known to be native... +// if (isNative()) return *this; + + const size_t slen=length()+1; + if(slen>1) + { + retval=UTF8ToNative(false,escape) ; + if(!retval.length()) + { + retval=(const char*)*this; + } + } + return retval; +} + +GUTF8String +GBaseString::NativeToUTF8(void) const +{ + GP<GStringRep> retval; + if(length()) + { + const char *source=(*this); +#if DO_CHANGELOCALE + GUTF8String lc_ctype=setlocale(LC_CTYPE,0); +#endif + bool repeat; + for(repeat=true;;repeat=false) + { + if( (retval=GStringRep::NativeToUTF8(source)) ) + { + if(GStringRep::cmp(retval->toNative(),source)) + { + retval=GStringRep::UTF8::create((unsigned int)0); + } + } +#if DO_CHANGELOCALE + if(!repeat || retval || (lc_ctype == setlocale(LC_CTYPE,""))) +#endif + break; + } +#if DO_CHANGELOCALE + if(!repeat) + { + setlocale(LC_CTYPE,(const char *)lc_ctype); + } +#endif + } + return GUTF8String(retval); +} + +GUTF8String +GBaseString::getNative2UTF8(void) const +{ //MBCS cvt + + // We don't want to do a transform this + // if we already are in the given type. +// if (isUTF8()) return *this; + + const size_t slen=length()+1; + GUTF8String retval; + if(slen > 1) + { + retval=NativeToUTF8(); + if(!retval.length()) + { + retval=(const char *)(*this); + } + } + return retval; +} /*MBCS*/ + +int +GStringRep::Native::cmp(const GP<GStringRep> &s2,const int len) const +{ + int retval; + if(s2) + { + if(s2->isUTF8()) + { + const GP<GStringRep> r(toUTF8(true)); + if(r) + { + retval=GStringRep::cmp(r->data,s2->data,len); + }else + { + retval=cmp(s2->toNative(NOT_ESCAPED),len); + } + }else + { + retval=GStringRep::cmp(data,s2->data,len); + } + }else + { + retval=GStringRep::cmp(data,0,len); + } + return retval; +} + +int +GStringRep::Native::toInt() const +{ + return atoi(data); +} + +long +GStringRep::Native::toLong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + const long retval=strtol(data+pos, &edata, base); + if(edata) + { + endpos=(int)((size_t)edata-(size_t)data); + }else + { + endpos=(-1); + } + return retval; +} + +unsigned long +GStringRep::Native::toULong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + const unsigned long retval=strtoul(data+pos, &edata, base); + if(edata) + { + endpos=(int)((size_t)edata-(size_t)data); + }else + { + endpos=(-1); + } + return retval; +} + +double +GStringRep::Native::toDouble( + const int pos, int &endpos) const +{ + char *edata=0; + const double retval=strtod(data+pos, &edata); + if(edata) + { + endpos=(int)((size_t)edata-(size_t)data); + }else + { + endpos=(-1); + } + return retval; +} + +unsigned long +GStringRep::Native::getValidUCS4(const char *&source) const +{ + unsigned long retval=0; + int n=(int)((size_t)size+(size_t)data-(size_t)source); + if(source && (n > 0)) + { + mbstate_t ps; + //(void)mbrlen(source, n, &ps); + memset(&ps,0,sizeof(mbstate_t)); + wchar_t wt; + const int len=mbrtowc(&wt,source,n,&ps); + if(len>=0) + { + if(sizeof(wchar_t) == sizeof(unsigned short)) + { + source+=len; + unsigned short s[2]; + s[0]=(unsigned short)wt; + if(UTF16toUCS4(retval,s,s+1)<=0) + { + if((n-=len)>0) + { + const int len=mbrtowc(&wt,source,n,&ps); + if(len>=0) + { + s[1]=(unsigned short)wt; + unsigned long w; + if(UTF16toUCS4(w,s,s+2)>0) + { + source+=len; + retval=w; + } + } + } + } + }else + { + retval=(unsigned long)wt; + source++; + } + }else + { + source++; + } + } + return retval; +} + +// Tests if a string is legally encoded in the current character set. +bool +GStringRep::Native::is_valid(void) const +{ + bool retval=true; + if(data && size) + { + size_t n=size; + const char *s=data; + mbstate_t ps; + //(void)mbrlen(s, n, &ps); + memset(&ps,0,sizeof(mbstate_t)); + do + { + size_t m=mbrlen(s,n,&ps); + if(m > n) + { + retval=false; + break; + }else if(m) + { + s+=m; + n-=m; + }else + { + break; + } + } while(n); + } + return retval; +} + +// These are dummy functions. +void +GStringRep::set_remainder(void const * const, const unsigned int, + const EncodeType) {} +void +GStringRep::set_remainder(void const * const, const unsigned int, + const GP<GStringRep> &encoding) {} +void +GStringRep::set_remainder( const GP<GStringRep::Unicode> &) {} + +GP<GStringRep::Unicode> +GStringRep::get_remainder( void ) const +{ + return 0; +} + +GNativeString::GNativeString(const char dat) +{ + init(GStringRep::Native::create(&dat,0,1)); +} + +GNativeString::GNativeString(const char *str) +{ + init(GStringRep::Native::create(str)); +} + +GNativeString::GNativeString(const unsigned char *str) +{ + init(GStringRep::Native::create((const char *)str)); +} + +GNativeString::GNativeString(const unsigned short *str) +{ + init(GStringRep::Native::create(str,0,-1)); +} + +GNativeString::GNativeString(const unsigned long *str) +{ + init(GStringRep::Native::create(str,0,-1)); +} + +GNativeString::GNativeString(const char *dat, unsigned int len) +{ + init( + GStringRep::Native::create(dat,0,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const unsigned short *dat, unsigned int len) +{ + init( + GStringRep::Native::create(dat,0,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const unsigned long *dat, unsigned int len) +{ + init( + GStringRep::Native::create(dat,0,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const GNativeString &str) +{ + init(str); +} + +GNativeString::GNativeString(const GBaseString &gs, int from, int len) +{ + init( + GStringRep::Native::create(gs,from,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const int number) +{ + init(GStringRep::Native::create_format("%d",number)); +} + +GNativeString::GNativeString(const double number) +{ + init(GStringRep::Native::create_format("%f",number)); +} + +GNativeString& +GNativeString::operator= (const char str) +{ return init(GStringRep::Native::create(&str,0,1)); } + +GNativeString& +GNativeString::operator= (const char *str) +{ return init(GStringRep::Native::create(str)); } + +GNativeString +GBaseString::operator+(const GNativeString &s2) const +{ + return GStringRep::Native::create(*this,s2); +} + +GP<GStringRep> +GStringRep::NativeToUTF8( const char *s ) +{ + return GStringRep::Native::create(s)->toUTF8(); +} + +#endif // HAS_WCHAR + +template <class TYPE> +GP<GStringRep> +GStringRep::create(const unsigned int sz, TYPE *) +{ + GP<GStringRep> gaddr; + if (sz > 0) + { + GStringRep *addr; + gaddr=(addr=new TYPE); + addr->data=(char *)(::operator new(sz+1)); + addr->size = sz; + addr->data[sz] = 0; + } + return gaddr; +} + +GP<GStringRep> +GStringRep::strdup(const char *s) const +{ + GP<GStringRep> retval; + const int length=s?strlen(s):0; + if(length>0) + { + retval=blank(length); + char const * const end=s+length; + char *ptr=retval->data; + for(;*s&&(s!=end);ptr++) + { + ptr[0]=s++[0]; + } + ptr[0]=0; + } + return retval; +} + +GP<GStringRep> +GStringRep::substr(const char *s,const int start,const int len) const +{ + GP<GStringRep> retval; + if(s && s[0]) + { + const unsigned int length=(start<0 || len<0)?(unsigned int)strlen(s):(unsigned int)(-1); + const char *startptr, *endptr; + if(start<0) + { + startptr=s+length+start; + if(startptr<s) + startptr=s; + }else + { + startptr=s; + for(const char * const ptr=s+start;(startptr<ptr)&&*startptr;++startptr) + EMPTY_LOOP; + } + if(len<0) + { + if(s+length+1 < startptr+len) + { + endptr=startptr; + }else + { + endptr=s+length+1+len; + } + }else + { + endptr=startptr; + for(const char * const ptr=startptr+len;(endptr<ptr)&&*endptr;++endptr) + EMPTY_LOOP; + } + if(endptr>startptr) + { + retval=blank((size_t)(endptr-startptr)); + char *data=retval->data; + for(; (startptr<endptr) && *startptr; ++startptr,++data) + { + data[0]=startptr[0]; + } + data[0]=0; + } + } + return retval; +} + +GP<GStringRep> +GStringRep::substr(const unsigned short *s,const int start,const int len) const +{ + GP<GStringRep> retval; + if(s && s[0]) + { + unsigned short const *eptr; + if(len<0) + { + for(eptr=s;eptr[0];++eptr) + EMPTY_LOOP; + }else + { + eptr=&(s[len]); + } + s=&s[start]; + if((size_t)s<(size_t)eptr) + { + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + unsigned char *buf,*ptr; + GPBuffer<unsigned char> gbuf(buf,(((size_t)eptr-(size_t)s)/2)*3+7); + for(ptr=buf;s[0];) + { + unsigned long w; + int i=UTF16toUCS4(w,s,eptr); + if(i<=0) + break; + s+=i; + ptr=UCS4toString(w,ptr,&ps); + } + ptr[0]=0; + retval = strdup( (const char *)buf ); + } + } + return retval; +} + +GP<GStringRep> +GStringRep::substr(const unsigned long *s,const int start,const int len) const +{ + GP<GStringRep> retval; + if(s && s[0]) + { + unsigned long const *eptr; + if(len<0) + { + for(eptr=s;eptr[0];++eptr) + EMPTY_LOOP; + }else + { + eptr=&(s[len]); + } + s=&s[start]; + if((size_t)s<(size_t)eptr) + { + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + unsigned char *buf,*ptr; + GPBuffer<unsigned char> gbuf(buf,((((size_t)eptr-(size_t)s))/4)*6+7); + for(ptr=buf;s[0];++s) + { + ptr=UCS4toString(s[0],ptr,&ps); + } + ptr[0]=0; + retval = strdup( (const char *)buf ); + } + } + return retval; +} + +GP<GStringRep> +GStringRep::append(const char *s2) const +{ + GP<GStringRep> retval; + if(s2) + { + retval=concat(data,s2); + }else + { + retval=const_cast<GStringRep *>(this); + } + return retval; +} + +GP<GStringRep> +GStringRep::UTF8::append(const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s2) + { + if(s2->isNative()) + { + G_THROW( ERR_MSG("GStringRep.appendNativeToUTF8") ); + } + retval=concat(data,s2->data); + }else + { + retval=const_cast<GStringRep::UTF8 *>(this); + } + return retval; +} + +GP<GStringRep> +GStringRep::concat(const char *s1,const char *s2) const +{ + const int length1=(s1?strlen(s1):0); + const int length2=(s2?strlen(s2):0); + const int length=length1+length2; + GP<GStringRep> retval; + if(length>0) + { + retval=blank(length); + GStringRep &r=*retval; + if(length1) + { + strcpy(r.data,s1); + if(length2) + strcat(r.data,s2); + }else + { + strcpy(r.data,s2); + } + } + return retval; +} + +const char *GBaseString::nullstr = ""; + +void +GBaseString::empty( void ) +{ + init(0); +} + +GP<GStringRep> +GStringRep::getbuf(int n) const +{ + GP<GStringRep> retval; + if(n< 0) + n=strlen(data); + if(n>0) + { + retval=blank(n); + char *ndata=retval->data; + strncpy(ndata,data,n); + ndata[n]=0; + } + return retval; +} + +const char * +GStringRep::isCharType( + bool (*xiswtest)(const unsigned long wc), const char *ptr, const bool reverse) const +{ + char const * xptr=ptr; + const unsigned long w=getValidUCS4(xptr); + if((ptr != xptr) + &&(((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ||(reverse?(!xiswtest(w)):xiswtest(w)))) + { + ptr=xptr; + } + return ptr; +} + +int +GStringRep::nextCharType( + bool (*xiswtest)(const unsigned long wc), const int from, const int len, + const bool reverse) const +{ + // We want to return the position of the next + // non white space starting from the #from# + // location. isspace should work in any locale + // so we should only need to do this for the non- + // native locales (UTF8) + int retval; + if(from<size) + { + retval=from; + const char * ptr = data+from; + for( const char * const eptr=ptr+((len<0)?(size-from):len); + (ptr<eptr) && *ptr;) + { + // Skip characters that fail the isCharType test + char const * const xptr=isCharType(xiswtest,ptr,!reverse); + if(xptr == ptr) + break; + ptr=xptr; + } + retval=(int)((size_t)ptr-(size_t)data); + }else + { + retval=size; + } + return retval; +} + +bool +GStringRep::giswspace(const unsigned long w) +{ +#if HAS_WCTYPE + return + ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ||((unsigned long)iswspace((wchar_t)w)) + ||((w == '\r')||(w == '\n')); +#else + return + (w&~0xff)?(true):(((unsigned long)isspace((char)w))||((w == '\r')||(w == '\n'))); +#endif +} + +bool +GStringRep::giswupper(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?(true):((unsigned long)iswupper((wchar_t)w)?true:false); +#else + return (w&~0xff)?(true):((unsigned long)isupper((char)w)?true:false); +#endif +} + +bool +GStringRep::giswlower(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?(true):((unsigned long)iswlower((wchar_t)w)?true:false); +#else + return (w&~0xff)?(true):((unsigned long)islower((char)w)?true:false); +#endif +} + +unsigned long +GStringRep::gtowupper(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?w:((unsigned long)towupper((wchar_t)w)); +#else + return (w&~0xff)?w:((unsigned long)toupper((char)w)); +#endif +} + +unsigned long +GStringRep::gtowlower(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?w:((unsigned long)towlower((wchar_t)w)); +#else + return (w&~0xff)?w:((unsigned long)tolower((char)w)); +#endif +} + +GP<GStringRep> +GStringRep::tocase( + bool (*xiswcase)(const unsigned long wc), + unsigned long (*xtowcase)(const unsigned long wc)) const +{ + GP<GStringRep> retval; + char const * const eptr=data+size; + char const *ptr=data; + while(ptr<eptr) + { + char const * const xptr=isCharType(xiswcase,ptr,false); + if(ptr == xptr) + break; + ptr=xptr; + } + if(ptr<eptr) + { + const int n=(int)((size_t)ptr-(size_t)data); + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,n+(1+size-n)*6); + if(n>0) + { + strncpy((char *)buf,data,n); + } + unsigned char *buf_ptr=buf+n; + for(char const *ptr=data+n;ptr<eptr;) + { + char const * const xptr=ptr; + const unsigned long w=getValidUCS4(ptr); + if(ptr == xptr) + break; + if(xiswcase(w)) + { + const int len=(int)((size_t)ptr-(size_t)xptr); + strncpy((char *)buf_ptr,xptr,len); + buf_ptr+=len; + }else + { + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + buf_ptr=UCS4toString(xtowcase(w),buf_ptr,&ps); + } + } + buf_ptr[0]=0; + retval=substr((const char *)buf,0,(int)((size_t)buf_ptr-(size_t)buf)); + }else + { + retval=const_cast<GStringRep *>(this); + } + return retval; +} + +// Returns a copy of this string with characters used in XML escaped as follows: +// '<' --> "<" +// '>' --> ">" +// '&' --> "&" +// '\'' --> "'" +// '\"' --> """ +// Also escapes characters 0x00 through 0x1f and 0x7e through 0x7f. +GP<GStringRep> +GStringRep::toEscaped( const bool tosevenbit ) const +{ + bool modified=false; + char *ret; + GPBuffer<char> gret(ret,size*7); + ret[0]=0; + char *retptr=ret; + char const *start=data; + char const *s=start; + char const *last=s; + GP<GStringRep> special; + for(unsigned long w;(w=getValidUCS4(s));last=s) + { + char const *ss=0; + switch(w) + { + case '<': + ss="<"; + break; + case '>': + ss=">"; + break; + case '&': + ss="&"; + break; + case '\47': + ss="'"; + break; + case '\42': + ss="""; + break; + default: + if((w<' ')||(w>=0x7e && (tosevenbit || (w < 0x80)))) + { + special=toThis(UTF8::create_format("&#%lu;",w)); + ss=special->data; + } + break; + } + if(ss) + { + modified=true; + if(s!=start) + { + size_t len=(size_t)last-(size_t)start; + strncpy(retptr,start,len); + retptr+=len; + start=s; + } + if(ss[0]) + { + size_t len=strlen(ss); + strcpy(retptr,ss); + retptr+=len; + } + } + } + GP<GStringRep> retval; + if(modified) + { + strcpy(retptr,start); + retval=strdup( ret ); + }else + { + retval=const_cast<GStringRep *>(this); + } +// DEBUG_MSG( "Escaped string is '" << ret << "'\n" ); + return retval; +} + + +static const GMap<GUTF8String,GUTF8String> & +BasicMap( void ) +{ + static GMap<GUTF8String,GUTF8String> Basic; + if (! Basic.size()) + { + Basic["lt"] = GUTF8String('<'); + Basic["gt"] = GUTF8String('>'); + Basic["amp"] = GUTF8String('&'); + Basic["apos"] = GUTF8String('\47'); + Basic["quot"] = GUTF8String('\42'); + } + return Basic; +} + +GUTF8String +GUTF8String::fromEscaped( const GMap<GUTF8String,GUTF8String> ConvMap ) const +{ + GUTF8String ret; // Build output string here + int start_locn = 0; // Beginning of substring to skip + int amp_locn; // Location of a found ampersand + + while( (amp_locn = search( '&', start_locn )) > -1 ) + { + // Found the next apostrophe + // Locate the closing semicolon + const int semi_locn = search( ';', amp_locn ); + // No closing semicolon, exit and copy + // the rest of the string. + if( semi_locn < 0 ) + break; + ret += substr( start_locn, amp_locn - start_locn ); + int const len = semi_locn - amp_locn - 1; + if(len) + { + GUTF8String key = substr( amp_locn+1, len); + //DEBUG_MSG( "key = '" << key << "'\n" ); + char const * s=key; + if( s[0] == '#') + { + unsigned long value; + char *ptr=0; + if(s[1] == 'x' || s[1] == 'X') + { + value=strtoul((char const *)(s+2),&ptr,16); + }else + { + value=strtoul((char const *)(s+1),&ptr,10); + } + if(ptr) + { + unsigned char utf8char[7]; + unsigned char const * const end=GStringRep::UCS4toUTF8(value,utf8char); + ret+=GUTF8String((char const *)utf8char,(size_t)end-(size_t)utf8char); + }else + { + ret += substr( amp_locn, semi_locn - amp_locn + 1 ); + } + }else + { + GPosition map_entry = ConvMap.contains( key ); + if( map_entry ) + { // Found in the conversion map, substitute + ret += ConvMap[map_entry]; + } else + { + static const GMap<GUTF8String,GUTF8String> &Basic = BasicMap(); + GPosition map_entry = Basic.contains( key ); + if ( map_entry ) + { + ret += Basic[map_entry]; + }else + { + ret += substr( amp_locn, len+2 ); + } + } + } + }else + { + ret += substr( amp_locn, len+2 ); + } + start_locn = semi_locn + 1; +// DEBUG_MSG( "ret = '" << ret << "'\n" ); + } + + // Copy the end of the string to the output + ret += substr( start_locn, length()-start_locn ); + +// DEBUG_MSG( "Unescaped string is '" << ret << "'\n" ); + return (ret == *this)?(*this):ret; +} + +GUTF8String +GUTF8String::fromEscaped(void) const +{ + const GMap<GUTF8String,GUTF8String> nill; + return fromEscaped(nill); +} + +GP<GStringRep> +GStringRep::setat(int n, char ch) const +{ + GP<GStringRep> retval; + if(n<0) + n+=size; + if (n < 0 || n>size) + GBaseString::throw_illegal_subscript(); + if(ch == data[n]) + { + retval=const_cast<GStringRep *>(this); + }else if(!ch) + { + retval=getbuf(n); + }else + { + retval=getbuf((n<size)?size:n); + retval->data[n]=ch; + if(n == size) + retval->data[n+1]=0; + } + return retval; +} + +#ifdef WIN32 +#define USE_VSNPRINTF _vsnprintf +#endif + +#ifdef AUTOCONF +# ifdef HAVE_VSNPRINTF +# define USE_VSNPRINTF vsnprintf +# endif +#else +# ifdef linux +# define USE_VSNPRINTF vsnprintf +# endif +#endif + +GUTF8String & +GUTF8String::format(const char fmt[], ... ) +{ + va_list args; + va_start(args, fmt); + return init(GStringRep::UTF8::create(fmt,args)); +} + +GP<GStringRep> +GStringRep::UTF8::create_format(const char fmt[],...) +{ + va_list args; + va_start(args, fmt); + return create(fmt,args); +} + +GP<GStringRep> +GStringRep::vformat(va_list args) const +{ + GP<GStringRep> retval; + if(size) + { +#ifndef WIN32 + char *nfmt; + GPBuffer<char> gnfmt(nfmt,size+1); + nfmt[0]=0; + int start=0; +#endif + int from=0; + while((from=search('%',from)) >= 0) + { + if(data[++from] != '%') + { + int m,n=0; + sscanf(data+from,"%d!%n",&m,&n); + if(n) + { +#ifdef WIN32 + char *lpszFormat=data; + LPTSTR lpszTemp; + if((!::FormatMessage( + FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, + lpszFormat, 0, 0, (LPTSTR)&lpszTemp,0,&args)) + || !lpszTemp) + { + G_THROW(GException::outofmemory); + } + va_end(args); + retval=strdup((const char *)lpszTemp); + LocalFree(lpszTemp); + break; +#else + from+=n; + const int end=search('!',from); + if(end>=0) + { + strncat(nfmt,data+start,(int)(end-start)); + strncat(nfmt,"$",1); + start=from=end+1; + }else + { + gnfmt.resize(0); + from=(-1); + break; + } +#endif + }else + { +#ifndef WIN32 + gnfmt.resize(0); +#endif + from=(-1); + break; + } + } + } + if(from < 0) + { +#ifndef WIN32 + char const * const fmt=(nfmt&&nfmt[0])?nfmt:data; +#else + char const * const fmt=data; +#endif + int buflen=32768; + char *buffer; + GPBuffer<char> gbuffer(buffer,buflen); + + ChangeLocale locale(LC_NUMERIC,(isNative()?0:"C")); + + // Format string +#ifdef USE_VSNPRINTF + while(USE_VSNPRINTF(buffer, buflen, fmt, args)<0) + { + gbuffer.resize(0); + gbuffer.resize(buflen+32768); + } + va_end(args); +#else + buffer[buflen-1] = 0; + vsprintf(buffer, fmt, args); + va_end(args); + if (buffer[buflen-1]) + { + // This isn't as fatal since it is on the stack, but we + // definitely should stop the current operation. + G_THROW( ERR_MSG("GString.overwrite") ); + } +#endif + retval=strdup((const char *)buffer); + } + } + // Go altering the string + return retval; +} + +int +GStringRep::search(char c, int from) const +{ + if (from<0) + from += size; + int retval=(-1); + if (from>=0 && from<size) + { + char const *const s = strchr(data+from,c); + if(s) + retval=(int)((size_t)s-(size_t)data); + } + return retval; +} + +int +GStringRep::search(char const *ptr, int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + if (from>=0 && from<size) + { + char const *const s = strstr(data+from,ptr); + if(s) + retval=(int)((size_t)s-(size_t)data); + } + return retval; +} + +int +GStringRep::rsearch(char c, int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + if ((from>=0) && (from<size)) + { + char const *const s = strrchr(data+from,c); + if(s) + retval=(int)((size_t)s-(size_t)data); + } + return retval; +} + +int +GStringRep::rsearch(char const *ptr, int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + for(int loc=from;(loc=search(ptr,loc)) >= 0;++loc) + retval=loc; + return retval; +} + +int +GStringRep::contains(const char accept[],int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + if (accept && accept[0] && from>=0 && from<size) + { + char const * const src = data+from; + char const *ptr=strpbrk(src,accept); + if(ptr) + { + retval=(int)(ptr-src)+from; + } + } + return retval; +} + +int +GStringRep::rcontains(const char accept[],int from) const +{ + int retval=(-1); + while((from=contains(accept,from)) >= 0) + { + retval=from++; + } + return retval; +} + +bool +GBaseString::is_int(void) const +{ + bool isLong=!!ptr; + if(isLong) + { + int endpos; + (*this)->toLong(0,endpos); + if(endpos>=0) + { + isLong=((*this)->nextNonSpace(endpos) == (int)length()); + } + } + return isLong; +} + +bool +GBaseString::is_float(void) const +{ + bool isDouble=!!ptr; + if(isDouble) + { + int endpos; + (*this)->toDouble(0,endpos); + if(endpos>=0) + { + isDouble=((*this)->nextNonSpace(endpos) == (int)length()); + } + } + return isDouble; +} + +unsigned int +hash(const GBaseString &str) +{ + unsigned int x = 0; + const char *s = (const char*)str; + while (*s) + x = x ^ (x<<6) ^ (unsigned char)(*s++); + return x; +} + +void +GBaseString::throw_illegal_subscript() +{ + G_THROW( ERR_MSG("GString.bad_subscript") ); +} + +unsigned char * +GStringRep::UTF8::UCS4toString( + const unsigned long w0,unsigned char *ptr, mbstate_t *) const +{ + return UCS4toUTF8(w0,ptr); +} + +int +GStringRep::UTF8::ncopy( + wchar_t * const buf, const int buflen ) const +{ + int retval=(-1); + if(buf && buflen) + { + buf[0]=0; + if(data[0]) + { + const size_t length=strlen(data); + const unsigned char * const eptr=(const unsigned char *)(data+length); + wchar_t *r=buf; + wchar_t const * const rend=buf+buflen; + for(const unsigned char *s=(const unsigned char *)data;(r<rend)&&(s<eptr)&&*s;) + { + const unsigned long w0=UTF8toUCS4(s,eptr); + unsigned short w1; + unsigned short w2=1; + for(int count=(sizeof(wchar_t) == sizeof(w1))?UCS4toUTF16(w0,w1,w2):1; + count&&(r<rend); + --count,w1=w2,++r) + { + r[0]=(sizeof(wchar_t) == sizeof(w1))?(wchar_t)w1:(wchar_t)w0; + } + } + if(r<rend) + { + r[0]=0; + retval=((size_t)r-(size_t)buf)/sizeof(wchar_t); + } + }else + { + retval=0; + } + } + return retval; +} + +GP<GStringRep> +GStringRep::UTF8::toNative(const EscapeMode escape) const +{ + GP<GStringRep> retval; + if(data[0]) + { + const size_t length=strlen(data); + const unsigned char * const eptr=(const unsigned char *)(data+length); + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,12*length+12); + unsigned char *r=buf; + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + for(const unsigned char *s=(const unsigned char *)data;(s<eptr)&& *s;) + { + const unsigned long w0=UTF8toUCS4(s,eptr); + const unsigned char * const r0=r; + r=UCS4toNative(w0,r,&ps); + if(r == r0) + { + if(escape == IS_ESCAPED) + { + sprintf((char *)r,"&#%lu;",w0); + r+=strlen((char *)r); + }else + { + r=buf; + break; + } + } + } + r[0]=0; + retval = NATIVE_CREATE( (const char *)buf ); + } else + { + retval = NATIVE_CREATE( (unsigned int)0 ); + } + return retval; +} + +GP<GStringRep> +GStringRep::UTF8::toUTF8(const bool nothrow) const +{ + if(!nothrow) + G_THROW( ERR_MSG("GStringRep.UTF8ToUTF8") ); + return const_cast<GStringRep::UTF8 *>(this); +} + +// Tests if a string is legally encoded in the current character set. +bool +GStringRep::UTF8::is_valid(void) const +{ + bool retval=true; + if(data && size) + { + const unsigned char * const eptr=(const unsigned char *)(data+size); + for(const unsigned char *s=(const unsigned char *)data;(s<eptr)&& *s;) + { + const unsigned char * const r=s; + (void)UTF8toUCS4(s,eptr); + if(r == s) + { + retval=false; + break; + } + } + } + return retval; +} + +static inline unsigned long +add_char(unsigned long const U, unsigned char const * const r) +{ + unsigned long const C=r[0]; + return ((C|0x3f) == 0xbf)?((U<<6)|(C&0x3f)):0; +} + +unsigned long +GStringRep::UTF8toUCS4( + unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const *r=s; + if(r < eptr) + { + unsigned long const C1=r++[0]; + if(C1&0x80) + { + if(r < eptr) + { + U=C1; + if((U=((C1&0x40)?add_char(U,r++):0))) + { + if(C1&0x20) + { + if(r < eptr) + { + if((U=add_char(U,r++))) + { + if(C1&0x10) + { + if(r < eptr) + { + if((U=add_char(U,r++))) + { + if(C1&0x8) + { + if(r < eptr) + { + if((U=add_char(U,r++))) + { + if(C1&0x4) + { + if(r < eptr) + { + if((U=((!(C1&0x2))?(add_char(U,r++)&0x7fffffff):0))) + { + s=r; + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x4000000)?0:(U&0x3ffffff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x200000)?0:(U&0x1fffff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x10000)?0:(U&0xffff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x800)?0:(U&0x7ff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=C1)) + { + s=r; + } + } + return U; +} + +unsigned char * +GStringRep::UCS4toUTF8(const unsigned long w,unsigned char *ptr) +{ + if(w <= 0x7f) + { + *ptr++ = (unsigned char)w; + } + else if(w <= 0x7ff) + { + *ptr++ = (unsigned char)((w>>6)|0xC0); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0xFFFF) + { + *ptr++ = (unsigned char)((w>>12)|0xE0); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0x1FFFFF) + { + *ptr++ = (unsigned char)((w>>18)|0xF0); + *ptr++ = (unsigned char)(((w>>12)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0x3FFFFFF) + { + *ptr++ = (unsigned char)((w>>24)|0xF8); + *ptr++ = (unsigned char)(((w>>18)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>12)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0x7FFFFFFF) + { + *ptr++ = (unsigned char)((w>>30)|0xFC); + *ptr++ = (unsigned char)(((w>>24)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>18)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>12)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else + { + *ptr++ = '?'; + } + return ptr; +} + + // Creates with a concat operation. +GP<GStringRep> +GStringRep::concat( const char *s1, const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s2) + { + retval=toThis(s2); + if(s1 && s1[0]) + { + if(retval) + { + retval=concat(s1,retval->data); + }else + { + retval=strdup(s1); + } + } + }else if(s1 && s1[0]) + { + retval=strdup(s1); + } + return retval; +} + + // Creates with a concat operation. + +GP<GStringRep> +GStringRep::concat( const GP<GStringRep> &s1,const char *s2) const +{ + GP<GStringRep> retval; + if(s1) + { + retval=toThis(s1); + if(s2 && s2[0]) + { + if(retval) + { + retval=retval->append(s2); + }else + { + retval=strdup(s2); + } + } + }else if(s2 && s2[0]) + { + retval=strdup(s2); + } + return retval; +} + +GP<GStringRep> +GStringRep::concat(const GP<GStringRep> &s1,const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s1) + { + retval=toThis(s1,s2); + if(retval && s2) + { + retval=retval->append(toThis(s2)); + } + }else if(s2) + { + retval=toThis(s2); + } + return retval; +} + +#ifdef WIN32 +static const char *setlocale_win32(void) +{ + static const char *locale=setlocale(LC_ALL,0); + if(! locale || (locale[0] == 'C' && !locale[1])) + { + locale=setlocale(LC_ALL,""); + } + return locale; +} +#endif + +GStringRep::GStringRep(void) +{ +#ifdef WIN32 + static const char *locale=setlocale_win32(); +#endif + size=0; + data=0; +} + +GStringRep::~GStringRep() +{ + if(data) + { + data[0]=0; + ::operator delete(data); + } + data=0; +} + +GStringRep::UTF8::UTF8(void) {} + +GStringRep::UTF8::~UTF8() {} + +int +GStringRep::cmp(const char *s1,const int len) const +{ + return cmp(data,s1,len); +} + +int +GStringRep::cmp(const char *s1, const char *s2,const int len) +{ + return (len + ?((s1&&s1[0]) + ?((s2&&s2[0]) + ?((len>0) + ?strncmp(s1,s2,len) + :strcmp(s1,s2)) + :1) + :((s2&&s2[0])?(-1):0)) + :0); +} + +int +GStringRep::cmp(const GP<GStringRep> &s1, const GP<GStringRep> &s2, + const int len ) +{ + return (s1?(s1->cmp(s2,len)):cmp(0,(s2?(s2->data):0),len)); +} + +int +GStringRep::cmp(const GP<GStringRep> &s1, const char *s2, + const int len ) +{ + return cmp((s1?s1->data:0),s2,len); +} + +int +GStringRep::cmp(const char *s1, const GP<GStringRep> &s2, + const int len ) +{ + return cmp(s1,(s2?(s2->data):0),len); +} + +int +GStringRep::UTF8::cmp(const GP<GStringRep> &s2,const int len) const +{ + int retval; + if(s2) + { + if(s2->isNative()) + { + GP<GStringRep> r(s2->toUTF8(true)); + if(r) + { + retval=GStringRep::cmp(data,r->data,len); + }else + { + retval=-(s2->cmp(toNative(NOT_ESCAPED),len)); + } + }else + { + retval=GStringRep::cmp(data,s2->data,len); + } + }else + { + retval=GStringRep::cmp(data,0,len); + } + return retval; +} + +int +GStringRep::UTF8::toInt() const +{ + int endpos; + return (int)toLong(0,endpos); +} + +static inline long +Cstrtol(char *data,char **edata, const int base) +{ + GStringRep::ChangeLocale locale(LC_NUMERIC,"C"); + while (data && *data==' ') data++; + return strtol(data,edata,base); +} + +long +GStringRep::UTF8::toLong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + long retval=Cstrtol(data+pos,&edata, base); + if(edata) + { + endpos=edata-data; + }else + { + endpos=(-1); + GP<GStringRep> ptr=ptr->strdup(data+pos); + if(ptr) + ptr=ptr->toNative(NOT_ESCAPED); + if(ptr) + { + int xendpos; + retval=ptr->toLong(0,xendpos,base); + if(xendpos> 0) + { + endpos=(int)size; + ptr=ptr->strdup(data+xendpos); + if(ptr) + { + ptr=ptr->toUTF8(true); + if(ptr) + { + endpos-=(int)(ptr->size); + } + } + } + } + } + return retval; +} + +static inline unsigned long +Cstrtoul(char *data,char **edata, const int base) +{ + GStringRep::ChangeLocale locale(LC_NUMERIC,"C"); + while (data && *data==' ') data++; + return strtoul(data,edata,base); +} + +unsigned long +GStringRep::UTF8::toULong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + unsigned long retval=Cstrtoul(data+pos,&edata, base); + if(edata) + { + endpos=edata-data; + }else + { + endpos=(-1); + GP<GStringRep> ptr=ptr->strdup(data+pos); + if(ptr) + ptr=ptr->toNative(NOT_ESCAPED); + if(ptr) + { + int xendpos; + retval=ptr->toULong(0,xendpos,base); + if(xendpos> 0) + { + endpos=(int)size; + ptr=ptr->strdup(data+xendpos); + if(ptr) + { + ptr=ptr->toUTF8(true); + if(ptr) + { + endpos-=(int)(ptr->size); + } + } + } + } + } + return retval; +} + +static inline double +Cstrtod(char *data,char **edata) +{ + GStringRep::ChangeLocale locale(LC_NUMERIC,"C"); + while (data && *data==' ') data++; + return strtod(data,edata); +} + +double +GStringRep::UTF8::toDouble(const int pos, int &endpos) const +{ + char *edata=0; + double retval=Cstrtod(data+pos,&edata); + if(edata) + { + endpos=edata-data; + }else + { + endpos=(-1); + GP<GStringRep> ptr=ptr->strdup(data+pos); + if(ptr) + ptr=ptr->toNative(NOT_ESCAPED); + if(ptr) + { + int xendpos; + retval=ptr->toDouble(0,xendpos); + if(xendpos >= 0) + { + endpos=(int)size; + ptr=ptr->strdup(data+xendpos); + if(ptr) + { + ptr=ptr->toUTF8(true); + if(ptr) + { + endpos-=(int)(ptr->size); + } + } + } + } + } + return retval; +} + +int +GStringRep::getUCS4(unsigned long &w, const int from) const +{ + int retval; + if(from>=size) + { + w=0; + retval=size; + }else if(from<0) + { + w=(unsigned int)(-1); + retval=(-1); + }else + { + const char *source=data+from; + w=getValidUCS4(source); + retval=(int)((size_t)source-(size_t)data); + } + return retval; +} + + +unsigned long +GStringRep::UTF8::getValidUCS4(const char *&source) const +{ + return GStringRep::UTF8toUCS4((const unsigned char *&)source,data+size); +} + +int +GStringRep::nextNonSpace(const int from,const int len) const +{ + return nextCharType(giswspace,from,len,true); +} + +int +GStringRep::nextSpace(const int from,const int len) const +{ + return nextCharType(giswspace,from,len,false); +} + +int +GStringRep::nextChar(const int from) const +{ + char const * xptr=data+from; + (void)getValidUCS4(xptr); + return (int)((size_t)xptr-(size_t)data); +} + +int +GStringRep::firstEndSpace(int from,const int len) const +{ + const int xsize=(len<0)?size:(from+len); + const int ysize=(size<xsize)?size:xsize; + int retval=ysize; + while(from<ysize) + { + from=nextNonSpace(from,ysize-from); + if(from < size) + { + const int r=nextSpace(from,ysize-from); + // If a character isn't legal, then it will return + // tru for both nextSpace and nextNonSpace. + if(r == from) + { + from++; + }else + { + from=retval=r; + } + } + } + return retval; +} + +int +GStringRep::UCS4toUTF16( + const unsigned long w,unsigned short &w1, unsigned short &w2) +{ + int retval; + if(w<0x10000) + { + w1=(unsigned short)w; + w2=0; + retval=1; + }else + { + w1=(unsigned short)((((w-0x10000)>>10)&0x3ff)+0xD800); + w2=(unsigned short)((w&0x3ff)+0xDC00); + retval=2; + } + return retval; +} + +int +GStringRep::UTF16toUCS4( + unsigned long &U,unsigned short const * const s,void const * const eptr) +{ + int retval=0; + U=0; + unsigned short const * const r=s+1; + if(r <= eptr) + { + unsigned long const W1=s[0]; + if((W1<0xD800)||(W1>0xDFFF)) + { + if((U=W1)) + { + retval=1; + } + }else if(W1<=0xDBFF) + { + unsigned short const * const rr=r+1; + if(rr <= eptr) + { + unsigned long const W2=s[1]; + if(((W2>=0xDC00)||(W2<=0xDFFF))&&((U=(0x10000+((W1&0x3ff)<<10))|(W2&0x3ff)))) + { + retval=2; + }else + { + retval=(-1); + } + } + } + } + return retval; +} + + +//bcr + +GUTF8String& +GUTF8String::operator+= (char ch) +{ + return init( + GStringRep::UTF8::create((const char*)*this, + GStringRep::UTF8::create(&ch,0,1))); +} + +GUTF8String& +GUTF8String::operator+= (const char *str) +{ + return init(GStringRep::UTF8::create(*this,str)); +} + +GUTF8String& +GUTF8String::operator+= (const GBaseString &str) +{ + return init(GStringRep::UTF8::create(*this,str)); +} + +GUTF8String +GUTF8String::substr(int from, int len) const +{ return GUTF8String(*this, from, len); } + +GUTF8String +GUTF8String::operator+(const GBaseString &s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +GUTF8String +GUTF8String::operator+(const GUTF8String &s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +GUTF8String +GUTF8String::operator+(const char *s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +char * +GUTF8String::getbuf(int n) +{ + if(ptr) + init((*this)->getbuf(n)); + else if(n>0) + init(GStringRep::UTF8::create(n)); + else + init(0); + return ptr?((*this)->data):0; +} + +void +GUTF8String::setat(const int n, const char ch) +{ + if((!n)&&(!ptr)) + { + init(GStringRep::UTF8::create(&ch,0,1)); + }else + { + init((*this)->setat(CheckSubscript(n),ch)); + } +} + +GP<GStringRep> +GStringRep::UTF8ToNative( const char *s, const EscapeMode escape ) +{ + return GStringRep::UTF8::create(s)->toNative(escape); +} + +GUTF8String::GUTF8String(const char dat) +{ init(GStringRep::UTF8::create(&dat,0,1)); } + +GUTF8String::GUTF8String(const GUTF8String &fmt, va_list &args) +{ + if (fmt.ptr) + init(fmt->vformat(args)); + else + init(fmt); +} + +GUTF8String::GUTF8String(const char *str) +{ init(GStringRep::UTF8::create(str)); } + +GUTF8String::GUTF8String(const unsigned char *str) +{ init(GStringRep::UTF8::create((const char *)str)); } + +GUTF8String::GUTF8String(const unsigned short *str) +{ init(GStringRep::UTF8::create(str,0,-1)); } + +GUTF8String::GUTF8String(const unsigned long *str) +{ init(GStringRep::UTF8::create(str,0,-1)); } + +GUTF8String::GUTF8String(const char *dat, unsigned int len) +{ init(GStringRep::UTF8::create(dat,0,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const unsigned short *dat, unsigned int len) +{ init(GStringRep::UTF8::create(dat,0,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const unsigned long *dat, unsigned int len) +{ init(GStringRep::UTF8::create(dat,0,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const GBaseString &gs, int from, int len) +{ init(GStringRep::UTF8::create(gs,from,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const int number) +{ init(GStringRep::UTF8::create_format("%d",number)); } + +GUTF8String::GUTF8String(const double number) +{ init(GStringRep::UTF8::create_format("%f",number)); } + +GUTF8String& GUTF8String::operator= (const char str) +{ return init(GStringRep::UTF8::create(&str,0,1)); } + +GUTF8String& GUTF8String::operator= (const char *str) +{ return init(GStringRep::UTF8::create(str)); } + +GUTF8String GBaseString::operator+(const GUTF8String &s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +#if HAS_WCHAR +GUTF8String +GNativeString::operator+(const GUTF8String &s2) const +{ + if (ptr) + return GStringRep::UTF8::create((*this)->toUTF8(true),s2); + else + return GStringRep::UTF8::create((*this),s2); +} +#endif + +GUTF8String +GUTF8String::operator+(const GNativeString &s2) const +{ + GP<GStringRep> g = s2; + if (s2.ptr) + g = s2->toUTF8(true); + return GStringRep::UTF8::create(*this,g); +} + +GUTF8String +operator+(const char *s1, const GUTF8String &s2) +{ return GStringRep::UTF8::create(s1,s2); } + +#if HAS_WCHAR +GNativeString +operator+(const char *s1, const GNativeString &s2) +{ return GStringRep::Native::create(s1,s2); } + +GNativeString& +GNativeString::operator+= (char ch) +{ + char s[2]; s[0]=ch; s[1]=0; + return init(GStringRep::Native::create((const char*)*this, s)); +} + +GNativeString& +GNativeString::operator+= (const char *str) +{ + return init(GStringRep::Native::create(*this,str)); +} + +GNativeString& +GNativeString::operator+= (const GBaseString &str) +{ + return init(GStringRep::Native::create(*this,str)); +} + +GNativeString +GNativeString::operator+(const GBaseString &s2) const +{ return GStringRep::Native::create(*this,s2); } + +GNativeString +GNativeString::operator+(const GNativeString &s2) const +{ return GStringRep::Native::create(*this,s2); } + +GNativeString +GNativeString::operator+(const char *s2) const +{ return GStringRep::Native::create(*this,s2); } + +char * +GNativeString::getbuf(int n) +{ + if(ptr) + init((*this)->getbuf(n)); + else if(n>0) + init(GStringRep::Native::create(n)); + else + init(0); + return ptr?((*this)->data):0; +} + +void +GNativeString::setat(const int n, const char ch) +{ + if((!n)&&(!ptr)) + { + init(GStringRep::Native::create(&ch,0,1)); + }else + { + init((*this)->setat(CheckSubscript(n),ch)); + } +} + +#endif + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GString.h b/kviewshell/plugins/djvu/libdjvu/GString.h new file mode 100644 index 00000000..601db983 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GString.h @@ -0,0 +1,1676 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GString.h,v 1.19 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GSTRING_H_ +#define _GSTRING_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GString.h + + Files #"GString.h"# and #"GString.cpp"# implement a general + purpose string class \Ref{GBaseString}, with dirived types + \Ref{GUTF8String} and \Ref{GNativeString} for UTF8 MBS encoding + and the current Native MBS encoding respectively. This + implementation relies on smart pointers (see + \Ref{GSmartPointer.h}). + + {\bf Historical Comments} --- At some point during the DjVu + research era, it became clear that C++ compilers rarely provided + portable libraries. We then decided to avoid fancy classes (like + #iostream# or #string#) and to rely only on the good old C + library. A good string class however is very useful. We had + already randomly picked letter 'G' to prefix class names and we + logically derived the new class name. Native English speakers + kept laughing in hiding. This is ironic because we completely + forgot this letter 'G' when creating more challenging things + like the ZP Coder or the IW44 wavelets. + + {\bf Later Changes} + When converting to I18N, we (Lizardtech) decided that two string classes + where needing, replacing the original GString with \Ref{GUTF8String} and + \Ref{GNativeString}. + + @memo + General purpose string class. + @author + L\'eon Bottou <leonb@research.att.com> -- initial implementation.\\ + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// My original implementation was very small in comparison +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. +// In my opinion, the duplication of the string classes is a failed +// attempt to use the type system to enforce coding policies. +// This could be fixed. But there are better things to do in djvulibre. + + @version + #$Id: GString.h,v 1.19 2004/08/06 15:11:29 leonb Exp $# */ +//@{ + + +#include "DjVuGlobal.h" +#include "GContainer.h" + +#include <stdlib.h> +#include <stdarg.h> +#ifdef WIN32 +# include <windows.h> +# define HAS_WCHAR 1 +# define HAS_MBSTATE 1 +#endif + +#if HAS_WCHAR +# if !defined(AUTOCONF) || HAVE_WCHAR_H +# include <wchar.h> +# endif +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +#if !HAS_MBSTATE +# ifndef HAVE_MBSTATE_T +typedef int mbstate_t; +# endif +#endif + +class GBaseString; + +// Internal string representation. +class GStringRep : public GPEnabled +{ +public: + enum EncodeType { XUCS4, XUCS4BE, XUCS4LE, XUCS4_2143, XUCS4_3412, + XUTF16, XUTF16BE, XUTF16LE, XUTF8, XEBCDIC, XOTHER } ; + + enum EscapeMode { UNKNOWN_ESCAPED=0, IS_ESCAPED=1, NOT_ESCAPED=2 }; + + class UTF8; + friend class UTF8; + class Unicode; + friend class Unicode; + + class ChangeLocale; +#if HAS_WCHAR + class Native; + friend class Native; +#endif // HAS_WCHAR + friend class GBaseString; + friend class GUTF8String; + friend class GNativeString; + friend unsigned int hash(const GBaseString &ref); + +public: + // default constructor + GStringRep(void); + // virtual destructor + virtual ~GStringRep(); + + // Other virtual methods. + // Create an empty string. + virtual GP<GStringRep> blank(const unsigned int sz) const = 0; + // Create a duplicate at the given size. + GP<GStringRep> getbuf(int n) const; + // Change the value of one of the bytes. + GP<GStringRep> setat(int n, char ch) const; + // Append a string. + virtual GP<GStringRep> append(const GP<GStringRep> &s2) const = 0; + // Test if isUTF8. + virtual bool isUTF8(void) const { return false; } + // Test if Native. + virtual bool isNative(void) const { return false; } + // Convert to Native. + virtual GP<GStringRep> toNative( + const EscapeMode escape=UNKNOWN_ESCAPED ) const = 0; + // Convert to UTF8. + virtual GP<GStringRep> toUTF8(const bool nothrow=false) const = 0; + // Convert to same as current class. + virtual GP<GStringRep> toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &locale=0) const = 0; + // Compare with #s2#. + virtual int cmp(const GP<GStringRep> &s2,const int len=(-1)) const = 0; + + // Convert strings to numbers. + virtual int toInt(void) const = 0; + virtual long int toLong( + const int pos, int &endpos, const int base=10) const = 0; + virtual unsigned long toULong( + const int pos, int &endpos, const int base=10) const = 0; + virtual double toDouble(const int pos, int &endpos) const = 0; + + // return the position of the next character + int nextChar( const int from=0 ) const; + + // return next non space position + int nextNonSpace( const int from=0, const int len=(-1) ) const; + + // return next white space position + int nextSpace( const int from=0, const int len=(-1) ) const; + + // return the position after the last non-whitespace character. + int firstEndSpace( int from=0, const int len=(-1) ) const; + + // Create an empty string. + template <class TYPE> static GP<GStringRep> create( + const unsigned int sz,TYPE *); + // Creates with a strdup string. + GP<GStringRep> strdup(const char *s) const; + + // Creates by appending to the current string + GP<GStringRep> append(const char *s2) const; + + // Creates with a concat operation. + GP<GStringRep> concat(const GP<GStringRep> &s1,const GP<GStringRep> &s2) const; + GP<GStringRep> concat(const char *s1,const GP<GStringRep> &s2) const; + GP<GStringRep> concat(const GP<GStringRep> &s1,const char *s2) const; + GP<GStringRep> concat(const char *s1,const char *s2) const; + + /* Creates with a strdup and substr. Negative values have strlen(s)+1 + added to them. + */ + GP<GStringRep> substr( + const char *s,const int start,const int length=(-1)) const; + + GP<GStringRep> substr( + const unsigned short *s,const int start,const int length=(-1)) const; + + GP<GStringRep> substr( + const unsigned long *s,const int start,const int length=(-1)) const; + + /** Initializes a string with a formatted string (as in #vprintf#). The + string is re-initialized with the characters generated according to the + specified format #fmt# and using the optional arguments. See the ANSI-C + function #vprintf()# for more information. The current implementation + will cause a segmentation violation if the resulting string is longer + than 32768 characters. */ + GP<GStringRep> vformat(va_list args) const; + // -- SEARCHING + + static GP<GStringRep> UTF8ToNative( const char *s, + const EscapeMode escape=UNKNOWN_ESCAPED ); + static GP<GStringRep> NativeToUTF8( const char *s ); + + // Creates an uppercase version of the current string. + GP<GStringRep> upcase(void) const; + // Creates a lowercase version of the current string. + GP<GStringRep> downcase(void) const; + + /** Returns the next UCS4 character, and updates the pointer s. */ + static unsigned long UTF8toUCS4( + unsigned char const *&s, void const * const endptr ); + + /** Returns the number of bytes in next UCS4 character, + and sets #w# to the next UCS4 chacter. */ + static int UTF8toUCS4( + unsigned long &w, unsigned char const s[], void const * const endptr ) + { unsigned char const *r=s;w=UTF8toUCS4(r,endptr);return (int)((size_t)r-(size_t)s); } + + /** Returns the next UCS4 word from the UTF16 string. */ + static int UTF16toUCS4( + unsigned long &w, unsigned short const * const s,void const * const eptr); + + static int UCS4toUTF16( + unsigned long w, unsigned short &w1, unsigned short &w2); + + int cmp(const char *s2, const int len=(-1)) const; + static int cmp( + const GP<GStringRep> &s1, const GP<GStringRep> &s2, const int len=(-1)) ; + static int cmp( + const GP<GStringRep> &s1, const char *s2, const int len=(-1)); + static int cmp( + const char *s1, const GP<GStringRep> &s2, const int len=(-1)); + static int cmp( + const char *s1, const char *s2, const int len=(-1)); + + // Lookup the next character, and return the position of the next character. + int getUCS4(unsigned long &w, const int from) const; + + virtual unsigned char *UCS4toString( + const unsigned long w, unsigned char *ptr, mbstate_t *ps=0) const = 0; + + static unsigned char *UCS4toUTF8( + const unsigned long w,unsigned char *ptr); + + static unsigned char *UCS4toNative( + const unsigned long w,unsigned char *ptr, mbstate_t *ps); + + int search(char c, int from=0) const; + + int search(char const *str, int from=0) const; + + int rsearch(char c, int from=0) const; + + int rsearch(char const *str, int from=0) const; + + int contains(char const accept[], int from=0) const; + + int rcontains(char const accept[], int from=0) const; + +protected: + // Return the next character and increment the source pointer. + virtual unsigned long getValidUCS4(const char *&source) const = 0; + + GP<GStringRep> tocase( + bool (*xiswcase)(const unsigned long wc), + unsigned long (*xtowcase)(const unsigned long wc)) const; + + // Tests if the specified character passes the xiswtest. If so, the + // return pointer is incremented to the next character, otherwise the + // specified #ptr# is returned. + const char * isCharType( bool (*xiswtest)(const unsigned long wc), const char *ptr, + const bool reverse=false) const; + + // Find the next character position that passes the isCharType test. + int nextCharType( + bool (*xiswtest)(const unsigned long wc),const int from,const int len, + const bool reverse=false) const; + + static bool giswspace(const unsigned long w); + static bool giswupper(const unsigned long w); + static bool giswlower(const unsigned long w); + static unsigned long gtowupper(const unsigned long w); + static unsigned long gtowlower(const unsigned long w); + + virtual void set_remainder( void const * const buf, const unsigned int size, + const EncodeType encodetype); + virtual void set_remainder( void const * const buf, const unsigned int size, + const GP<GStringRep> &encoding ); + virtual void set_remainder ( const GP<Unicode> &remainder ); + + virtual GP<Unicode> get_remainder( void ) const; + +public: + /* Returns a copy of this string with characters used in XML with + '<' to "<", '>' to ">", '&' to "&" '\'' to + "'", and '\"' to """. Characters 0x01 through + 0x1f are also escaped. */ + GP<GStringRep> toEscaped( const bool tosevenbit ) const; + + // Tests if a string is legally encoded in the current character set. + virtual bool is_valid(void) const = 0; + + virtual int ncopy(wchar_t * const buf, const int buflen) const = 0; + +protected: + +// Actual string data. + int size; + char *data; +}; + +class GStringRep::UTF8 : public GStringRep +{ +public: + // default constructor + UTF8(void); + // virtual destructor + virtual ~UTF8(); + + // Other virtual methods. + virtual GP<GStringRep> blank(const unsigned int sz = 0) const; + virtual GP<GStringRep> append(const GP<GStringRep> &s2) const; + // Test if Native. + virtual bool isUTF8(void) const; + // Convert to Native. + virtual GP<GStringRep> toNative( + const EscapeMode escape=UNKNOWN_ESCAPED) const; + // Convert to UTF8. + virtual GP<GStringRep> toUTF8(const bool nothrow=false) const; + // Convert to same as current class. + virtual GP<GStringRep> toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const; + // Compare with #s2#. + virtual int cmp(const GP<GStringRep> &s2,const int len=(-1)) const; + + static GP<GStringRep> create(const unsigned int sz = 0); + + // Convert strings to numbers. + virtual int toInt(void) const; + virtual long int toLong( + const int pos, int &endpos, const int base=10) const; + virtual unsigned long toULong( + const int pos, int &endpos, const int base=10) const; + virtual double toDouble( + const int pos, int &endpos) const; + + // Create a strdup string. + static GP<GStringRep> create(const char *s); + + // Creates with a concat operation. + static GP<GStringRep> create( + const GP<GStringRep> &s1,const GP<GStringRep> &s2); + static GP<GStringRep> create( const GP<GStringRep> &s1,const char *s2); + static GP<GStringRep> create( const char *s1, const GP<GStringRep> &s2); + static GP<GStringRep> create( const char *s1,const char *s2); + + // Create with a strdup and substr operation. + static GP<GStringRep> create( + const char *s,const int start,const int length=(-1)); + + static GP<GStringRep> create( + const unsigned short *s,const int start,const int length=(-1)); + + static GP<GStringRep> create( + const unsigned long *s,const int start,const int length=(-1)); + + static GP<GStringRep> create_format(const char fmt[],...); + static GP<GStringRep> create(const char fmt[],va_list& args); + + virtual unsigned char *UCS4toString( + const unsigned long w,unsigned char *ptr, mbstate_t *ps=0) const; + + // Tests if a string is legally encoded in the current character set. + virtual bool is_valid(void) const; + + virtual int ncopy(wchar_t * const buf, const int buflen) const; + + friend class GBaseString; + +protected: + // Return the next character and increment the source pointer. + virtual unsigned long getValidUCS4(const char *&source) const; +}; + +class GUTF8String; +class GNativeString; + +/** General purpose character string. + Each dirivied instance of class #GBaseString# represents a + character string. Overloaded operators provide a value semantic + to #GBaseString# objects. Conversion operators and constructors + transparently convert between #GBaseString# objects and + #const char*# pointers. The #GBaseString# class has no public + constructors, since a dirived type should always be used + to specify the desired multibyte character encoding. + + Functions taking strings as arguments should declare their + arguments as "#const char*#". Such functions will work equally + well with dirived #GBaseString# objects since there is a fast + conversion operator from the dirivied #GBaseString# objects + to "#const char*#". Functions returning strings should return + #GUTF8String# or #GNativeString# objects because the class will + automatically manage the necessary memory. + + Characters in the string can be identified by their position. The + first character of a string is numbered zero. Negative positions + represent characters relative to the end of the string (i.e. + position #-1# accesses the last character of the string, + position #-2# represents the second last character, etc.) */ + +class GBaseString : protected GP<GStringRep> +{ +public: + enum EscapeMode { + UNKNOWN_ESCAPED=GStringRep::UNKNOWN_ESCAPED, + IS_ESCAPED=GStringRep::IS_ESCAPED, + NOT_ESCAPED=GStringRep::NOT_ESCAPED }; + + friend class GUTF8String; + friend class GNativeString; +protected: + // Sets the gstr pointer; + void init(void); + + ~GBaseString(); + GBaseString &init(const GP<GStringRep> &rep); + + // -- CONSTRUCTORS + /** Null constructor. Constructs an empty string. */ + GBaseString( void ); + +public: + // -- ACCESS + /** Converts a string into a constant null terminated character + array. This conversion operator is very efficient because + it simply returns a pointer to the internal string data. The + returned pointer remains valid as long as the string is + unmodified. */ + operator const char* ( void ) const ; + /// Returns the string length. + unsigned int length( void ) const; + /** Returns true if and only if the string contains zero characters. + This operator is useful for conditional expression in control + structures. + \begin{verbatim} + if (! str) { ... } + while (!! str) { ... } -- Note the double operator! + \end{verbatim} + Class #GBaseString# does not to support syntax + "#if# #(str)# #{}#" because the required conversion operator + introduces dangerous ambiguities with certain compilers. */ + bool operator! ( void ) const; + + // -- INDEXING + /** Returns the character at position #n#. An exception + \Ref{GException} is thrown if number #n# is not in range #-len# + to #len-1#, where #len# is the length of the string. The first + character of a string is numbered zero. Negative positions + represent characters relative to the end of the string. */ + char operator[] (int n) const; + /// Returns #TRUE# if the string contains an integer number. + bool is_int(void) const; + /// Returns #TRUE# if the string contains a float number. + bool is_float(void) const; + + /** Converts strings between native & UTF8 **/ + GNativeString getUTF82Native( EscapeMode escape=UNKNOWN_ESCAPED ) const; + GUTF8String getNative2UTF8( void ) const; + + // -- ALTERING + /// Reinitializes a string with the null string. + void empty( void ); + // -- SEARCHING + /** Searches character #c# in the string, starting at position + #from# and scanning forward until reaching the end of the + string. This function returns the position of the matching + character. It returns #-1# if character #c# cannot be found. */ + int search(char c, int from=0) const; + + /** Searches sub-string #str# in the string, starting at position + #from# and scanning forward until reaching the end of the + string. This function returns the position of the first + matching character of the sub-string. It returns #-1# if + string #str# cannot be found. */ + int search(const char *str, int from=0) const; + + /** Searches character #c# in the string, starting at position + #from# and scanning backwards until reaching the beginning of + the string. This function returns the position of the matching + character. It returns #-1# if character #c# cannot be found. */ + int rsearch(char c, const int from=0) const; + /** Searches sub-string #str# in the string, starting at position + #from# and scanning backwards until reaching the beginning of + the string. This function returns the position of the first + matching character of the sub-string. It returns #-1# if + string #str# cannot be found. */ + int rsearch(const char *str, const int from=0) const; + /** Searches for any of the specified characters in the accept + string. It returns #-1# if the none of the characters and + be found, otherwise the position of the first match. */ + int contains(const char accept[], const int from=0) const; + /** Searches for any of the specified characters in the accept + string. It returns #-1# if the none of the characters and be + found, otherwise the position of the last match. */ + int rcontains(const char accept[], const int from=0) const; + + /** Concatenates strings. Returns a string composed by concatenating + the characters of strings #s1# and #s2#. */ + GUTF8String operator+(const GUTF8String &s2) const; + GNativeString operator+(const GNativeString &s2) const; + + /** Returns an integer. Implements i18n atoi. */ + int toInt(void) const; + + /** Returns a long intenger. Implments i18n strtol. */ + long toLong(const int pos, int &endpos, const int base=10) const; + + /** Returns a unsigned long integer. Implements i18n strtoul. */ + unsigned long toULong( + const int pos, int &endpos, const int base=10) const; + + /** Returns a double. Implements the i18n strtod. */ + double toDouble( + const int pos, int &endpos ) const; + + /** Returns a long intenger. Implments i18n strtol. */ + static long toLong( + const GUTF8String& src, const int pos, int &endpos, const int base=10); + + static unsigned long toULong( + const GUTF8String& src, const int pos, int &endpos, const int base=10); + + static double toDouble( + const GUTF8String& src, const int pos, int &endpos); + + /** Returns a long intenger. Implments i18n strtol. */ + static long toLong( + const GNativeString& src, const int pos, int &endpos, const int base=10); + + static unsigned long toULong( + const GNativeString& src, const int pos, int &endpos, const int base=10); + + static double toDouble( + const GNativeString& src, const int pos, int &endpos); + + // -- HASHING + + // -- COMPARISONS + /** Returns an #int#. Compares string with #s2# and returns + sorting order. */ + int cmp(const GBaseString &s2, const int len=(-1)) const; + /** Returns an #int#. Compares string with #s2# and returns + sorting order. */ + int cmp(const char *s2, const int len=(-1)) const; + /** Returns an #int#. Compares string with #s2# and returns + sorting order. */ + int cmp(const char s2) const; + /** Returns an #int#. Compares #s2# with #s2# and returns + sorting order. */ + static int cmp(const char *s1, const char *s2, const int len=(-1)); + /** Returns a boolean. The Standard C strncmp takes two string and + compares the first N characters. static bool GBaseString::ncmp + will compare #s1# with #s2# with the #len# characters starting + from the beginning of the string. */ + /** String comparison. Returns true if and only if character + strings #s1# and #s2# are equal (as with #strcmp#.) + */ + bool operator==(const GBaseString &s2) const; + bool operator==(const char *s2) const; + friend bool operator==(const char *s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# and #s2# are not equal (as with #strcmp#.) + */ + bool operator!=(const GBaseString &s2) const; + bool operator!=(const char *s2) const; + friend bool operator!=(const char *s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically greater than or equal to + string #s2# (as with #strcmp#.) */ + bool operator>=(const GBaseString &s2) const; + bool operator>=(const char *s2) const; + bool operator>=(const char s2) const; + friend bool operator>=(const char *s1, const GBaseString &s2); + friend bool operator>=(const char s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically less than string #s2# + (as with #strcmp#.) + */ + bool operator<(const GBaseString &s2) const; + bool operator<(const char *s2) const; + bool operator<(const char s2) const; + friend bool operator<(const char *s1, const GBaseString &s2); + friend bool operator<(const char s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically greater than string #s2# + (as with #strcmp#.) + */ + bool operator> (const GBaseString &s2) const; + bool operator> (const char *s2) const; + bool operator> (const char s2) const; + friend bool operator> (const char *s1, const GBaseString &s2); + friend bool operator> (const char s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically less than or equal to string + #s2# (as with #strcmp#.) + */ + bool operator<=(const GBaseString &s2) const; + bool operator<=(const char *s2) const; + bool operator<=(const char s2) const; + friend bool operator<=(const char *s1, const GBaseString &s2); + friend bool operator<=(const char s1, const GBaseString &s2); + + /** Returns an integer. Implements a functional i18n atoi. Note + that if you pass a GBaseString that is not in Native format + the results may be disparaging. */ + + /** Returns a hash code for the string. This hashing function + helps when creating associative maps with string keys (see + \Ref{GMap}). This hash code may be reduced to an arbitrary + range by computing its remainder modulo the upper bound of + the range. */ + friend unsigned int hash(const GBaseString &ref); + // -- HELPERS + friend class GStringRep; + + /// Returns next non space position. + int nextNonSpace( const int from=0, const int len=(-1) ) const; + + /// Returns next character position. + int nextChar( const int from=0 ) const; + + /// Returns next non space position. + int nextSpace( const int from=0, const int len=(-1) ) const; + + /// return the position after the last non-whitespace character. + int firstEndSpace( const int from=0,const int len=(-1) ) const; + + /// Tests if the string is legally encoded in the current codepage. + bool is_valid(void) const; + + /// copy to a wchar_t buffer + int ncopy(wchar_t * const buf, const int buflen) const; + +protected: + const char *gstr; + static void throw_illegal_subscript() no_return; + static const char *nullstr; +public: + GNativeString UTF8ToNative( + const bool currentlocale=false, + const EscapeMode escape=UNKNOWN_ESCAPED) const; + GUTF8String NativeToUTF8(void) const; +protected: + int CheckSubscript(int n) const; +}; + +/** General purpose character string. + Each instance of class #GUTF8String# represents a character + string. Overloaded operators provide a value semantic to + #GUTF8String# objects. Conversion operators and constructors + transparently convert between #GUTF8String# objects and + #const char*# pointers. + + Functions taking strings as arguments should declare their + arguments as "#const char*#". Such functions will work equally + well with #GUTF8String# objects since there is a fast conversion + operator from #GUTF8String# to "#const char*#". Functions + returning strings should return #GUTF8String# or #GNativeString# + objects because the class will automatically manage the necessary + memory. + + Characters in the string can be identified by their position. The + first character of a string is numbered zero. Negative positions + represent characters relative to the end of the string (i.e. + position #-1# accesses the last character of the string, + position #-2# represents the second last character, etc.) */ + +class GUTF8String : public GBaseString +{ +public: + ~GUTF8String(); + void init(void); + + GUTF8String &init(const GP<GStringRep> &rep); + + // -- CONSTRUCTORS + /** Null constructor. Constructs an empty string. */ + GUTF8String(void); + /// Constructs a string from a character. + GUTF8String(const char dat); + /// Constructs a string from a null terminated character array. + GUTF8String(const char *str); + /// Constructs a string from a null terminated character array. + GUTF8String(const unsigned char *str); + GUTF8String(const unsigned short *dat); + GUTF8String(const unsigned long *dat); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GUTF8String(const char *dat, unsigned int len); + GUTF8String(const unsigned short *dat, unsigned int len); + GUTF8String(const unsigned long *dat, unsigned int len); + + /// Construct from base class. + GUTF8String(const GP<GStringRep> &str); + GUTF8String(const GBaseString &str); + GUTF8String(const GUTF8String &str); + GUTF8String(const GNativeString &str); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GUTF8String(const GBaseString &gs, int from, int len); + + /** Copy a null terminated character array. Resets this string + with the character string contained in the null terminated + character array #str#. */ + GUTF8String& operator= (const char str); + GUTF8String& operator= (const char *str); + GUTF8String& operator= (const GP<GStringRep> &str); + GUTF8String& operator= (const GBaseString &str); + GUTF8String& operator= (const GUTF8String &str); + GUTF8String& operator= (const GNativeString &str); + + /** Constructs a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer + than 32768 characters. */ + GUTF8String(const GUTF8String &fmt, va_list &args); + + /// Constructs a string from a character. + /** Constructs a string with a human-readable representation of + integer #number#. The format is similar to format #"%d"# in + function #printf#. */ + GUTF8String(const int number); + + /** Constructs a string with a human-readable representation of + floating point number #number#. The format is similar to + format #"%f"# in function #printf#. */ + GUTF8String(const double number); + + + /** Initializes a string with a formatted string (as in #printf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #printf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer + than 32768 characters. */ + GUTF8String &format(const char *fmt, ... ); + /** Initializes a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer + than 32768 characters. */ + GUTF8String &vformat(const GUTF8String &fmt, va_list &args); + + /** Returns a copy of this string with characters used in XML with + '<' to "<", '>' to ">", '&' to "&" '\'' to + "'", and '\"' to """. Characters 0x01 through + 0x1f are also escaped. */ + GUTF8String toEscaped( const bool tosevenbit=false ) const; + + /** Converts strings containing HTML/XML escaped characters into + their unescaped forms. Numeric representations of characters + (e.g., "&" or "&" for "*") are the only forms + converted by this function. */ + GUTF8String fromEscaped( void ) const; + + /** Converts strings containing HTML/XML escaped characters + (e.g., "<" for "<") into their unescaped forms. The + conversion is partially defined by the ConvMap argument which + specifies the conversion strings to be recognized. Numeric + representations of characters (e.g., "&" or "&" + for "*") are always converted. */ + GUTF8String fromEscaped( + const GMap<GUTF8String,GUTF8String> ConvMap ) const; + + + // -- CONCATENATION + /// Appends character #ch# to the string. + GUTF8String& operator+= (char ch); + + /// Appends the null terminated character array #str# to the string. + GUTF8String& operator+= (const char *str); + /// Appends the specified GBaseString to the string. + GUTF8String& operator+= (const GBaseString &str); + + /** Returns a sub-string. The sub-string is composed by copying + #len# characters starting at position #from# in this string. + The length of the resulting string may be smaller than #len# + if the specified range is too large. */ + GUTF8String substr(int from, int len/*=(-1)*/) const; + + /** Returns an upper case copy of this string. The returned string + contains a copy of the current string with all letters turned + into upper case letters. */ + GUTF8String upcase( void ) const; + /** Returns an lower case copy of this string. The returned string + contains a copy of the current string with all letters turned + into lower case letters. */ + GUTF8String downcase( void ) const; + + /** Concatenates strings. Returns a string composed by concatenating + the characters of strings #s1# and #s2#. + */ + GUTF8String operator+(const GBaseString &s2) const; + GUTF8String operator+(const GUTF8String &s2) const; + GUTF8String operator+(const GNativeString &s2) const; + GUTF8String operator+(const char *s2) const; + friend GUTF8String operator+(const char *s1, const GUTF8String &s2); + + /** Provides a direct access to the string buffer. Returns a + pointer for directly accessing the string buffer. This pointer + valid remains valid as long as the string is not modified by + other means. Positive values for argument #n# represent the + length of the returned buffer. The returned string buffer will + be large enough to hold at least #n# characters plus a null + character. If #n# is positive but smaller than the string + length, the string will be truncated to #n# characters. */ + char *getbuf(int n = -1); + /** Set the character at position #n# to value #ch#. An exception + \Ref{GException} is thrown if number #n# is not in range #-len# + to #len#, where #len# is the length of the string. If character + #ch# is zero, the string is truncated at position #n#. The + first character of a string is numbered zero. Negative + positions represent characters relative to the end of the + string. If position #n# is equal to the length of the string, + this function appends character #ch# to the end of the string. */ + void setat(const int n, const char ch); +public: + typedef enum GStringRep::EncodeType EncodeType; + static GUTF8String create(void const * const buf, + const unsigned int size, + const EncodeType encodetype, const GUTF8String &encoding); + static GUTF8String create( void const * const buf, + unsigned int size, const EncodeType encodetype ); + static GUTF8String create( void const * const buf, + const unsigned int size, const GUTF8String &encoding ); + static GUTF8String create( void const * const buf, + const unsigned int size, const GP<GStringRep::Unicode> &remainder); + GP<GStringRep::Unicode> get_remainder(void) const; + static GUTF8String create( const char *buf, const unsigned int bufsize ); + static GUTF8String create( const unsigned short *buf, const unsigned int bufsize ); + static GUTF8String create( const unsigned long *buf, const unsigned int bufsize ); +}; + + +#if !HAS_WCHAR +#define GBaseString GUTF8String +#endif + +/** General purpose character string. + Each instance of class #GNativeString# represents a character + string. Overloaded operators provide a value semantic to + #GNativeString# objects. Conversion operators and constructors + transparently convert between #GNativeString# objects and + #const char*# pointers. + + Functions taking strings as arguments should declare their + arguments as "#const char*#". Such functions will work equally + well with #GNativeString# objects since there is a fast conversion + operator from #GNativeString# to "#const char*#". Functions + returning strings should return #GUTF8String# or #GNativeString# + objects because the class will automatically manage the necessary + memory. + + Characters in the string can be identified by their position. The + first character of a string is numbered zero. Negative positions + represent characters relative to the end of the string (i.e. + position #-1# accesses the last character of the string, + position #-2# represents the second last character, etc.) */ + +class GNativeString : public GBaseString +{ +public: + ~GNativeString(); + // -- CONSTRUCTORS + /** Null constructor. Constructs an empty string. */ + GNativeString(void); + /// Constructs a string from a character. + GNativeString(const char dat); + /// Constructs a string from a null terminated character array. + GNativeString(const char *str); + /// Constructs a string from a null terminated character array. + GNativeString(const unsigned char *str); + GNativeString(const unsigned short *str); + GNativeString(const unsigned long *str); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GNativeString(const char *dat, unsigned int len); + GNativeString(const unsigned short *dat, unsigned int len); + GNativeString(const unsigned long *dat, unsigned int len); + /// Construct from base class. + GNativeString(const GP<GStringRep> &str); + GNativeString(const GBaseString &str); +#if HAS_WCHAR + GNativeString(const GUTF8String &str); +#endif + GNativeString(const GNativeString &str); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GNativeString(const GBaseString &gs, int from, int len); + + /** Constructs a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer than + 32768 characters. */ + GNativeString(const GNativeString &fmt, va_list &args); + + /** Constructs a string with a human-readable representation of + integer #number#. The format is similar to format #"%d"# in + function #printf#. */ + GNativeString(const int number); + + /** Constructs a string with a human-readable representation of + floating point number #number#. The format is similar to + format #"%f"# in function #printf#. */ + GNativeString(const double number); + +#if !HAS_WCHAR +#undef GBaseString +#else + /// Initialize this string class + void init(void); + + /// Initialize this string class + GNativeString &init(const GP<GStringRep> &rep); + + /** Copy a null terminated character array. Resets this string with + the character string contained in the null terminated character + array #str#. */ + GNativeString& operator= (const char str); + GNativeString& operator= (const char *str); + GNativeString& operator= (const GP<GStringRep> &str); + GNativeString& operator= (const GBaseString &str); + GNativeString& operator= (const GUTF8String &str); + GNativeString& operator= (const GNativeString &str); + // -- CONCATENATION + /// Appends character #ch# to the string. + GNativeString& operator+= (char ch); + /// Appends the null terminated character array #str# to the string. + GNativeString& operator+= (const char *str); + /// Appends the specified GBaseString to the string. + GNativeString& operator+= (const GBaseString &str); + + /** Returns a sub-string. The sub-string is composed by copying + #len# characters starting at position #from# in this string. + The length of the resulting string may be smaller than #len# + if the specified range is too large. */ + GNativeString substr(int from, int len/*=(-1)*/) const; + + /** Returns an upper case copy of this string. The returned + string contains a copy of the current string with all letters + turned into upper case letters. */ + GNativeString upcase( void ) const; + /** Returns an lower case copy of this string. The returned + string contains a copy of the current string with all letters + turned into lower case letters. */ + GNativeString downcase( void ) const; + + + GNativeString operator+(const GBaseString &s2) const; + GNativeString operator+(const GNativeString &s2) const; + GUTF8String operator+(const GUTF8String &s2) const; + GNativeString operator+(const char *s2) const; + friend GNativeString operator+(const char *s1, const GNativeString &s2); + + /** Initializes a string with a formatted string (as in #printf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #printf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer than + 32768 characters. */ + GNativeString &format(const char *fmt, ... ); + /** Initializes a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer than + 32768 characters. */ + GNativeString &vformat(const GNativeString &fmt, va_list &args); + + /** Returns a copy of this string with characters used in XML with + '<' to "<", '>' to ">", '&' to "&" '\'' to + "'", and '\"' to """. Characters 0x01 through + 0x1f are also escaped. */ + GNativeString toEscaped( const bool tosevenbit=false ) const; + + + /** Provides a direct access to the string buffer. Returns a + pointer for directly accessing the string buffer. This + pointer valid remains valid as long as the string is not + modified by other means. Positive values for argument #n# + represent the length of the returned buffer. The returned + string buffer will be large enough to hold at least #n# + characters plus a null character. If #n# is positive but + smaller than the string length, the string will be truncated + to #n# characters. */ + char *getbuf(int n = -1); + /** Set the character at position #n# to value #ch#. An exception + \Ref{GException} is thrown if number #n# is not in range #-len# + to #len#, where #len# is the length of the string. If + character #ch# is zero, the string is truncated at position + #n#. The first character of a string is numbered zero. + Negative positions represent characters relative to the end of + the string. If position #n# is equal to the length of the + string, this function appends character #ch# to the end of the + string. */ + void setat(const int n, const char ch); + + static GNativeString create( const char *buf, const unsigned int bufsize ); + static GNativeString create( const unsigned short *buf, const unsigned int bufsize ); + static GNativeString create( const unsigned long *buf, const unsigned int bufsize ); +#endif // WinCE +}; + +//@} + +inline +GBaseString::operator const char* ( void ) const +{ + return ptr?(*this)->data:nullstr; +} + +inline unsigned int +GBaseString::length( void ) const +{ + return ptr ? (*this)->size : 0; +} + +inline bool +GBaseString::operator! ( void ) const +{ + return !ptr; +} + +inline GUTF8String +GUTF8String::upcase( void ) const +{ + if (ptr) return (*this)->upcase(); + return *this; +} + +inline GUTF8String +GUTF8String::downcase( void ) const +{ + if (ptr) return (*this)->downcase(); + return *this; +} + +inline void +GUTF8String::init(void) +{ GBaseString::init(); } + +inline GUTF8String & +GUTF8String::init(const GP<GStringRep> &rep) +{ GP<GStringRep>::operator=(rep?rep->toUTF8(true):rep); init(); return *this; } + +inline GUTF8String & +GUTF8String::vformat(const GUTF8String &fmt, va_list &args) +{ return (*this = (fmt.ptr?GUTF8String(fmt,args):fmt)); } + +inline GUTF8String +GUTF8String::toEscaped( const bool tosevenbit ) const +{ return ptr?GUTF8String((*this)->toEscaped(tosevenbit)):(*this); } + +inline GP<GStringRep::Unicode> +GUTF8String::get_remainder(void) const +{ + GP<GStringRep::Unicode> retval; + if(ptr) + retval=((*this)->get_remainder()); + return retval; +} + +inline +GUTF8String::GUTF8String(const GNativeString &str) +{ init(str.length()?(str->toUTF8(true)):(GP<GStringRep>)str); } + +inline +GUTF8String::GUTF8String(const GP<GStringRep> &str) +{ init(str?(str->toUTF8(true)):str); } + +inline +GUTF8String::GUTF8String(const GBaseString &str) +{ init(str.length()?(str->toUTF8(true)):(GP<GStringRep>)str); } + +inline void +GBaseString::init(void) +{ + gstr=ptr?((*this)->data):nullstr; +} +/** Returns an integer. Implements i18n atoi. */ +inline int +GBaseString::toInt(void) const +{ return ptr?(*this)->toInt():0; } + +/** Returns a long intenger. Implments i18n strtol. */ +inline long +GBaseString::toLong(const int pos, int &endpos, const int base) const +{ + long int retval=0; + if(ptr) + { + retval=(*this)->toLong(pos, endpos, base); + }else + { + endpos=(-1); + } + return retval; +} + +inline long +GBaseString::toLong( + const GUTF8String& src, const int pos, int &endpos, const int base) +{ + return src.toLong(pos,endpos,base); +} + +inline long +GBaseString::toLong( + const GNativeString& src, const int pos, int &endpos, const int base) +{ + return src.toLong(pos,endpos,base); +} + +/** Returns a unsigned long integer. Implements i18n strtoul. */ +inline unsigned long +GBaseString::toULong(const int pos, int &endpos, const int base) const +{ + unsigned long retval=0; + if(ptr) + { + retval=(*this)->toULong(pos, endpos, base); + }else + { + endpos=(-1); + } + return retval; +} + +inline unsigned long +GBaseString::toULong( + const GUTF8String& src, const int pos, int &endpos, const int base) +{ + return src.toULong(pos,endpos,base); +} + +inline unsigned long +GBaseString::toULong( + const GNativeString& src, const int pos, int &endpos, const int base) +{ + return src.toULong(pos,endpos,base); +} + +/** Returns a double. Implements the i18n strtod. */ +inline double +GBaseString::toDouble( + const int pos, int &endpos ) const +{ + double retval=(double)0; + if(ptr) + { + retval=(*this)->toDouble(pos, endpos); + }else + { + endpos=(-1); + } + return retval; +} + +inline double +GBaseString::toDouble( + const GUTF8String& src, const int pos, int &endpos) +{ + return src.toDouble(pos,endpos); +} + +inline double +GBaseString::toDouble( + const GNativeString& src, const int pos, int &endpos) +{ + return src.toDouble(pos,endpos); +} + +inline GBaseString & +GBaseString::init(const GP<GStringRep> &rep) +{ GP<GStringRep>::operator=(rep); init(); return *this;} + +inline char +GBaseString::operator[] (int n) const +{ return ((n||ptr)?((*this)->data[CheckSubscript(n)]):0); } + +inline int +GBaseString::search(char c, int from) const +{ return ptr?((*this)->search(c,from)):(-1); } + +inline int +GBaseString::search(const char *str, int from) const +{ return ptr?((*this)->search(str,from)):(-1); } + +inline int +GBaseString::rsearch(char c, const int from) const +{ return ptr?((*this)->rsearch(c,from)):(-1); } + +inline int +GBaseString::rsearch(const char *str, const int from) const +{ return ptr?((*this)->rsearch(str,from)):(-1); } + +inline int +GBaseString::contains(const char accept[], const int from) const +{ return ptr?((*this)->contains(accept,from)):(-1); } + +inline int +GBaseString::rcontains(const char accept[], const int from) const +{ return ptr?((*this)->rcontains(accept,from)):(-1); } + +inline int +GBaseString::cmp(const GBaseString &s2, const int len) const +{ return GStringRep::cmp(*this,s2,len); } + +inline int +GBaseString::cmp(const char *s2, const int len) const +{ return GStringRep::cmp(*this,s2,len); } + +inline int +GBaseString::cmp(const char s2) const +{ return GStringRep::cmp(*this,&s2,1); } + +inline int +GBaseString::cmp(const char *s1, const char *s2, const int len) +{ return GStringRep::cmp(s1,s2,len); } + +inline bool +GBaseString::operator==(const GBaseString &s2) const +{ return !cmp(s2); } + +inline bool +GBaseString::operator==(const char *s2) const +{ return !cmp(s2); } + +inline bool +GBaseString::operator!=(const GBaseString &s2) const +{ return !!cmp(s2); } + +inline bool +GBaseString::operator!=(const char *s2) const +{ return !!cmp(s2); } + +inline bool +GBaseString::operator>=(const GBaseString &s2) const +{ return (cmp(s2)>=0); } + +inline bool +GBaseString::operator>=(const char *s2) const +{ return (cmp(s2)>=0); } + +inline bool +GBaseString::operator>=(const char s2) const +{ return (cmp(s2)>=0); } + +inline bool +GBaseString::operator<(const GBaseString &s2) const +{ return (cmp(s2)<0); } + +inline bool +GBaseString::operator<(const char *s2) const +{ return (cmp(s2)<0); } + +inline bool +GBaseString::operator<(const char s2) const +{ return (cmp(s2)<0); } + +inline bool +GBaseString::operator> (const GBaseString &s2) const +{ return (cmp(s2)>0); } + +inline bool +GBaseString::operator> (const char *s2) const +{ return (cmp(s2)>0); } + +inline bool +GBaseString::operator> (const char s2) const +{ return (cmp(s2)>0); } + +inline bool +GBaseString::operator<=(const GBaseString &s2) const +{ return (cmp(s2)<=0); } + +inline bool +GBaseString::operator<=(const char *s2) const +{ return (cmp(s2)<=0); } + +inline bool +GBaseString::operator<=(const char s2) const +{ return (cmp(s2)<=0); } + +inline int +GBaseString::nextNonSpace( const int from, const int len ) const +{ return ptr?(*this)->nextNonSpace(from,len):0; } + +inline int +GBaseString::nextChar( const int from ) const +{ return ptr?(*this)->nextChar(from):0; } + +inline int +GBaseString::nextSpace( const int from, const int len ) const +{ return ptr?(*this)->nextSpace(from,len):0; } + +inline int +GBaseString::firstEndSpace( const int from,const int len ) const +{ return ptr?(*this)->firstEndSpace(from,len):0; } + +inline bool +GBaseString::is_valid(void) const +{ return ptr?((*this)->is_valid()):true; } + +inline int +GBaseString::ncopy(wchar_t * const buf, const int buflen) const +{if(buf&&buflen)buf[0]=0;return ptr?((*this)->ncopy(buf,buflen)):0;} + +inline int +GBaseString::CheckSubscript(int n) const +{ + if(n) + { + if (n<0 && ptr) + n += (*this)->size; + if (n<0 || !ptr || n > (int)(*this)->size) + throw_illegal_subscript(); + } + return n; +} + +inline GBaseString::GBaseString(void) { init(); } + +inline GUTF8String::GUTF8String(void) { } + +inline GUTF8String::GUTF8String(const GUTF8String &str) : GBaseString(str) +{ init(str); } + +inline GUTF8String& GUTF8String::operator= (const GP<GStringRep> &str) +{ return init(str); } + +inline GUTF8String& GUTF8String::operator= (const GBaseString &str) +{ return init(str); } + +inline GUTF8String& GUTF8String::operator= (const GUTF8String &str) +{ return init(str); } + +inline GUTF8String& GUTF8String::operator= (const GNativeString &str) +{ return init(str); } + +inline GUTF8String +GUTF8String::create( const char *buf, const unsigned int bufsize ) +{ +#if HAS_WCHAR + return GNativeString(buf,bufsize); +#else + return GUTF8String(buf,bufsize); +#endif +} + +inline GUTF8String +GUTF8String::create( const unsigned short *buf, const unsigned int bufsize ) +{ + return GUTF8String(buf,bufsize); +} + +inline GUTF8String +GUTF8String::create( const unsigned long *buf, const unsigned int bufsize ) +{ + return GUTF8String(buf,bufsize); +} + +inline GNativeString::GNativeString(void) {} + +#if !HAS_WCHAR +// For Windows CE, GNativeString is essentially GUTF8String + +inline +GNativeString::GNativeString(const GUTF8String &str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const GP<GStringRep> &str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const char dat) +: GUTF8String(dat) {} + +inline +GNativeString::GNativeString(const char *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const unsigned char *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const unsigned short *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const unsigned long *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const char *dat, unsigned int len) +: GUTF8String(dat,len) {} + +inline +GNativeString::GNativeString(const unsigned short *dat, unsigned int len) +: GUTF8String(dat,len) {} + +inline +GNativeString::GNativeString(const unsigned long *dat, unsigned int len) +: GUTF8String(dat,len) {} + +inline +GNativeString::GNativeString(const GNativeString &str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const int number) +: GUTF8String(number) {} + +inline +GNativeString::GNativeString(const double number) +: GUTF8String(number) {} + +inline +GNativeString::GNativeString(const GNativeString &fmt, va_list &args) +: GUTF8String(fmt,args) {} + +#else // HAS_WCHAR + +/// Initialize this string class +inline void +GNativeString::init(void) +{ GBaseString::init(); } + +/// Initialize this string class +inline GNativeString & +GNativeString::init(const GP<GStringRep> &rep) +{ + GP<GStringRep>::operator=(rep?rep->toNative(GStringRep::NOT_ESCAPED):rep); + init(); + return *this; +} + +inline GNativeString +GNativeString::substr(int from, int len) const +{ return GNativeString(*this, from, len); } + +inline GNativeString & +GNativeString::vformat(const GNativeString &fmt, va_list &args) +{ return (*this = (fmt.ptr?GNativeString(fmt,args):fmt)); } + +inline GNativeString +GNativeString::toEscaped( const bool tosevenbit ) const +{ return ptr?GNativeString((*this)->toEscaped(tosevenbit)):(*this); } + +inline +GNativeString::GNativeString(const GUTF8String &str) +{ + if (str.length()) + init(str->toNative(GStringRep::NOT_ESCAPED)); + else + init((GP<GStringRep>)str); +} + +inline +GNativeString::GNativeString(const GP<GStringRep> &str) +{ + if (str) + init(str->toNative(GStringRep::NOT_ESCAPED)); + else + init(str); +} + +inline +GNativeString::GNativeString(const GBaseString &str) +{ + if (str.length()) + init(str->toNative(GStringRep::NOT_ESCAPED)); + else + init((GP<GStringRep>)str); +} + + +inline +GNativeString::GNativeString(const GNativeString &fmt, va_list &args) +{ + if (fmt.ptr) + init(fmt->vformat(args)); + else + init(fmt); +} + +inline GNativeString +GNativeString::create( const char *buf, const unsigned int bufsize ) +{ + return GNativeString(buf,bufsize); +} + +inline GNativeString +GNativeString::create( const unsigned short *buf, const unsigned int bufsize ) +{ + return GNativeString(buf,bufsize); +} + +inline GNativeString +GNativeString::create( const unsigned long *buf, const unsigned int bufsize ) +{ + return GNativeString(buf,bufsize); +} + +inline GNativeString& +GNativeString::operator= (const GP<GStringRep> &str) +{ return init(str); } + +inline GNativeString& +GNativeString::operator= (const GBaseString &str) +{ return init(str); } + +inline GNativeString& +GNativeString::operator= (const GUTF8String &str) +{ return init(str); } + +inline GNativeString& +GNativeString::operator= (const GNativeString &str) +{ return init(str); } + +inline GNativeString +GNativeString::upcase( void ) const +{ + if (ptr) return (*this)->upcase(); + return *this; +} + +inline GNativeString +GNativeString::downcase( void ) const +{ + if (ptr) return (*this)->downcase(); + return *this; +} + +#endif // HAS_WCHAR + +inline bool +operator==(const char *s1, const GBaseString &s2) +{ return !s2.cmp(s1); } + +inline bool +operator!=(const char *s1, const GBaseString &s2) +{ return !!s2.cmp(s1); } + +inline bool +operator>=(const char *s1, const GBaseString &s2) +{ return (s2.cmp(s1)<=0); } + +inline bool +operator>=(const char s1, const GBaseString &s2) +{ return (s2.cmp(s1)<=0); } + +inline bool +operator<(const char *s1, const GBaseString &s2) +{ return (s2.cmp(s1)>0); } + +inline bool +operator<(const char s1, const GBaseString &s2) +{ return (s2.cmp(s1)>0); } + +inline bool +operator> (const char *s1, const GBaseString &s2) +{ return (s2.cmp(s1)<0); } + +inline bool +operator> (const char s1, const GBaseString &s2) +{ return (s2.cmp(s1)<0); } + +inline bool +operator<=(const char *s1, const GBaseString &s2) +{ return !(s1>s2); } + +inline bool +operator<=(const char s1, const GBaseString &s2) +{ return !(s1>s2); } + +// ------------------- The end + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GThreads.cpp b/kviewshell/plugins/djvu/libdjvu/GThreads.cpp new file mode 100644 index 00000000..ce88361e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GThreads.cpp @@ -0,0 +1,1887 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GThreads.cpp,v 1.15 2004/04/21 14:54:43 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// This file defines machine independent classes +// for running and synchronizing threads. +// - Author: Leon Bottou, 01/1998 + +// From: Leon Bottou, 1/31/2002 +// Almost unchanged by Lizardtech. +// GSafeFlags should go because it not as safe as it claims. + +#include "GThreads.h" +#include "GException.h" +#include "DjVuMessageLite.h" +#include <stdlib.h> +#include <stdio.h> + +// ---------------------------------------- +// Consistency check + +#if THREADMODEL!=NOTHREADS +#ifdef USE_EXCEPTION_EMULATION +#warning "Compiler must support thread safe exceptions" +#endif //USE_EXCEPTION_EMULATION +#if defined(__GNUC__) +#if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=8)) +#warning "GCC 2.8 exceptions are not thread safe." +#warning "Use properly configured EGCS-1.1 or greater." +#endif // (__GNUC__<2 ... +#endif // defined(__GNUC__) +#endif // THREADMODEL!=NOTHREADS + +#ifndef _DEBUG +#if defined(DEBUG) +#define _DEBUG /* */ +#elif DEBUGLVL >= 1 +#define _DEBUG /* */ +#endif +#endif + +#if THREADMODEL==WINTHREADS +# include <process.h> +#endif +#if THREADMODEL==COTHREADS +# include <setjmp.h> +# include <string.h> +# include <unistd.h> +# include <sys/types.h> +# include <sys/time.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ---------------------------------------- +// NOTHREADS +// ---------------------------------------- + +#if THREADMODEL==NOTHREADS +int +GThread::create( void (*entry)(void*), void *arg) +{ + (*entry)(arg); + return 0; +} +#endif + + +// ---------------------------------------- +// WIN32 IMPLEMENTATION +// ---------------------------------------- + +#if THREADMODEL==WINTHREADS + +static unsigned __stdcall +start(void *arg) +{ + GThread *gt = (GThread*)arg; + try + { + G_TRY + { + gt->xentry( gt->xarg ); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } + return 0; +} + +GThread::GThread(int stacksize) + : hthr(0), thrid(0), xentry(0), xarg(0) +{ +} + +GThread::~GThread() +{ + if (hthr) + CloseHandle(hthr); + hthr = 0; + thrid = 0; +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (hthr) + return -1; + xentry = entry; + xarg = arg; + unsigned uthread = 0; + hthr = (HANDLE)_beginthreadex(NULL, 0, start, (void*)this, 0, &uthread); + thrid = (DWORD) uthread; + if (hthr) + return 0; + return -1; +} + +void +GThread::terminate() +{ + OutputDebugString("Terminating thread.\n"); + if (hthr) + TerminateThread(hthr,0); +} + +int +GThread::yield() +{ + Sleep(0); + return 0; +} + +void * +GThread::current() +{ + return (void*) GetCurrentThreadId(); +} + +struct thr_waiting { + struct thr_waiting *next; + struct thr_waiting *prev; + BOOL waiting; + HANDLE gwait; +}; + +GMonitor::GMonitor() + : ok(0), count(1), head(0), tail(0) +{ + InitializeCriticalSection(&cs); + locker = GetCurrentThreadId(); + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + EnterCriticalSection(&cs); + for (struct thr_waiting *w=head; w; w=w->next) + SetEvent(w->gwait); + LeaveCriticalSection(&cs); + DeleteCriticalSection(&cs); +} + +void +GMonitor::enter() +{ + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + { + if (ok) + EnterCriticalSection(&cs); + locker = self; + count = 1; + } + count -= 1; +} + +void +GMonitor::leave() +{ + DWORD self = GetCurrentThreadId(); + if (ok && (count>0 || self!=locker)) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + count += 1; + if (count > 0) + { + count = 1; + if (ok) + LeaveCriticalSection(&cs); + } +} + +void +GMonitor::signal() +{ + if (ok) + { + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + for (struct thr_waiting *w=head; w; w=w->next) + if (w->waiting) + { + SetEvent(w->gwait); + w->waiting = FALSE; + break; // Only one thread is allowed to run! + } + } +} + +void +GMonitor::broadcast() +{ + if (ok) + { + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + for (struct thr_waiting *w=head; w; w=w->next) + if (w->waiting) + { + SetEvent(w->gwait); + w->waiting = FALSE; + } + } +} + +void +GMonitor::wait() +{ + // Check state + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Prepare wait record + struct thr_waiting waitrec; + waitrec.waiting = TRUE; + waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL); + waitrec.next = 0; + waitrec.prev = tail; + // Link wait record (protected by critical section) + *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; + *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec; + // Start wait + int sav_count = count; + count = 1; + LeaveCriticalSection(&cs); + WaitForSingleObject(waitrec.gwait,INFINITE); + // Re-acquire + EnterCriticalSection(&cs); + count = sav_count; + locker = self; + // Unlink wait record + *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev; + *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next; + CloseHandle(waitrec.gwait); + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Check state + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Prepare wait record + struct thr_waiting waitrec; + waitrec.waiting = TRUE; + waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL); + waitrec.next = 0; + waitrec.prev = tail; + // Link wait record (protected by critical section) + *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec; + *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; + // Start wait + int sav_count = count; + count = 1; + LeaveCriticalSection(&cs); + WaitForSingleObject(waitrec.gwait,timeout); + // Re-acquire + EnterCriticalSection(&cs); + count = sav_count; + locker = self; + // Unlink wait record + *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev; + *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next; + CloseHandle(waitrec.gwait); + } +} + +#endif + + + +// ---------------------------------------- +// MACTHREADS IMPLEMENTATION (from Praveen) +// ---------------------------------------- + +#if THREADMODEL==MACTHREADS + +// Doubly linked list of waiting threads +struct thr_waiting { + struct thr_waiting *next; // ptr to next waiting thread record + struct thr_waiting *prev; // ptr to ptr to this waiting thread + unsigned long thid; // id of waiting thread + int *wchan; // cause of the wait +}; +static struct thr_waiting *first_waiting_thr = 0; +static struct thr_waiting *last_waiting_thr = 0; + + +// Stops current thread. +// Argument ``self'' must be current thread id. +// Assumes ``ThreadBeginCritical'' has been called before. +static void +macthread_wait(ThreadID self, int *wchan) +{ + // Prepare and link wait record + struct thr_waiting wait; // no need to malloc :-) + wait.thid = self; + wait.wchan = wchan; + wait.next = 0; + wait.prev = last_waiting_thr; + *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = &wait; + *(wait.next ? &wait.next->prev : &last_waiting_thr ) = &wait; + // Leave critical section and start waiting. + (*wchan)++; + SetThreadStateEndCritical(self, kStoppedThreadState, kNoThreadID); + // The Apple documentation says that the above call reschedules a new + // thread. Therefore it will only return when the thread wakes up. + ThreadBeginCritical(); + (*wchan)--; + // Unlink wait record + *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = wait.next; + *(wait.next ? &wait.next->prev : &last_waiting_thr ) = wait.prev; + // Returns from the wait. +} + +// Wakeup one thread or all threads waiting on cause wchan +static void +macthread_wakeup(int *wchan, int onlyone) +{ + if (*wchan == 0) + return; + for (struct thr_waiting *q=first_waiting_thr; q; q=q->next) + if (q->wchan == wchan) { + // Found a waiting thread + q->wchan = 0; + SetThreadState(q->thid, kReadyThreadState, kNoThreadID); + if (onlyone) + return; + } +} + +GThread::GThread(int stacksize) + : thid(kNoThreadID), xentry(0), xarg(0) +{ +} + +GThread::~GThread(void) +{ + thid = kNoThreadID; +} + +pascal void * +GThread::start(void *arg) +{ + GThread *gt = (GThread*)arg; + try + { + G_TRY + { + (gt->xentry)(gt->xarg); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } + return 0; +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (xentry || thid!=kNoThreadID) + return -1; + xentry = entry; + xarg = arg; + int err = NewThread( kCooperativeThread, GThread::start , this, 0, + kCreateIfNeeded, (void**)nil, &thid ); + if( err != noErr ) + return err; + return 0; +} + +void +GThread::terminate() +{ + if (thid != kNoThreadID) { + DisposeThread( thid, NULL, false ); + thid = kNoThreadID; + } +} + +int +GThread::yield() +{ + YieldToAnyThread(); + return 0; +} + +void* +GThread::current() +{ + unsigned long thid = kNoThreadID; + GetCurrentThread(&thid); + return (void*) thid; +} + + +// GMonitor implementation +GMonitor::GMonitor() + : ok(0), count(1), locker(0), wlock(0), wsig(0) +{ + locker = kNoThreadID; + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + ThreadBeginCritical(); + macthread_wakeup(&wsig, 0); + macthread_wakeup(&wlock, 0); + ThreadEndCritical(); + YieldToAnyThread(); +} + +void +GMonitor::enter() +{ + ThreadID self; + GetCurrentThread(&self); + ThreadBeginCritical(); + if (count>0 || self!=locker) + { + while (ok && count<=0) + macthread_wait(self, &wlock); + count = 1; + locker = self; + } + count -= 1; + ThreadEndCritical(); +} + +void +GMonitor::leave() +{ + ThreadID self; + GetCurrentThread(&self); + if (ok && (count>0 || self!=locker)) + G_THROW( ERR_MSG("GThreads.not_acq_leave") ); + ThreadBeginCritical(); + if (++count > 0) + macthread_wakeup(&wlock, 1); + ThreadEndCritical(); +} + +void +GMonitor::signal() +{ + ThreadID self; + GetCurrentThread(&self); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + ThreadBeginCritical(); + macthread_wakeup(&wsig, 1); + ThreadEndCritical(); +} + +void +GMonitor::broadcast() +{ + ThreadID self; + GetCurrentThread(&self); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + ThreadBeginCritical(); + macthread_wakeup(&wsig, 0); + ThreadEndCritical(); +} + +void +GMonitor::wait() +{ + // Check state + ThreadID self; + GetCurrentThread(&self); + if (count>0 || locker!=self) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Atomically release monitor and wait + ThreadBeginCritical(); + int sav_count = count; + count = 1; + macthread_wakeup(&wlock, 1); + macthread_wait(self, &wsig); + // Re-acquire + while (ok && count<=0) + macthread_wait(self, &wlock); + count = sav_count; + locker = self; + ThreadEndCritical(); + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Timeouts are not used for anything important. + // Just ignore the timeout and wait the regular way. + wait(); +} + +#endif + + + +// ---------------------------------------- +// POSIXTHREADS IMPLEMENTATION +// ---------------------------------------- + +#if THREADMODEL==POSIXTHREADS + +#if defined(CMA_INCLUDE) +#define DCETHREADS +#define pthread_key_create pthread_keycreate +#else +#define pthread_mutexattr_default NULL +#define pthread_condattr_default NULL +#endif + + +void * +GThread::start(void *arg) +{ + GThread *gt = (GThread*)arg; +#ifdef DCETHREADS +#ifdef CANCEL_ON + pthread_setcancel(CANCEL_ON); + pthread_setasynccancel(CANCEL_ON); +#endif +#else // !DCETHREADS +#ifdef PTHREAD_CANCEL_ENABLE + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); +#endif +#ifdef PTHREAD_CANCEL_ASYNCHRONOUS + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); +#endif +#endif + // Catch exceptions +#ifdef __EXCEPTIONS + try + { +#endif + G_TRY + { + (gt->xentry)(gt->xarg); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; +#ifdef __EXCEPTIONS + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } +#endif + return 0; +} + + +// GThread + +GThread::GThread(int stacksize) : + hthr(0), xentry(0), xarg(0) +{ +} + +GThread::~GThread() +{ + hthr = 0; +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (xentry || xarg) + return -1; + xentry = entry; + xarg = arg; +#ifdef DCETHREADS + int ret = pthread_create(&hthr, pthread_attr_default, GThread::start, (void*)this); + if (ret >= 0) + pthread_detach(hthr); +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + int ret = pthread_create(&hthr, &attr, start, (void*)this); + pthread_attr_destroy(&attr); +#endif + return ret; +} + +void +GThread::terminate() +{ + if (xentry || xarg) + pthread_cancel(hthr); +} + +int +GThread::yield() +{ +#ifdef DCETHREADS + pthread_yield(); +#else + // should use sched_yield() when available. + static struct timeval timeout = { 0, 0 }; + ::select(0, 0,0,0, &timeout); +#endif + return 0; +} + +void* +GThread::current() +{ + pthread_t self = pthread_self(); +#if defined(pthread_getunique_np) + return (void*) pthread_getunique_np( & self ); +#elif defined(cma_thread_get_unique) + return (void*) cma_thread_get_unique( & self ); +#else + return (void*) self; +#endif +} + +// -- GMonitor + +GMonitor::GMonitor() + : ok(0), count(1), locker(0) +{ + // none of this should be necessary ... in theory. +#ifdef PTHREAD_MUTEX_INITIALIZER + static pthread_mutex_t tmutex=PTHREAD_MUTEX_INITIALIZER; + memcpy(&mutex,&tmutex,sizeof(mutex)); +#endif +#ifdef PTHREAD_COND_INITIALIZER + static pthread_cond_t tcond=PTHREAD_COND_INITIALIZER; + memcpy(&cond,&tcond,sizeof(cond)); +#endif + // standard + pthread_mutex_init(&mutex, pthread_mutexattr_default); + pthread_cond_init(&cond, pthread_condattr_default); + locker = pthread_self(); + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); +} + + +void +GMonitor::enter() +{ + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + { + if (ok) + pthread_mutex_lock(&mutex); + locker = self; + count = 1; + } + count -= 1; +} + +void +GMonitor::leave() +{ + pthread_t self = pthread_self(); + if (ok && (count>0 || !pthread_equal(locker, self))) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + count += 1; + if (count > 0) + { + count = 1; + if (ok) + pthread_mutex_unlock(&mutex); + } +} + +void +GMonitor::signal() +{ + if (ok) + { + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + pthread_cond_signal(&cond); + } +} + +void +GMonitor::broadcast() +{ + if (ok) + { + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + pthread_cond_broadcast(&cond); + } +} + +void +GMonitor::wait() +{ + // Check + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Release + int sav_count = count; + count = 1; + // Wait + pthread_cond_wait(&cond, &mutex); + // Re-acquire + count = sav_count; + locker = self; + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Check + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Release + int sav_count = count; + count = 1; + // Wait + struct timeval abstv; + struct timespec absts; + gettimeofday(&abstv, NULL); // grrr + absts.tv_sec = abstv.tv_sec + timeout/1000; + absts.tv_nsec = abstv.tv_usec*1000 + (timeout%1000)*1000000; + if (absts.tv_nsec > 1000000000) { + absts.tv_nsec -= 1000000000; + absts.tv_sec += 1; + } + pthread_cond_timedwait(&cond, &mutex, &absts); + // Re-acquire + count = sav_count; + locker = self; + } +} + +#endif + + + +// ---------------------------------------- +// CUSTOM COOPERATIVE THREADS +// ---------------------------------------- + +#if THREADMODEL==COTHREADS + +#ifndef __GNUG__ +#error "COTHREADS require G++" +#endif +#if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=90)) +#warning "COTHREADS require EGCS-1.1.1 with Leon's libgcc patch." +#warning "You may have trouble with thread-unsafe exceptions..." +#define NO_LIBGCC_HOOKS +#endif + +// -------------------------------------- constants + +// Minimal stack size +#define MINSTACK (32*1024) +// Default stack size +#define DEFSTACK (127*1024) +// Maxtime between checking fdesc (ms) +#define MAXFDWAIT (200) +// Maximum time to wait in any case +#define MAXWAIT (60*60*1000) +// Maximum penalty for hog task (ms) +#define MAXPENALTY (1000) +// Trace task switches +#undef COTHREAD_TRACE +#undef COTHREAD_TRACE_VERBOSE + +// -------------------------------------- context switch code + +struct mach_state { + jmp_buf buf; +}; + +static void +mach_switch(mach_state *st1, mach_state *st2) +{ +#if #cpu(sparc) + asm("ta 3"); // save register windows +#endif + if (! setjmp(st1->buf)) + longjmp(st2->buf, 1); +} + +static void +mach_start(mach_state *st1, void *pc, char *stacklo, char *stackhi) +{ +#if #cpu(sparc) + asm("ta 3"); // save register windows +#endif + if (! setjmp(st1->buf)) + { + // The following code must perform two tasks: + // -- set stack pointer to a proper value between #stacklo# and #stackhi#. + // -- branch to or call the function at address #pc#. + // This function never returns ... so there is no need to save anything +#if #cpu(mips) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("move $sp,%0\n\t" // set new stack pointer + "move $25,%1\n\t" // call subroutine via $25 + "jal $25\n\t" // call subroutine via $25 + "nop" // delay slot + : : "r" (sp), "r" (pc) ); +#elif #cpu(i386) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("movl %0,%%esp\n\t" // set new stack pointer + "call *%1" // call function + : : "r" (sp), "r" (pc) ); +#elif #cpu(sparc) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("ta 3\n\t" // saving the register windows will not hurt. + "mov %0,%%sp\n\t" // set new stack pointer + "call %1,0\n\t" // call function + "nop" // delay slot + : : "r" (sp), "r" (pc) ); +#elif #cpu(hppa) + char *sp = (char*)(((unsigned long)stacklo+128+255) & ~0xff); + asm volatile("copy %0,%%sp\n\t" // set stack pointer + "copy %1,%%r22\n\t" // set call address + ".CALL\n\t" // call pseudo instr (why?) + "bl $$dyncall,%%r31\n\t" // call + "copy %%r31,%%r2" // delay slot ??? + : : "r" (sp), "r" (pc) ); +#elif #cpu(alpha) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("bis $31,%0,$30\n\t" // set new stack pointer + "bis $31,%1,$27\n\t" // load function pointer + "jsr $26,($27),0" // call function + : : "r" (sp), "r" (pc) ); +#elif #cpu(powerpc) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("mr 1,%0\n\t" // set new stack pointer + "mr 0,%1\n\t" // load func pointer into r0 + "mtlr 0\n\t" // load link register with r0 + "blrl" // branch + : : "r" (sp), "r" (pc) ); +#elif #cpu(m68k) && defined(COTHREAD_UNTESTED) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("move%.l %0,%Rsp\n\t" // set new stack pointer + "jmp %a1" // branch to address %1 + : : "r" (sp), "a" (pc) ); +#elif #cpu(arm) && defined(COTHREAD_UNTESTED) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("mov%?\t%|sp, %0\n\t" // set new stack pointer + "mov%?\t%|pc, %1" // branch to address %1 + : : "r" (sp), "r" (pc) ); +#else +#error "COTHREADS not supported on this machine." +#error "Try -DTHREADMODEL=NOTHREADS." +#endif + // We should never reach this point + abort(); + // Note that this call to abort() makes sure + // that function mach_start() is compiled as a non-leaf + // function. It is indeed a non-leaf function since the + // piece of assembly code calls a function, but the compiler + // would not know without the call to abort() ... + } +} + +#ifdef CHECK +// This code can be used to verify that task switching works. +char stack[16384]; +mach_state st1, st2; +void th2() { + puts("2b"); mach_switch(&st2, &st1); + puts("4b"); mach_switch(&st2, &st1); + puts("6b"); mach_switch(&st2, &st1); +} +void th2relay() { + th2(); puts("ooops\n"); +} +void th1() { + mach_start(&st1, (void*)th2relay, stack, stack+sizeof(stack)); + puts("3a"); mach_switch(&st1, &st2); + puts("5a"); mach_switch(&st1, &st2); +} +int main() { + puts("1a"); th1(); puts("6a"); +} +#endif + + + +// -------------------------------------- select + +struct coselect { + int nfds; + fd_set rset; + fd_set wset; + fd_set eset; +}; + +static void +coselect_merge(coselect *dest, coselect *from) +{ + int i; + int nfds = from->nfds; + if (nfds > dest->nfds) + dest->nfds = nfds; + for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->rset)) FD_SET(i, &dest->rset); + for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->wset)) FD_SET(i, &dest->wset); + for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->eset)) FD_SET(i, &dest->eset); +} + +static int +coselect_test(coselect *c) +{ + static timeval tmzero = {0,0}; + fd_set copyr = c->rset; + fd_set copyw = c->wset; + fd_set copye = c->eset; + return select(c->nfds, ©r, ©w, ©e, &tmzero); +} + + +// -------------------------------------- cotask + +class GThread::cotask { +public: +#ifndef NO_LIBGCC_HOOKS + cotask(const int xstacksize,void *); +#else + cotask(const int xstacksize); +#endif + ~cotask(); + class GThread::cotask *next; + class GThread::cotask *prev; + // context + mach_state regs; + // stack information + char *stack; + GPBuffer<char> gstack; + int stacksize; + // timing information + unsigned long over; + // waiting information + void *wchan; + coselect *wselect; + unsigned long *maxwait; + // delete after termination + bool autodelete; + // egcs exception support +#ifndef NO_LIBGCC_HOOKS + void *ehctx; +#endif +}; + +#ifndef NO_LIBGCC_HOOKS +GThread::cotask::cotask(const int xstacksize, void *xehctx) +#else +GThread::cotask::cotask(const int xstacksize) +#endif +: next(0), prev(0), gstack(stack,xstacksize), stacksize(xstacksize), + over(0), wchan(0), wselect(0), maxwait(0), autodelete(false) +#ifndef NO_LIBGCC_HOOKS + ,ehctx(xehctx) +#endif +{ + memset(®s,0,sizeof(regs)); +} + +static GThread::cotask *maintask = 0; +static GThread::cotask *curtask = 0; +static GThread::cotask *autodeletetask = 0; +static unsigned long globalmaxwait = 0; +static void (*scheduling_callback)(int) = 0; +static timeval time_base; + + +GThread::cotask::~cotask() +{ + gstack.resize(0); +#ifndef NO_LIBGCC_HOOKS + if (ehctx) + free(ehctx); + ehctx = 0; +#endif +} + +static void +cotask_free(GThread::cotask *task) +{ +#ifdef COTHREAD_TRACE + DjVuPrintErrorUTF8("cothreads: freeing task %p with autodelete=%d\n", + task,task->autodelete); +#endif + if (task!=maintask) + { + delete task; + } +} + + +// -------------------------------------- time + +static unsigned long +time_elapsed(int reset=1) +{ + timeval tm; + gettimeofday(&tm, NULL); + long msec = (tm.tv_usec-time_base.tv_usec)/1000; + unsigned long elapsed = (long)(tm.tv_sec-time_base.tv_sec)*1000 + msec; + if (reset && elapsed>0) + { +#ifdef COTHREAD_TRACE +#ifdef COTHREAD_TRACE_VERBOSE + DjVuPrintErrorUTF8("cothreads: %4ld ms in task %p\n", elapsed, curtask); +#endif +#endif + time_base.tv_sec = tm.tv_sec; + time_base.tv_usec += msec*1000; + } + return elapsed; +} + + +// -------------------------------------- scheduler + +static int +cotask_yield() +{ + // ok + if (! maintask) + return 0; + // get elapsed time and return immediately when it is too small + unsigned long elapsed = time_elapsed(); + if (elapsed==0 && curtask->wchan==0 && curtask->prev && curtask->next) + return 0; + // adjust task running time + curtask->over += elapsed; + if (curtask->over > MAXPENALTY) + curtask->over = MAXPENALTY; + // start scheduling + reschedule: + // try unblocking tasks + GThread::cotask *n = curtask->next; + GThread::cotask *q = n; + do + { + if (q->wchan) + { + if (q->maxwait && *q->maxwait<=elapsed) + { + *q->maxwait = 0; + q->wchan=0; + q->maxwait=0; + q->wselect=0; + } + else if (q->wselect && globalmaxwait<=elapsed && coselect_test(q->wselect)) + { + q->wchan=0; + if (q->maxwait) + *q->maxwait -= elapsed; + q->maxwait = 0; + q->wselect=0; + } + if (q->maxwait) + *q->maxwait -= elapsed; + } + q = q->next; + } + while (q!=n); + // adjust globalmaxwait + if (globalmaxwait < elapsed) + globalmaxwait = MAXFDWAIT; + else + globalmaxwait -= elapsed; + // find best candidate + static int count; + unsigned long best = MAXPENALTY + 1; + GThread::cotask *r = 0; + count = 0; + q = n; + do + { + if (! q->wchan) + { + count += 1; + if (best > q->over) + { + r = q; + best = r->over; + } + } + q = q->next; + } + while (q != n); + // found + if (count > 0) + { + // adjust over + q = n; + do + { + q->over = (q->over>best ? q->over-best : 0); + q = q->next; + } + while (q != n); + // Switch + if (r != curtask) + { +#ifdef COTHREAD_TRACE + DjVuPrintErrorUTF8("cothreads: ----- switch to %p [%ld]\n", r, best); +#endif + GThread::cotask *old = curtask; + curtask = r; + mach_switch(&old->regs, &curtask->regs); + } + // handle autodelete + if (autodeletetask && autodeletetask->autodelete) + cotask_free(autodeletetask); + autodeletetask = 0; + // return + if (count == 1) + return 1; + return 0; + } + // No task ready + count = 0; + unsigned long minwait = MAXWAIT; + coselect allfds; + allfds.nfds = 1; + FD_ZERO(&allfds.rset); + FD_ZERO(&allfds.wset); + FD_ZERO(&allfds.eset); + q = n; + do + { + if (q->maxwait || q->wselect) + count += 1; + if (q->maxwait && *q->maxwait<minwait) + minwait = *q->maxwait; + if (q->wselect) + coselect_merge(&allfds, q->wselect); + q = q->next; + } + while (q != n); + // abort on deadlock + if (count == 0) { + DjVuMessageLite::perror( ERR_MSG("GThreads.panic") ); + abort(); + } + // select + timeval tm; + tm.tv_sec = minwait/1000; + tm.tv_usec = 1000*(minwait-1000*tm.tv_sec); + select(allfds.nfds,&allfds.rset, &allfds.wset, &allfds.eset, &tm); + // reschedule + globalmaxwait = 0; + elapsed = time_elapsed(); + goto reschedule; +} + + +static void +cotask_terminate(GThread::cotask *task) +{ +#ifdef COTHREAD_TRACE + DjVuPrintErrorUTF8("cothreads: terminating task %p\n", task); +#endif + if (task && task!=maintask) + { + if (task->prev && task->next) + { + if (scheduling_callback) + (*scheduling_callback)(GThread::CallbackTerminate); + task->prev->next = task->next; + task->next->prev = task->prev; + // mark task as terminated + task->prev = 0; + // self termination + if (task == curtask) + { + if (task->autodelete) + autodeletetask = task; + cotask_yield(); + } + } + } +} + + +static void +cotask_wakeup(void *wchan, int onlyone) +{ + if (maintask && curtask) + { + GThread::cotask *n = curtask->next; + GThread::cotask *q = n; + do + { + if (q->wchan == wchan) + { + q->wchan=0; + q->maxwait=0; + q->wselect=0; + q->over = 0; + if (onlyone) + return; + } + q = q->next; + } + while (q!=n); + } +} + + +// -------------------------------------- select / get_select + +static int +cotask_select(int nfds, + fd_set *rfds, fd_set *wfds, fd_set *efds, + struct timeval *tm) +{ + // bypass + if (maintask==0 || (tm && tm->tv_sec==0 && tm->tv_usec<1000)) + return select(nfds, rfds, wfds, efds, tm); + // copy parameters + unsigned long maxwait = 0; + coselect parm; + // set waiting info + curtask->wchan = (void*)&parm; + if (rfds || wfds || efds) + { + parm.nfds = nfds; + if (rfds) { parm.rset=*rfds; } else { FD_ZERO(&parm.rset); } + if (wfds) { parm.wset=*wfds; } else { FD_ZERO(&parm.wset); } + if (efds) { parm.eset=*efds; } else { FD_ZERO(&parm.eset); } + curtask->wselect = &parm; + } + if (tm) + { + maxwait = time_elapsed(0) + tm->tv_sec*1000 + tm->tv_usec/1000; + curtask->maxwait = &maxwait; + } + // reschedule + cotask_yield(); + // call select to update masks + if (tm) + { + tm->tv_sec = maxwait/1000; + tm->tv_usec = 1000*(maxwait-1000*tm->tv_sec); + } + static timeval tmzero = {0,0}; + return select(nfds, rfds, wfds, efds, &tmzero); +} + + +static void +cotask_get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, + unsigned long &timeout) +{ + int ready = 1; + unsigned long minwait = MAXWAIT; + unsigned long elapsed = time_elapsed(0); + coselect allfds; + allfds.nfds=0; + FD_ZERO(&allfds.rset); + FD_ZERO(&allfds.wset); + FD_ZERO(&allfds.eset); + if (curtask) + { + GThread::cotask *q=curtask->next; + while (q != curtask) + { + ready++; + if (q->wchan) + { + if (q->wselect) + coselect_merge(&allfds, q->wselect); + if (q->maxwait && *q->maxwait<minwait) + minwait = *q->maxwait; + ready--; + } + q = q->next; + } + } + timeout = 0; + nfds=allfds.nfds; + *rfds=allfds.rset; + *wfds=allfds.wset; + *efds=allfds.eset; + if (ready==1 && minwait>elapsed) + timeout = minwait-elapsed; +} + + + +// -------------------------------------- libgcc hook + +#ifndef NO_LIBGCC_HOOKS +// These are exported by Leon's patched version of libgcc.a +// Let's hope that the egcs people will include the patch in +// the distributions. +extern "C" +{ + extern void* (*__get_eh_context_ptr)(void); + extern void* __new_eh_context(void); +} + +// This function is called via the pointer __get_eh_context_ptr +// by the internal mechanisms of egcs. It must return the +// per-thread event handler context. This is necessary to +// implement thread safe exceptions on some machine and/or +// when flag -fsjlj-exception is set. +static void * +cotask_get_eh_context() +{ + if (curtask) + return curtask->ehctx; + else if (maintask) + return maintask->ehctx; + DjVuMessageLite::perror( ERR_MSG("GThreads.co_panic") ); + abort(); +} +#endif + + + +// -------------------------------------- GThread + +void +GThread::set_scheduling_callback(void (*call)(int)) +{ + if (scheduling_callback) + G_THROW( ERR_MSG("GThreads.dupl_callback") ); + scheduling_callback = call; +} + + +GThread::GThread(int stacksize) + : task(0), xentry(0), xarg(0) +{ + // check argument + if (stacksize < 0) + stacksize = DEFSTACK; + if (stacksize < MINSTACK) + stacksize = MINSTACK; + // initialization + if (! maintask) + { +#ifndef NO_LIBGCC_HOOKS + static GThread::cotask comaintask(0,(*__get_eh_context_ptr)()); + __get_eh_context_ptr = cotask_get_eh_context; +#else + static GThread::cotask comaintask(0); +#endif + maintask = &comaintask; +// memset(maintask, 0, sizeof(GThread::cotask)); + maintask->next = maintask; + maintask->prev = maintask; + gettimeofday(&time_base,NULL); + curtask = maintask; + } + // allocation +#ifndef NO_LIBGCC_HOOKS + task = new GThread::cotask(stacksize,__new_eh_context()); +#else + task = new GThread::cotask(stacksize); +#endif +} + + +GThread::~GThread() +{ + if (task && task!=maintask) + { + if (task->prev) // running + task->autodelete = true; + else + cotask_free(task); + task = 0; + } +} + +#if __GNUC__ >= 3 +# if __GNUC_MINOR__ >= 4 +# define noinline __attribute__((noinline,used)) +# elif __GNUC_MINOR >= 2 +# define noinline __attribute__((noinline)) +# endif +#endif +#ifndef noinline +# define noinline /**/ +#endif + +static noinline void startone(void); +static noinline void starttwo(GThread *thr); +static GThread * volatile starter; + +static void +startone(void) +{ + GThread *thr = starter; + mach_switch(&thr->task->regs, &curtask->regs); + // Registers may still contain an improper pointer + // to the exception context. We should neither + // register cleanups nor register handlers. + starttwo(thr); + abort(); +} + +static void +starttwo(GThread *thr) +{ + // Hopefully this function reacquires + // an exception context pointer. Therefore + // we can register the exception handlers. + // It is placed after ``startone'' to avoid inlining. +#ifdef __EXCEPTIONS + try + { +#endif + G_TRY + { + thr->xentry( thr->xarg ); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; +#ifdef __EXCEPTIONS + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } +#endif + cotask_terminate(curtask); + GThread::yield(); + // Do not add anything below this line! + // Nothing should reach it anyway. + abort(); +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (task->next || task->prev) + return -1; + xentry = entry; + xarg = arg; + task->wchan = 0; + task->next = curtask; + task->prev = curtask->prev; + task->next->prev = task; + task->prev->next = task; + GThread::cotask *old = curtask; + starter = this; + mach_start(&old->regs, (void*)startone, + task->stack, task->stack+task->stacksize); + if (scheduling_callback) + (*scheduling_callback)(CallbackCreate); + return 0; +} + + +void +GThread::terminate() +{ + if (task && task!=maintask) + cotask_terminate(task); +} + +int +GThread::yield() +{ + return cotask_yield(); +} + +int +GThread::select(int nfds, + fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + return cotask_select(nfds, readfds, writefds, exceptfds, timeout); +} + +void +GThread::get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, + unsigned long &timeout) +{ + cotask_get_select(nfds, rfds, wfds, efds, timeout); +} + +inline void * +GThread::current() +{ + if (curtask && curtask!=maintask) + return (void*)curtask; + return (void*)0; +} + + +// -------------------------------------- GMonitor + +GMonitor::GMonitor() + : count(1), locker(0), wlock(0), wsig(0) +{ + locker = 0; + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + cotask_wakeup((void*)&wsig, 0); + cotask_wakeup((void*)&wlock, 0); + cotask_yield(); + // Because we know how the scheduler works, we know that this single call to + // yield will run all unblocked tasks and given them the chance to leave the + // scope of the monitor object. +} + +void +GMonitor::enter() +{ + void *self = GThread::current(); + if (count>0 || self!=locker) + { + while (ok && count<=0) + { + curtask->wchan = (void*)&wlock; + wlock++; + cotask_yield(); + wlock--; + } + count = 1; + locker = self; + } + count -= 1; +} + +void +GMonitor::leave() +{ + void *self = GThread::current(); + if (ok && (count>0 || self!=locker)) + G_THROW( ERR_MSG("GThreads.not_acq_leave") ); + if (++count > 0 && wlock > 0) + cotask_wakeup((void*)&wlock, 1); +} + +void +GMonitor::signal() +{ + void *self = GThread::current(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + if (wsig > 0) + { + cotask_wakeup((void*)&wsig, 1); + if (scheduling_callback) + (*scheduling_callback)(GThread::CallbackUnblock); + } +} + +void +GMonitor::broadcast() +{ + void *self = GThread::current(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + if (wsig > 0) + { + cotask_wakeup((void*)&wsig, 0); + if (scheduling_callback) + (*scheduling_callback)(GThread::CallbackUnblock); + } +} + +void +GMonitor::wait() +{ + // Check state + void *self = GThread::current(); + if (count>0 || locker!=self) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Atomically release monitor and wait + int sav_count = count; + count = 1; + curtask->wchan = (void*)&wsig; + cotask_wakeup((void*)&wlock, 1); + wsig++; + cotask_yield(); + wsig--; + // Re-acquire + while (ok && count <= 0) + { + curtask->wchan = (void*)&wlock; + wlock++; + cotask_yield(); + wlock--; + } + count = sav_count; + locker = self; + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Check state + void *self = GThread::current(); + if (count>0 || locker!=self) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Atomically release monitor and wait + int sav_count = count; + count = 1; + unsigned long maxwait = time_elapsed(0) + timeout; + curtask->maxwait = &maxwait; + curtask->wchan = (void*)&wsig; + cotask_wakeup((void*)&wlock, 1); + wsig++; + cotask_yield(); + wsig--; + // Re-acquire + while (ok && count<=0) + { + curtask->wchan = (void*)&wlock; + wlock++; + cotask_yield(); + wlock--; + } + count = sav_count; + locker = self; + } +} + +#endif + + + + +// ---------------------------------------- +// GSAFEFLAGS +// ---------------------------------------- + + + +GSafeFlags & +GSafeFlags::operator=(long xflags) +{ + enter(); + if (flags!=xflags) + { + flags=xflags; + broadcast(); + } + leave(); + return *this; +} + +GSafeFlags::operator long(void) const +{ + long f; + ((GSafeFlags *) this)->enter(); + f=flags; + ((GSafeFlags *) this)->leave(); + return f; +} + +bool +GSafeFlags::test_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1) +{ + enter(); + if ((flags & set_mask)==set_mask && + (~flags & clr_mask)==clr_mask) + { + long new_flags=flags; + new_flags|=set_mask1; + new_flags&=~clr_mask1; + if (new_flags!=flags) + { + flags=new_flags; + broadcast(); + } + leave(); + return true; + } + leave(); + return false; +} + +void +GSafeFlags::wait_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1) +{ + enter(); + while((flags & set_mask)!=set_mask || + (~flags & clr_mask)!=clr_mask) wait(); + long new_flags=flags; + new_flags|=set_mask1; + new_flags&=~clr_mask1; + if (flags!=new_flags) + { + flags=new_flags; + broadcast(); + } + leave(); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GThreads.h b/kviewshell/plugins/djvu/libdjvu/GThreads.h new file mode 100644 index 00000000..92691db4 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GThreads.h @@ -0,0 +1,639 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GThreads.h,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GTHREADS_H_ +#define _GTHREADS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name GThreads.h + + Files #"GThreads.h"# and #"GThreads.cpp"# implement common entry points + for multithreading on multiple platforms. Each execution thread is + represented by an instance of class \Ref{GThread}. Synchronization is + provided by class \Ref{GMonitor} which implements a monitor (C.A.R Hoare, + Communications of the ACM, 17(10), 1974). + + The value of compiler symbol #THREADMODEL# selects an appropriate + implementation for these classes. The current implementation supports + the following values: + \begin{description} + \item[-DTHREADMODEL=NOTHREADS] Dummy implementation. This is a + good choice when the multithreading features are not required, + because it minimizes the portability problems. This is currently + the default when compiling under Unix. + \item[-DTHREADMODEL=WINTHREADS] Windows implementation. + This is the default when compiling under Windows. + \item[-DTHREADMODEL=MACTHREADS] Macintosh implementation, + which is based on the MacOS cooperative model. The current + implementation does not yet fully support synchronization. + This is the default when compiling under MacOS. + \item[-DTHREADMODEL=POSIXTHREADS] Posix implementation. + This implementation also supports DCE threads. The behavior of + the code is subject to the quality of the system implementation of + Posix threads. + \item[-DTHREADMODEL=COTHREADS] Custom cooperative threads. + These custom threads do not redefine system calls. Before executing + a potentially blocking system function, each thread must explicitly + check whether it is going to block and yield control explicitly if + this is the case. This code must be compiled with a patched version + of egcs-1.1.1 \URL{http://egcs.cygnus.com}. The patch addresses + exception thread-safety and is provided in #"@Tools/libgcc2.c.diff"#. + Once you get the right compiler, this implementation is remarkably + compact and portable. A variety of processors are supported, + including mips, intel, sparc, hppa, and alpha. + \item[-DTHREADMODEL=JRITHREADS] Java implementation hooks. + Multi-threading within a Netscape plugin can be tricky. A simple + idea however consists of implementing the threading primitives in + Java and to access them using JRI. The classes just contain a + JRIGlobalRef. This is not a real implementation since everything + (Java code, native functions, stubs, exception thread safety) must + be addressed by the plugin source code. Performance may be a serious + issue. + \end{description} + + {\bf Portability}: The simultaneous use of threads and exceptions caused a + lot of portability headaches under Unix. We eventually decided to + implement the COTHREADS cooperative threads (because preemptive threads + have more problems) and to patch EGCS in order to make exception handling + COTHREAD-safe. + + @memo + Portable threads + @author + L\'eon Bottou <leonb@research.att.com> -- initial implementation.\\ + Praveen Guduru <praveen@sanskrit.lz.att.com> -- mac implementation. + +// From: Leon Bottou, 1/31/2002 +// Almost unchanged by Lizardtech. +// GSafeFlags should go because it not as safe as it claims. + + @version + #$Id: GThreads.h,v 1.10 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "DjVuGlobal.h" +#include "GException.h" + +#define NOTHREADS 0 +#define COTHREADS 1 +#define JRITHREADS 2 +#define POSIXTHREADS 10 +#define WINTHREADS 11 +#define MACTHREADS 12 + +// Known platforms +#ifndef THREADMODEL +#if defined(WIN32) +#define THREADMODEL WINTHREADS +#endif +#if defined(macintosh) +#define THREADMODEL MACTHREADS +#endif +#endif + +// Exception emulation is not thread safe +#ifdef USE_EXCEPTION_EMULATION +#undef THREADMODEL +#define THREADMODEL NOTHREADS +#endif +// Default is nothreads +#ifndef THREADMODEL +#define THREADMODEL NOTHREADS +#endif + +// ---------------------------------------- +// INCLUDES + +#if THREADMODEL==WINTHREADS +#ifndef _WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif +#endif + +#if THREADMODEL==MACTHREADS +#include <threads.h> +#endif + +#if THREADMODEL==POSIXTHREADS +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#undef TRY +#undef CATCH +#define _CMA_NOWRAPPERS_ +#include <pthread.h> +#endif + +#if THREADMODEL==JRITHREADS +#include "jri.h" +#endif + +#if THREADMODEL==COTHREADS +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#endif + + +// ---------------------------------------- +// PORTABLE CLASSES + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** Thread class. A multithreaded process is composed of a main execution + thread and of several secondary threads. Each secondary thread is + represented by a #GThread# object. The amount of memory required for the + stack of a secondary thread is defined when the #GThread# object is + constructed. The execution thread is started when function + \Ref{GThread::create} is called. The destructor of class GThread waits + until the thread terminanes. Note that the execution can be terminated at + any time (with possible prejudice) by calling \Ref{GThread::terminate}. + + Several static member functions control the thread scheduler. Function + \Ref{GThread::yield} relinquishes the processor to another thread. + Function \Ref{GThread::select} (#COTHREADS# only) provides a thread-aware + replacement for the well-known unix system call #select#. + + {\bf Note} --- Both the copy constructor and the copy operator are declared + as private members. It is therefore not possible to make multiple copies + of instances of this class, as implied by the class semantic. */ + +class GThread { +public: + /** Constructs a new thread object. Memory is allocated for the + thread, but the thread is not started. + Argument #stacksize# is used by the #COTHREADS# model only for + specifying the amount of memory needed for the processor stack. A + negative value will be replaced by a suitable default value of 128Kb. + A minimum value of 32Kb is silently enforced. */ + GThread(int stacksize = -1); + /** Destructor. Destroying the thread object while the thread is running is + perfectly ok since it only destroys the thread identifier. Execution + will continue without interference. */ + ~GThread(); + /** Starts the thread. The new thread executes function #entry# with + argument #arg#. The thread terminates when the function returns. A + thread cannot be restarted after its termination. You must create a new + #GThread# object. */ + int create(void (*entry)(void*), void *arg); + /** Terminates a thread with extreme prejudice. The thread is removed from + the scheduling list. Execution terminates regardless of the execution + status of the thread function. Automatic variables may or may not be + destroyed. This function must be considered as a last resort since + memory may be lost. */ + void terminate(); + /** Causes the current thread to relinquish the processor. The scheduler + selects a thread ready to run and transfers control to that thread. The + actual effect of #yield# heavily depends on the selected implementation. + Function #yield# usually returns zero when the execution of the current + thread is resumed. It may return a positive number when it can + determine that the current thread will remain the only runnable thread + for some time. You may then call function \Ref{get_select} to + obtain more information. */ + static int yield(); + /** Returns a value which uniquely identifies the current thread. */ + static void *current(); + +#if THREADMODEL==WINTHREADS +private: + HANDLE hthr; + DWORD thrid; +#elif THREADMODEL==MACTHREADS +private: + unsigned long thid; + static pascal void *start(void *arg); +#elif THREADMODEL==POSIXTHREADS +private: + pthread_t hthr; + static void *start(void *arg); +#elif THREADMODEL==JRITHREADS +private: + JRIGlobalRef obj; +#elif THREADMODEL==COTHREADS + friend class GMonitor; +public: + class cotask; + class cotask *task; + /** Replaces system call #select# (COTHREADS only). The #COTHREADS# model + does not redefine system function. System functions therefore can + potentially block the whole process (instead of blocking the current + thread only) because the system is not aware of the #COTHREADS# + scheduler. The function #GThread::select# is a #COTHREADS#-aware + replacement for the well known system function #select#. You can also + use #GThread::select# for making sure that calls to system functions + will not block the entire process, as demonstrated below: + \begin{verbatim} + int + gthread_read(int fd, void *buffer, size_t len) + { + fd_set rdset; + FD_ZERO(&rdset); + FD_SET(fd, &rdset); + GThread::select(fd+1, &rdset, 0, 0, 0); + return read(fd, buffer, len); + } + \end{verbatim} */ + static int select(int nfds, fd_set*, fd_set*, fd_set*, struct timeval*); + /** Provide arguments for system call #select# (COTHREADS only). It may be + appropriate to call the real system call #select# if the current thread + is the only thread ready to run. Other threads however may wake up when + certain file descriptors are ready or when a certain delay expires. + Function #get_select# returns this information by filling the three + usual file descriptor sets (similar to the arguments of system call + #select#). It also returns a timeout #timeout# expressed in + milliseconds. Note that this timeout is zero is the current thread is + not the sole thread ready to run. */ + static void get_select(int &nfds, fd_set*, fd_set*, fd_set*, unsigned long &timeout); + /** Install hooks in the scheduler (COTHREADS only). The hook function + #call# is called when a new thread is created (argument is + #GThread::CallbackCreate#), when a thread terminates (argument is + #GThread::CallbackTerminate#), or when thread is unblocked (argument is + #GThread::CallbackUnblock#). This callback can be useful in certain GUI + toolkits where the most convenient method for scheduling the threads + consists in setting a timer event that calls \Ref{GThread::yield}. */ + static void set_scheduling_callback(void (*call)(int)); + enum { CallbackCreate, CallbackTerminate, CallbackUnblock }; + +#endif +public: + // Should be considered as private + void (*xentry)(void*); + void *xarg; +private: + // Disable default members + GThread(const GThread&); + GThread& operator=(const GThread&); +}; + + +/** Monitor class. Monitors have been first described in (C.A.R Hoare, + Communications of the ACM, 17(10), 1974). This mechanism provides the + basic mutual exclusion (mutex) and thread notification facilities + (condition variables). + + Only one thread can own the monitor at a given time. Functions + \Ref{enter} and \Ref{leave} can be used to acquire and release the + monitor. This mutual exclusion provides an efficient way to protect + segment of codes ({\em critical sections}) which should not be + simultaneously executed by two threads. Class \Ref{GMonitorLock} provides + a convenient way to do this effectively. + + When the thread owning the monitor calls function \Ref{wait}, the monitor + is released and the thread starts waiting until another thread calls + function \Ref{signal} or \Ref{broadcast}. When the thread wakes-up, it + re-acquires the monitor and function #wait# returns. Since the signaling + thread must acquire the monitor before calling functions #signal# and + #broadcast#, the signaled thread will not be able to re-acquire the + monitor until the signaling thread(s) releases the monitor. + + {\bf Note} --- Both the copy constructor and the copy operator are declared + as private members. It is therefore not possible to make multiple copies + of instances of this class, as implied by the class semantic. */ + +class GMonitor +{ +public: + GMonitor(); + ~GMonitor(); + /** Enters the monitor. If the monitor is acquired by another thread this + function waits until the monitor is released. The current thread then + acquires the monitor. Calls to #enter# and #leave# may be nested. */ + void enter(); + /** Leaves the monitor. The monitor counts how many times the current + thread has entered the monitor. Function #leave# decrement this count. + The monitor is released when this count reaches zero. An exception is + thrown if this function is called by a thread which does not own the + monitor. */ + void leave(); + /** Waits until the monitor is signaled. The current thread atomically + releases the monitor and waits until another thread calls function + #signal# or #broadcast#. Function #wait# then re-acquires the monitor + and returns. An exception is thrown if this function is called by a + thread which does not own the monitor. */ + void wait(); + /** Waits until the monitor is signaled or a timeout is reached. The + current thread atomically releases the monitor and waits until another + thread calls function #signal# or #broadcast# or a maximum of #timeout# + milliseconds. Function #wait# then re-acquires the monitor and returns. + An exception is thrown if this function is called by a thread which does + not own the monitor. */ + void wait(unsigned long timeout); + /** Signals one waiting thread. Function #signal# wakes up at most one of + the waiting threads for this monitor. An exception is thrown if this + function is called by a thread which does not own the monitor. */ + void signal(); + /** Signals all waiting threads. Function #broadcast# wakes up all the + waiting threads for this monitor. An exception is thrown if this + function is called by a thread which does not own the monitor. */ + void broadcast(); +private: +#if THREADMODEL==WINTHREADS + int ok; + int count; + DWORD locker; + CRITICAL_SECTION cs; + struct thr_waiting *head; + struct thr_waiting *tail; +#elif THREADMODEL==MACTHREADS + int ok; + int count; + unsigned long locker; + int wlock; + int wsig; +#elif THREADMODEL==POSIXTHREADS + int ok; + int count; + pthread_t locker; + pthread_mutex_t mutex; + pthread_cond_t cond; +#elif THREADMODEL==COTHREADS + int ok; + int count; + void *locker; + int wlock; + int wsig; +#elif THREADMODEL==JRITHREADS + JRIGlobalRef obj; +#endif +private: + // Disable default members + GMonitor(const GMonitor&); + GMonitor& operator=(const GMonitor&); +}; + + + + +// ---------------------------------------- +// NOTHREADS INLINES + +#if THREADMODEL==NOTHREADS +inline GThread::GThread(int) {} +inline GThread::~GThread(void) {} +inline void GThread::terminate() {} +inline int GThread::yield() { return 0; } +inline void* GThread::current() { return 0; } +inline GMonitor::GMonitor() {} +inline GMonitor::~GMonitor() {} +inline void GMonitor::enter() {} +inline void GMonitor::leave() {} +inline void GMonitor::wait() {} +inline void GMonitor::wait(unsigned long) {} +inline void GMonitor::signal() {} +inline void GMonitor::broadcast() {} +#endif // NOTHREADS + + +// ---------------------------------------- +// SCOPE LOCK + + +/** Wrapper for mutually exclusive code. + This class locks a specified critical section (see \Ref{GCriticalSection}) + at construction time and unlocks it at destruction time. It provides a + convenient way to take advantage of the C++ implicit destruction of + automatic variables in order to make sure that the monitor is + released when exiting the protected code. The following code will release + the monitor when the execution thread leaves the protected scope, either + because the protected code has executed successfully, or because an + exception was thrown. + \begin{verbatim} + { -- protected scope + static GMonitor theMonitor; + GMonitorLock lock(&theMonitor) + ... -- protected code + } + \end{verbatim} + This construct will do nothing when passed a null pointer. +*/ +class GMonitorLock +{ +private: + GMonitor *gsec; +public: + /** Constructor. Enters the monitor #gsec#. */ + GMonitorLock(GMonitor *gsec) : gsec(gsec) + { if (gsec) gsec->enter(); }; + /** Destructor. Leaves the associated monitor. */ + ~GMonitorLock() + { if (gsec) gsec->leave(); }; +}; + + + +// ---------------------------------------- +// GSAFEFLAGS (not so safe) + + +/** A thread safe class representing a set of flags. The flags are protected + by \Ref{GMonitor}, which is attempted to be locked whenever somebody + accesses the flags. One can modify the class contents using one of + two functions: \Ref{test_and_modify}() and \Ref{wait_and_modify}(). + Both of them provide atomic operation of testing (first) and modification + (second). The flags remain locked between the moment of testing and + modification, which guarantees, that their state cannot be changed in + between of these operations. */ +class GSafeFlags : public GMonitor +{ +private: + volatile long flags; +public: + /// Constructs #GSafeFlags# object. + GSafeFlags(long flags=0); + + /** Assignment operator. Will also wake up threads waiting for the + flags to change. */ + GSafeFlags & operator=(long flags); + + /** Returns the value of the flags */ + operator long(void) const; + /** Modifies the flags by ORing them with the provided mask. A broadcast + will be sent after the modification is done. */ + GSafeFlags & operator|=(long mask); + /** Modifies the flags by ANDing them with the provided mask. A broadcast + will be sent after the modification is done. */ + GSafeFlags & operator&=(long mask); + + /** If all bits mentioned in #set_mask# are set in the flags and all + bits mentioned in #clr_mask# are cleared in the flags, it sets all + bits from #set_mask1# in the flags, clears all flags from + #clr_mask1# in the flags and returns #TRUE#. Otherwise returns + #FALSE#. */ + bool test_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1); + + /** Waits until all bits mentioned in #set_mask# are set in the flags + and all bits mentioned in #clr_flags# are cleared in the flags. + After that it sets bits from #set_mask1# and clears bits from + #clr_mask1# in the flags. */ + void wait_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1); + + /** Waits until all bits set in #set_mask# are set in the flags and + all bits mentioned in #clr_mask# are cleared in the flags. */ + void wait_for_flags(long set_mask, long clr_mask=0) const; + + /** Modifies the flags by setting all bits mentioned in #set_mask# + and clearing all bits mentioned in #clr_mask#. If the flags have + actually been modified, a broadcast will be sent. */ + void modify(long set_mask, long clr_mask); +}; + +inline +GSafeFlags::GSafeFlags(long xflags) + : flags(xflags) +{ +} + +inline void +GSafeFlags::wait_for_flags(long set_mask, long clr_mask) const +{ + ((GSafeFlags *) this)->wait_and_modify(set_mask, clr_mask, 0, 0); +} + +inline void +GSafeFlags::modify(long set_mask, long clr_mask) +{ + test_and_modify(0, 0, set_mask, clr_mask); +} + +inline GSafeFlags & +GSafeFlags::operator|=(long mask) +{ + test_and_modify(0, 0, mask, 0); + return *this; +} + +inline GSafeFlags & +GSafeFlags::operator&=(long mask) +{ + test_and_modify(0, 0, 0, ~mask); + return *this; +} + +//@} + + + + +// ---------------------------------------- +// COMPATIBILITY CLASSES + + +// -- these classes are no longer documented. + +class GCriticalSection : protected GMonitor +{ +public: + void lock() + { GMonitor::enter(); }; + void unlock() + { GMonitor::leave(); }; +}; + +class GEvent : protected GMonitor +{ +private: + int status; +public: + GEvent() + : status(0) { }; + void set() + { if (!status) { enter(); status=1; signal(); leave(); } }; + void wait() + { enter(); if (!status) GMonitor::wait(); status=0; leave(); }; + void wait(int timeout) + { enter(); if (!status) GMonitor::wait(timeout); status=0; leave(); }; +}; + +class GCriticalSectionLock +{ +private: + GCriticalSection *gsec; +public: + GCriticalSectionLock(GCriticalSection *gsec) : gsec(gsec) + { if (gsec) gsec->lock(); }; + ~GCriticalSectionLock() + { if (gsec) gsec->unlock(); }; +}; + + +// ---------------------------------------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif //_GTHREADS_H_ + diff --git a/kviewshell/plugins/djvu/libdjvu/GURL.cpp b/kviewshell/plugins/djvu/libdjvu/GURL.cpp new file mode 100644 index 00000000..54b082ff --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GURL.cpp @@ -0,0 +1,1965 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GURL.cpp,v 1.19 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// This has been heavily changed by Lizardtech. +// They decided to use URLs for everyting, including +// the most basic file access. The URL class now is a unholy +// mixture of code for syntactically parsing the urls (which it was) +// and file status code (only for local file: urls). + +#include "GException.h" +#include "GOS.h" +#include "GURL.h" +#include "debug.h" + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <math.h> +#include <string.h> + +#ifdef WIN32 +# include <atlbase.h> +# include <windows.h> +# include <direct.h> +#endif /* WIN32 */ + +// -- MAXPATHLEN +#ifndef MAXPATHLEN +# ifdef _MAX_PATH +# define MAXPATHLEN _MAX_PATH +# else +# define MAXPATHLEN 1024 +# endif +#else +# if ( MAXPATHLEN < 1024 ) +# undef MAXPATHLEN +# define MAXPATHLEN 1024 +# endif +#endif + +#if defined(UNIX) || defined(OS2) +# include <unistd.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <errno.h> +# include <fcntl.h> +# include <pwd.h> +# include <stdio.h> +# ifdef AUTOCONF +# ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +# else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +# endif +# ifdef HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +# else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# ifdef HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# ifdef HAVE_NDIR_H +# include <ndir.h> +# endif +# endif +# else /* !AUTOCONF */ +# include <sys/time.h> +# if defined(XENIX) +# define USE_DIRECT +# include <sys/ndir.h> +# elif defined(OLDBSD) +# define USE_DIRECT +# include <sys/dir.h> +# endif +# ifdef USE_DIRECT +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# else +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +# endif +# endif /* !AUTOCONF */ +#endif /* UNIX */ + +#ifdef macintosh +#include <unix.h> +#include <errno.h> +#include <unistd.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const char djvuopts[]="DJVUOPTS"; +static const char localhost[]="file://localhost/"; +static const char backslash='\\'; +static const char colon=':'; +static const char dot='.'; +static const char filespecslashes[] = "file://"; +static const char filespec[] = "file:"; +static const char slash='/'; +static const char percent='%'; +static const char localhostspec1[] = "//localhost/"; +static const char localhostspec2[] = "///"; +static const char nillchar=0; +#if defined(UNIX) + static const char tilde='~'; + static const char root[] = "/"; +#elif defined(WIN32) || defined(OS2) + static const char root[] = "\\"; +#elif defined(macintosh) + static char const * const root = &nillchar; +#else +#error "Define something here for your operating system" +#endif + + +static const int +pathname_start(const GUTF8String &url, const int protolength); + +// hexval -- +// -- Returns the hexvalue of a character. +// Returns -1 if illegal; + +static int +hexval(char c) +{ + return ((c>='0' && c<='9') + ?(c-'0') + :((c>='A' && c<='F') + ?(c-'A'+10) + :((c>='a' && c<='f') + ?(c-'a'+10):(-1)))); +} + + +static bool +is_argument(const char * start) + // Returns TRUE if 'start' points to the beginning of an argument + // (either hash or CGI) +{ + // return (*start=='#' || *start=='?' || *start=='&' || *start==';'); + return (*start=='#' || *start=='?' ); +} + +static bool +is_argument_sep(const char * start) + // Returns TRUE if 'start' points to the beginning of an argument + // (either hash or CGI) +{ + return (*start=='&')||(*start == ';'); +} + +void +GURL::convert_slashes(void) +{ + GUTF8String xurl(get_string()); +#if defined(WIN32) + const int protocol_length=protocol(xurl).length(); + for(char *ptr=(xurl.getbuf()+protocol_length);*ptr;ptr++) + if(*ptr == backslash) + *ptr=slash; + url=xurl; +#endif +} + +static void +collapse(char * ptr, const int chars) + // Will remove the first 'chars' chars from the string and + // move the rest toward the beginning. Will take into account + // string length +{ + const int length=strlen(ptr); + const char *srcptr=ptr+((chars>length)?length:chars); + while((*(ptr++) = *(srcptr++))) + EMPTY_LOOP; +} + +GUTF8String +GURL::beautify_path(GUTF8String xurl) +{ + + const int protocol_length=GURL::protocol(xurl).length(); + + // Eats parts like ./ or ../ or /// + char * buffer; + GPBuffer<char> gbuffer(buffer,xurl.length()+1); + strcpy(buffer, (const char *)xurl); + + // Find start point + char * start=buffer+pathname_start(xurl,protocol_length); + + // Find end of the url (don't touch arguments) + char * ptr; + GUTF8String args; + for(ptr=start;*ptr;ptr++) + { + if (is_argument(ptr)) + { + args=ptr; + *ptr=0; + break; + } + } + + // Eat multiple slashes + for(;(ptr=strstr(start, "////"));collapse(ptr, 3)) + EMPTY_LOOP; + for(;(ptr=strstr(start, "//"));collapse(ptr, 1)) + EMPTY_LOOP; + // Convert /./ stuff into plain / + for(;(ptr=strstr(start, "/./"));collapse(ptr, 2)) + EMPTY_LOOP; +#if defined(WIN32) || defined(OS2) + if(!xurl.cmp(filespec,sizeof(filespec)-1)) + { + int offset=1; + if(start&&(start[0] == '/')&& + !xurl.cmp("file:////",sizeof("file:////")-1)) + { + collapse(start, 1); + offset=0; + } + for(ptr=start+offset;(ptr=strchr(ptr, '/'));) + { + if(isalpha((++ptr)[0])) + { + if((ptr[1] == ':')&&(ptr[2]=='/')) + { + char *buffer2; + GPBuffer<char> gbuffer2(buffer2,strlen(ptr)+1); + strcpy(buffer2,ptr); + gbuffer.resize(strlen(ptr)+sizeof(localhost)); + strcpy(buffer,localhost); + strcat(buffer,buffer2); + ptr=(start=buffer+sizeof(localhost))+1; + } + } + } + } +#endif + // Process /../ + while((ptr=strstr(start, "/../"))) + { + for(char * ptr1=ptr-1;(ptr1>=start);ptr1--) + { + if (*ptr1==slash) + { + collapse(ptr1, ptr-ptr1+3); + break; + } + } + } + + // Remove trailing /. + ptr=start+strlen(start)-2; + if((ptr>=start)&& (ptr == GUTF8String("/."))) + { + ptr[1]=0; + } + // Eat trailing /.. + ptr=start+strlen(start)-3; + if((ptr >= start) && (ptr == GUTF8String("/.."))) + { + for(char * ptr1=ptr-1;(ptr1>=start);ptr1--) + { + if (*ptr1==slash) + { + ptr1[1]=0; + break; + } + } + } + + // Done. Copy the buffer back into the URL and add arguments. + xurl=buffer; + return (xurl+args); +} + + +void +GURL::beautify_path(void) +{ + url=beautify_path(get_string()); +} + +void +GURL::init(const bool nothrow) +{ + GCriticalSectionLock lock(&class_lock); + validurl=true; + + if (url.length()) + { + GUTF8String proto=protocol(); + if (proto.length()<2) + { + validurl=false; + if(!nothrow) + G_THROW( ERR_MSG("GURL.no_protocol") "\t"+url); + return; + } + + // Below we have to make this complex test to detect URLs really + // referring to *local* files. Surprisingly, file://hostname/dir/file + // is also valid, but shouldn't be treated thru local FS. + if (proto=="file" && url[5]==slash && + (url[6]!=slash || !url.cmp(localhost, sizeof(localhost)))) + { + // Separate the arguments + GUTF8String arg; + { + const char * const url_ptr=url; + const char * ptr; + for(ptr=url_ptr;*ptr&&!is_argument(ptr);ptr++) + EMPTY_LOOP; + arg=ptr; + url=url.substr(0,(size_t)ptr-(size_t)url_ptr); + } + + // Do double conversion + GUTF8String tmp=UTF8Filename(); + if (!tmp.length()) + { + validurl=false; + if(!nothrow) + G_THROW( ERR_MSG("GURL.fail_to_file") ); + return; + } + url=GURL::Filename::UTF8(tmp).get_string(); + if (!url.length()) + { + validurl=false; + if(!nothrow) + G_THROW( ERR_MSG("GURL.fail_to_URL") ); + return; + } + // Return the argument back + url+=arg; + } + convert_slashes(); + beautify_path(); + parse_cgi_args(); + } +} + +GURL::GURL(void) + : validurl(false) +{ +} + +GURL::GURL(const char * url_in) + : url(url_in ? url_in : ""), validurl(false) +{ +} + +GURL::GURL(const GUTF8String & url_in) + : url(url_in), validurl(false) +{ +} + +GURL::GURL(const GNativeString & url_in) + : url(url_in.getNative2UTF8()), validurl(false) +{ +#if defined(WIN32) || defined(OS2) + if(is_valid() && is_local_file_url()) + { + GURL::Filename::UTF8 xurl(UTF8Filename()); + url=xurl.get_string(true); + validurl=false; + } +#endif +} + +GURL::GURL(const GURL & url_in) + : validurl(false) +{ + if(url_in.is_valid()) + { + url=url_in.get_string(); + init(); + }else + { + url=url_in.url; + } +} + +GURL & +GURL::operator=(const GURL & url_in) +{ + GCriticalSectionLock lock(&class_lock); + if(url_in.is_valid()) + { + url=url_in.get_string(); + init(true); + }else + { + url=url_in.url; + validurl=false; + } + return *this; +} + +GUTF8String +GURL::protocol(const GUTF8String& url) +{ + const char * const url_ptr=url; + const char * ptr=url_ptr; + for(char c=*ptr; + c && (isalnum(c) || c == '+' || c == '-' || c == '.'); + c=*(++ptr)) EMPTY_LOOP; + return(*ptr==colon)?GUTF8String(url_ptr, ptr-url_ptr):GUTF8String(); +} + +GUTF8String +GURL::hash_argument(void) const + // Returns the HASH argument (anything after '#' and before '?') +{ + const GUTF8String xurl(get_string()); + + bool found=false; + GUTF8String arg; + + // Break if CGI argument is found + for(const char * start=xurl;*start&&(*start!='?');start++) + { + if (found) + { + arg+=*start; + }else + { + found=(*start=='#'); + } + } + return decode_reserved(arg); +} + +void +GURL::set_hash_argument(const GUTF8String &arg) +{ + const GUTF8String xurl(get_string()); + + GUTF8String new_url; + bool found=false; + const char * ptr; + for(ptr=xurl;*ptr;ptr++) + { + if (is_argument(ptr)) + { + if (*ptr!='#') + { + break; + } + found=true; + } else if (!found) + { + new_url+=*ptr; + } + } + + url=new_url+"#"+GURL::encode_reserved(arg)+ptr; +} + +void +GURL::parse_cgi_args(void) + // Will read CGI arguments from the URL into + // cgi_name_arr and cgi_value_arr +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + cgi_name_arr.empty(); + cgi_value_arr.empty(); + + // Search for the beginning of CGI arguments + const char * start=url; + while(*start) + { + if(*(start++)=='?') + { + break; + } + } + + // Now loop until we see all of them + while(*start) + { + GUTF8String arg; // Storage for another argument + while(*start) // Seek for the end of it + { + if (is_argument_sep(start)) + { + start++; + break; + } else + { + arg+=*start++; + } + } + if (arg.length()) + { + // Got argument in 'arg'. Split it into 'name' and 'value' + const char * ptr; + const char * const arg_ptr=arg; + for(ptr=arg_ptr;*ptr&&(*ptr != '=');ptr++) + EMPTY_LOOP; + + GUTF8String name, value; + if (*ptr) + { + name=GUTF8String(arg_ptr, (int)((ptr++)-arg_ptr)); + value=GUTF8String(ptr, arg.length()-name.length()-1); + } else + { + name=arg; + } + + int args=cgi_name_arr.size(); + cgi_name_arr.resize(args); + cgi_value_arr.resize(args); + cgi_name_arr[args]=decode_reserved(name); + cgi_value_arr[args]=decode_reserved(value); + } + } +} + +void +GURL::store_cgi_args(void) + // Will store CGI arguments from the cgi_name_arr and cgi_value_arr + // back into the URL +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + + const char * const url_ptr=url; + const char * ptr; + for(ptr=url_ptr;*ptr&&(*ptr!='?');ptr++) + EMPTY_LOOP; + + GUTF8String new_url(url_ptr, ptr-url_ptr); + + for(int i=0;i<cgi_name_arr.size();i++) + { + GUTF8String name=GURL::encode_reserved(cgi_name_arr[i]); + GUTF8String value=GURL::encode_reserved(cgi_value_arr[i]); + new_url+=(i?"&":"?")+name; + if (value.length()) + new_url+="="+value; + } + + url=new_url; +} + +int +GURL::cgi_arguments(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + return cgi_name_arr.size(); +} + +int +GURL::djvu_cgi_arguments(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + int args=0; + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + args=cgi_name_arr.size()-(i+1); + break; + } + } + return args; +} + +GUTF8String +GURL::cgi_name(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return (num<cgi_name_arr.size())?cgi_name_arr[num]:GUTF8String(); +} + +GUTF8String +GURL::djvu_cgi_name(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GUTF8String arg; + for(int i=0;i<cgi_name_arr.size();i++) + if (cgi_name_arr[i].upcase()==djvuopts) + { + for(i++;i<cgi_name_arr.size();i++) + if (! num--) + { + arg=cgi_name_arr[i]; + break; + } + break; + } + return arg; +} + +GUTF8String +GURL::cgi_value(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return (num<cgi_value_arr.size())?cgi_value_arr[num]:GUTF8String(); +} + +GUTF8String +GURL::djvu_cgi_value(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GUTF8String arg; + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + for(i++;i<cgi_name_arr.size();i++) + { + if (! num--) + { + arg=cgi_value_arr[i]; + break; + } + } + break; + } + } + return arg; +} + +DArray<GUTF8String> +GURL::cgi_names(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return cgi_name_arr; +} + +DArray<GUTF8String> +GURL::cgi_values(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return cgi_value_arr; +} + +DArray<GUTF8String> +GURL::djvu_cgi_names(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + int i; + DArray<GUTF8String> arr; + for(i=0;(i<cgi_name_arr.size())&& + (cgi_name_arr[i].upcase()!=djvuopts) + ;i++) + EMPTY_LOOP; + + int size=cgi_name_arr.size()-(i+1); + if (size>0) + { + arr.resize(size-1); + for(i=0;i<arr.size();i++) + arr[i]=cgi_name_arr[cgi_name_arr.size()-arr.size()+i]; + } + + return arr; +} + +DArray<GUTF8String> +GURL::djvu_cgi_values(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + int i; + DArray<GUTF8String> arr; + for(i=0;i<cgi_name_arr.size()&&(cgi_name_arr[i].upcase()!=djvuopts);i++) + EMPTY_LOOP; + + int size=cgi_name_arr.size()-(i+1); + if (size>0) + { + arr.resize(size-1); + for(i=0;i<arr.size();i++) + arr[i]=cgi_value_arr[cgi_value_arr.size()-arr.size()+i]; + } + + return arr; +} + +void +GURL::clear_all_arguments(void) +{ + clear_hash_argument(); + clear_cgi_arguments(); +} + +void +GURL::clear_hash_argument(void) + // Clear anything after first '#' and before the following '?' +{ + if(!validurl) init(); + GCriticalSectionLock lock(&class_lock); + bool found=false; + GUTF8String new_url; + for(const char * start=url;*start;start++) + { + // Break on first CGI arg. + if (*start=='?') + { + new_url+=start; + break; + } + + if (!found) + { + if (*start=='#') + found=true; + else + new_url+=*start; + } + } + url=new_url; +} + +void +GURL::clear_cgi_arguments(void) +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + + // Clear the arrays + cgi_name_arr.empty(); + cgi_value_arr.empty(); + + // And clear everything past the '?' sign in the URL + for(const char * ptr=url;*ptr;ptr++) + if (*ptr=='?') + { + url.setat(ptr-url, 0); + break; + } +} + +void +GURL::clear_djvu_cgi_arguments(void) +{ + if(!validurl) init(); + // First - modify the arrays + GCriticalSectionLock lock(&class_lock); + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + cgi_name_arr.resize(i-1); + cgi_value_arr.resize(i-1); + break; + } + } + + // And store them back into the URL + store_cgi_args(); +} + +void +GURL::add_djvu_cgi_argument(const GUTF8String &name, const char * value) +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + + // Check if we already have the "DJVUOPTS" argument + bool have_djvuopts=false; + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + have_djvuopts=true; + break; + } + } + + // If there is no DJVUOPTS, insert it + if (!have_djvuopts) + { + int pos=cgi_name_arr.size(); + cgi_name_arr.resize(pos); + cgi_value_arr.resize(pos); + cgi_name_arr[pos]=djvuopts; + } + + // Add new argument to the array + int pos=cgi_name_arr.size(); + cgi_name_arr.resize(pos); + cgi_value_arr.resize(pos); + cgi_name_arr[pos]=name; + cgi_value_arr[pos]=value; + + // And update the URL + store_cgi_args(); +} + +bool +GURL::is_local_file_url(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return (protocol()=="file" && url[5]==slash); +} + +static const int +pathname_start(const GUTF8String &url, const int protolength) +{ + const int length=url.length(); + int retval=0; + if(protolength+1<length) + { + retval=url.search(slash,((url[protolength+1] == '/') + ?((url[protolength+2] == '/')?(protolength+3):(protolength+2)) + :(protolength+1))); + } + return (retval>0)?retval:length; +} + +GUTF8String +GURL::pathname(void) const +{ + return (is_local_file_url()) + ?GURL::encode_reserved(UTF8Filename()) + :url.substr(pathname_start(url,protocol().length()),(unsigned int)(-1)); +} + +GURL +GURL::base(void) const +{ + const GUTF8String xurl(get_string()); + const int protocol_length=protocol(xurl).length(); + const int xurl_length=xurl.length(); + const char * const url_ptr=xurl; + const char * ptr, * xslash; + ptr=xslash=url_ptr+protocol_length+1; + if(xslash[0] == '/') + { + xslash++; + if(xslash[0] == '/') + xslash++; + for(ptr=xslash;ptr[0] && !is_argument(ptr);ptr++) + { + if ((ptr[0]==slash)&&ptr[1]&&!is_argument(ptr+1)) + xslash=ptr; + } + if(xslash[0] != '/') + { + xslash=url_ptr+xurl_length; + } + } + return GURL::UTF8( +// ifdef WIN32 +// (*(xslash-1) == colon)? +// (GUTF8String(xurl,(int)(xslash-url_ptr))+"/" ): +// endif + (GUTF8String(xurl,(int)(xslash-url_ptr))+"/")); +} + +bool +GURL::operator==(const GURL & gurl2) const +{ + bool retval=false; + const GUTF8String g1(get_string()); + const int g1_length=g1.length(); + const GUTF8String g2(gurl2.get_string()); + const int g2_length=g2.length(); + if(g1_length == g2_length) // exactly equal + { + retval=(g1==g2); + }else if(g1_length+1 == g2_length) // g1 is g2 with a slash at the end + { + retval=(g2[g1_length] == '/')&&!g1.cmp(g2,g1_length); + }else if(g2_length+1 == g1_length) // g2 is g1 with a slash at the end + { + retval=(g1[g2_length] == '/')&&!g1.cmp(g2,g2_length); + } + return retval; +} + +GUTF8String +GURL::name(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GUTF8String retval; + if(!is_empty()) + { + const GUTF8String xurl(url); + const int protocol_length=protocol(xurl).length(); + const char * ptr, * xslash=(const char *)xurl+protocol_length-1; + for(ptr=(const char *)xurl+protocol_length; + *ptr && !is_argument(ptr);ptr++) + { + if (*ptr==slash) + xslash=ptr; + } + retval=GUTF8String(xslash+1, ptr-xslash-1); + } + return retval; +} + +GUTF8String +GURL::fname(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + return decode_reserved(name()); +} + +GUTF8String +GURL::extension(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GUTF8String xfilename=name(); + GUTF8String retval; + + for(int i=xfilename.length()-1;i>=0;i--) + { + if (xfilename[i]=='.') + { + retval=(const char*)xfilename+i+1; + break; + } + } + return retval; +} + +GUTF8String +GURL::decode_reserved(const GUTF8String &gurl) +{ + const char *url=gurl; + char *res; + GPBuffer<char> gres(res,gurl.length()+1); + char *r=res; + for(const char * ptr=url;*ptr;++ptr,++r) + { + if (*ptr!=percent) + { + r[0]=*ptr; + }else + { + int c1,c2; + if ( ((c1=hexval(ptr[1]))>=0) + && ((c2=hexval(ptr[2]))>=0) ) + { + r[0]=(c1<<4)|c2; + ptr+=2; + } else + { + r[0]=*ptr; + } + } + } + r[0]=0; + GUTF8String retval(res); + if(!retval.is_valid()) + { + retval=GNativeString(res); + } + return retval; +} + +GUTF8String +GURL::encode_reserved(const GUTF8String &gs) +{ + const char *s=(const char *)gs; + // Potentially unsafe characters (cf. RFC1738 and RFC1808) + static const char hex[] = "0123456789ABCDEF"; + + unsigned char *retval; + GPBuffer<unsigned char> gd(retval,strlen(s)*3+1); + unsigned char *d=retval; + for (; *s; s++,d++) + { + // Convert directory separator to slashes +#if defined(WIN32) || defined(OS2) + if (*s == backslash || *s== slash) +#else +#ifdef macintosh + if (*s == colon ) +#else +#ifdef UNIX + if (*s == slash ) +#else +#error "Define something here for your operating system" +#endif +#endif +#endif + { + *d = slash; + continue; + } + unsigned char const ss=(unsigned char const)(*s); + // WARNING: Whenever you modify this conversion code, + // make sure, that the following functions are in sync: + // encode_reserved() + // decode_reserved() + // url_to_filename() + // filename_to_url() + // unreserved characters + if ( (ss>='a' && ss<='z') || + (ss>='A' && ss<='Z') || + (ss>='0' && ss<='9') || + (strchr("$-_.+!*'(),:~=", ss)) ) + { + *d = ss; + continue; + } + // escape sequence + d[0] = percent; + d[1] = hex[ (ss >> 4) & 0xf ]; + d[2] = hex[ (ss) & 0xf ]; + d+=2; + } + *d = 0; + return retval; +} + +// ------------------------------------------- +// Functions for converting filenames and urls +// ------------------------------------------- + +static GUTF8String +url_from_UTF8filename(const GUTF8String &gfilename) +{ + if(GURL::UTF8(gfilename).is_valid()) + { + DEBUG_MSG("Debug: URL as Filename: " << gfilename << "\n"); + } + const char *filename=gfilename; + if(filename && (unsigned char)filename[0] == (unsigned char)0xEF + && (unsigned char)filename[1] == (unsigned char)0xBB + && (unsigned char)filename[2] == (unsigned char)0xBF) + { + filename+=3; + } + + // Special case for blank pages + if(!filename || !filename[0]) + { + return GUTF8String(); + } + + // Normalize file name to url slash-and-escape syntax + GUTF8String oname=GURL::expand_name(filename); + GUTF8String nname=GURL::encode_reserved(oname); + + // Preprend "file://" to file name. If file is on the local + // machine, include "localhost". + GUTF8String url=filespecslashes; + const char *cnname=nname; + if (cnname[0] == slash) + { + if (cnname[1] == slash) + { + url += cnname+2; + }else + { + url = localhost + nname; + } + }else + { + url += (localhostspec1+2) + nname; + } + return url; +} + +GUTF8String +GURL::get_string(const bool nothrow) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(nothrow); + return url; +} + +// -- Returns a url for accessing a given file. +// If useragent is not provided, standard url will be created, +// but will not be understood by some versions if IE. +GUTF8String +GURL::get_string(const GUTF8String &useragent) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GUTF8String retval(url); + if(is_local_file_url()&&useragent.length()) + { + if(useragent.search("MSIE") >= 0 || useragent.search("Microsoft")>=0) + { + retval=filespecslashes + expand_name(UTF8Filename()); + } + } + return retval; +} + +GURL::UTF8::UTF8(const GUTF8String &xurl) +: GURL(xurl) {} + +GURL::UTF8::UTF8(const GUTF8String &xurl,const GURL &codebase) +: GURL(xurl,codebase) {} + +GURL::GURL(const GUTF8String &xurl,const GURL &codebase) + : validurl(false) +{ + if(GURL::UTF8(xurl).is_valid()) + { + url=xurl; + }else + { + const char *c=xurl; + if(c[0] == slash) + { + GURL base(codebase); + for(GURL newbase=base.base();newbase!=base;newbase=base.base()) + { + base=newbase; + } + url=base.get_string(true)+GURL::encode_reserved(xurl); + }else + { + url=beautify_path(codebase.get_string(true)+GUTF8String(slash)+GURL::encode_reserved(xurl)); + } + } +} + +GURL::Native::Native(const GNativeString &xurl) +: GURL(xurl) {} + +GURL::Native::Native(const GNativeString &xurl,const GURL &codebase) +: GURL(xurl,codebase) {} + +GURL::GURL(const GNativeString &xurl,const GURL &codebase) + : validurl(false) +{ + GURL retval(xurl.getNative2UTF8(),codebase); + if(retval.is_valid()) + { +#if defined(WIN32) + // Hack for IE to change \\ to / + if(retval.is_local_file_url()) + { + GURL::Filename::UTF8 retval2(retval.UTF8Filename()); + url=retval2.get_string(true); + validurl=false; + }else +#endif // WIN32 + { + url=retval.get_string(true); + validurl=false; + } + } +} + +GURL::Filename::Filename(const GNativeString &gfilename) +{ + url=url_from_UTF8filename(gfilename.getNative2UTF8()); +} + +GURL::Filename::Native::Native(const GNativeString &gfilename) +: GURL::Filename(gfilename) {} + +GURL::Filename::Filename(const GUTF8String &gfilename) +{ + url=url_from_UTF8filename(gfilename); +} + +GURL::Filename::UTF8::UTF8(const GUTF8String &gfilename) +: GURL::Filename(gfilename) {} + +// filename -- +// -- Applies heuristic rules to convert a url into a valid file name. +// Returns a simple basename in case of failure. +GUTF8String +GURL::UTF8Filename(void) const +{ + GUTF8String retval; + if(! is_empty()) + { + const char *url_ptr=url; + + // WARNING: Whenever you modify this conversion code, + // make sure, that the following functions are in sync: + // encode_reserved() + // decode_reserved() + // url_to_filename() + // filename_to_url() + + GUTF8String urlcopy=decode_reserved(url); + url_ptr = urlcopy; + + // All file urls are expected to start with filespec which is "file:" + if (GStringRep::cmp(filespec, url_ptr, sizeof(filespec)-1)) //if not + return GOS::basename(url_ptr); + url_ptr += sizeof(filespec)-1; + +#if defined(macintosh) + //remove all leading slashes + for(;*url_ptr==slash;url_ptr++) + EMPTY_LOOP; + // Remove possible localhost spec + if ( !GStringRep::cmp(localhost, url_ptr, sizeof(localhost)-1) ) + url_ptr += sizeof(localhost)-1; + //remove all leading slashes + while(*url_ptr==slash) + url_ptr++; +#else + // Remove possible localhost spec + if ( !GStringRep::cmp(localhostspec1, url_ptr, sizeof(localhostspec1)-1) ) + // RFC 1738 local host form + url_ptr += sizeof(localhostspec1)-1; + else if ( !GStringRep::cmp(localhostspec2, url_ptr, sizeof(localhostspec2)-1 ) ) + // RFC 1738 local host form + url_ptr += sizeof(localhostspec2)-1; + else if ( (strlen(url_ptr) > 4) // "file://<letter>:/<path>" + && (url_ptr[0] == slash) // "file://<letter>|/<path>" + && (url_ptr[1] == slash) + && isalpha(url_ptr[2]) + && ( url_ptr[3] == colon || url_ptr[3] == '|' ) + && (url_ptr[4] == slash) ) + url_ptr += 2; + else if ( (strlen(url_ptr)) > 2 // "file:/<path>" + && (url_ptr[0] == slash) + && (url_ptr[1] != slash) ) + url_ptr++; +#endif + + // Check if we are finished +#if defined(macintosh) + { + char *l_url; + GPBuffer<char> gl_url(l_url,strlen(url_ptr)+1); + const char *s; + char *r; + for ( s=url_ptr,r=l_url; *s; s++,r++) + { + *r=(*s == slash)?colon:*s; + } + *r=0; + retval = expand_name(l_url,root); + } +#else + retval = expand_name(url_ptr,root); +#endif + +#if defined(WIN32) || defined(OS2) + if (url_ptr[0] && url_ptr[1]=='|' && url_ptr[2]== slash) + { + if ((url_ptr[0]>='a' && url_ptr[0]<='z') + || (url_ptr[0]>='A' && url_ptr[0]<='Z')) + { + GUTF8String drive; + drive.format("%c%c%c", url_ptr[0],colon,backslash); + retval = expand_name(url_ptr+3, drive); + } + } +#endif + } + // Return what we have + return retval; +} + +GNativeString +GURL::NativeFilename(void) const +{ + return UTF8Filename().getUTF82Native(); +} + +#if defined(UNIX) || defined(macintosh) || defined(OS2) +static int +urlstat(const GURL &url,struct stat &buf) +{ + return ::stat(url.NativeFilename(),&buf); +} +#endif + +// is_file(url) -- +// -- returns true if filename denotes a regular file. +bool +GURL::is_file(void) const +{ + bool retval=false; + if(is_local_file_url()) + { +#if defined(UNIX) || defined(macintosh) || defined(OS2) + struct stat buf; + if (!urlstat(*this,buf)) + { + retval=!(buf.st_mode & S_IFDIR); + } +#elif defined(WIN32) + GUTF8String filename(UTF8Filename()); + if(filename.length() >= MAX_PATH) + { + if(!filename.cmp("\\\\",2)) + filename="\\\\?\\UNC"+filename.substr(1,-1); + else + filename="\\\\?\\"+filename; + } + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + filename.ncopy(wfilename,wfilename_size); + DWORD dwAttrib; + dwAttrib = GetFileAttributesW(wfilename); + if((dwAttrib|1) == 0xFFFFFFFF) + { + USES_CONVERSION ; + dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt + } + retval=!( dwAttrib & FILE_ATTRIBUTE_DIRECTORY ); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +bool +GURL::is_local_path(void) const +{ + bool retval=false; + if(is_local_file_url()) + { +#if defined(UNIX) || defined(macintosh) || defined(OS2) + struct stat buf; + retval=!urlstat(*this,buf); +#else + GUTF8String filename(UTF8Filename()); + if(filename.length() >= MAX_PATH) + { + if(!filename.cmp("\\\\",2)) + filename="\\\\?\\UNC"+filename.substr(1,-1); + else + filename="\\\\?\\"+filename; + } + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + filename.ncopy(wfilename,wfilename_size); + DWORD dwAttrib; + dwAttrib = GetFileAttributesW(wfilename); + if((dwAttrib|1) == 0xFFFFFFFF) + { + USES_CONVERSION ; + dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt + } + retval=( (dwAttrib|1) != 0xFFFFFFFF); +#endif + } + return retval; +} + +// is_dir(url) -- +// -- returns true if url denotes a directory. +bool +GURL::is_dir(void) const +{ + bool retval=false; + if(is_local_file_url()) + { + // UNIX implementation +#if defined(UNIX) || defined(macintosh) || defined(OS2) + struct stat buf; + if (!urlstat(*this,buf)) + { + retval=(buf.st_mode & S_IFDIR); + } +#elif defined(WIN32) // (either Windows or WCE) + GUTF8String filename(UTF8Filename()); + if(filename.length() >= MAX_PATH) + { + if(!filename.cmp("\\\\",2)) + filename="\\\\?\\UNC"+filename.substr(1,-1); + else + filename="\\\\?\\"+filename; + } + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + filename.ncopy(wfilename,wfilename_size); + DWORD dwAttrib; + dwAttrib = GetFileAttributesW(wfilename); + if((dwAttrib|1) == 0xFFFFFFFF) + { + USES_CONVERSION ; + dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt + } + retval=((dwAttrib != 0xFFFFFFFF)&&( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +// Follows symbolic links. +GURL +GURL::follow_symlinks(void) const +{ + GURL ret = *this; +#if defined(S_IFLNK) +#if defined(UNIX) || defined(macintosh) + int lnklen; + char lnkbuf[MAXPATHLEN+1]; + struct stat buf; + while ( (urlstat(ret, buf) >= 0) && + (buf.st_mode & S_IFLNK) && + ((lnklen = readlink(ret.NativeFilename(),lnkbuf,sizeof(lnkbuf))) > 0) ) + { + lnkbuf[lnklen] = 0; + GNativeString lnk(lnkbuf); + ret = GURL(lnk, ret.base()); + } +#endif +#endif + return ret; +} + +int +GURL::mkdir() const +{ + if(! is_local_file_url()) + return -1; + int retval=0; + const GURL baseURL=base(); + if (baseURL.get_string() != url && !baseURL.is_dir()) + retval = baseURL.mkdir(); + if(!retval) + { +#if defined(UNIX) + if (is_dir()) + retval = 0; + else + retval = ::mkdir(NativeFilename(), 0755); +#elif defined(WIN32) + USES_CONVERSION; + if (is_dir()) + retval = 0; + else + retval = CreateDirectory(A2CT(NativeFilename()), NULL); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +// deletefile +// -- deletes a file or directory + +int +GURL::deletefile(void) const +{ + int retval = -1; + if(is_local_file_url()) + { +#if defined(UNIX) + if (is_dir()) + retval = ::rmdir(NativeFilename()); + else + retval = ::unlink(NativeFilename()); +#elif defined(WIN32) + USES_CONVERSION; + if (is_dir()) + retval = ::RemoveDirectory(A2CT(NativeFilename())); + else + retval = ::DeleteFile(A2CT(NativeFilename())); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +GList<GURL> +GURL::listdir(void) const +{ + GList<GURL> retval; + if(is_dir()) + { +#if defined(UNIX) || defined(OS2) + DIR * dir=opendir(NativeFilename());//MBCS cvt + for(dirent *de=readdir(dir);de;de=readdir(dir)) + { + const int len = NAMLEN(de); + if (de->d_name[0]== dot && len==1) + continue; + if (de->d_name[0]== dot && de->d_name[1]== dot && len==2) + continue; + retval.append(GURL::Native(de->d_name,*this)); + } + closedir(dir); +#elif defined (WIN32) + GURL::UTF8 wildcard("*.*",*this); + WIN32_FIND_DATA finddata; + HANDLE handle = FindFirstFile(wildcard.NativeFilename(), &finddata);//MBCS cvt + const GUTF8String gpathname=pathname(); + const GUTF8String gbase=base().pathname(); + if( handle != INVALID_HANDLE_VALUE) + { + do + { + GURL::UTF8 Entry(finddata.cFileName,*this); + const GUTF8String gentry=Entry.pathname(); + if((gentry != gpathname) && (gentry != gbase)) + retval.append(Entry); + } while( FindNextFile(handle, &finddata) ); + + FindClose(handle); + } +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +int +GURL::cleardir(const int timeout) const +{ + int retval=(-1); + if(is_dir()) + { + GList<GURL> dirlist=listdir(); + retval=0; + for(GPosition pos=dirlist;pos&&!retval;++pos) + { + const GURL &Entry=dirlist[pos]; + if(Entry.is_dir()) + { + if((retval=Entry.cleardir(timeout)) < 0) + { + break; + } + } + if(((retval=Entry.deletefile())<0) && (timeout>0)) + { + GOS::sleep(timeout); + retval=Entry.deletefile(); + } + } + } + return retval; +} + +int +GURL::renameto(const GURL &newurl) const +{ + if (is_local_file_url() && newurl.is_local_file_url()) + return rename(NativeFilename(),newurl.NativeFilename()); + return -1; +} + +// expand_name(filename[, fromdirname]) +// -- returns the full path name of filename interpreted +// relative to fromdirname. Use current working dir when +// fromdirname is null. +GUTF8String +GURL::expand_name(const GUTF8String &xfname, const char *from) +{ + const char *fname=xfname; + GUTF8String retval; + const size_t maxlen=xfname.length()*9+MAXPATHLEN+10; + char * const string_buffer = retval.getbuf(maxlen); + // UNIX implementation +#if defined(UNIX) + // Perform tilde expansion + GUTF8String senv; + if (fname && fname[0]==tilde) + { + int n; + for(n=1;fname[n] && fname[n]!= slash;n++) + EMPTY_LOOP; + struct passwd *pw=0; + if (n!=1) + { + GUTF8String user(fname+1, n-1); + pw=getpwnam(user); + }else if ((senv=GOS::getenv("HOME")).length()) + { + from=(const char *)senv; + fname = fname + n; + }else if ((senv=GOS::getenv("LOGNAME")).length()) + { + pw = getpwnam((const char *)senv.getUTF82Native()); + }else + { + pw=getpwuid(getuid()); + } + if (pw) + { + senv=GNativeString(pw->pw_dir).getNative2UTF8(); + from = (const char *)senv; + fname = fname + n; + } + for(;fname[0] == slash; fname++) + EMPTY_LOOP; + } + // Process absolute vs. relative path + if (fname && fname[0]== slash) + { + string_buffer[0]=slash; + string_buffer[1]=0; + }else if (from) + { + strcpy(string_buffer, expand_name(from)); + }else + { + strcpy(string_buffer, GOS::cwd()); + } + char *s = string_buffer + strlen(string_buffer); + if(fname) + { + for(;fname[0]== slash;fname++) + EMPTY_LOOP; + // Process path components + while(fname[0]) + { + if (fname[0] == dot ) + { + if (!fname[1] || fname[1]== slash) + { + fname++; + continue; + }else if (fname[1]== dot && (fname[2]== slash || !fname[2])) + { + fname +=2; + for(;s>string_buffer+1 && *(s-1)== slash; s--) + EMPTY_LOOP; + for(;s>string_buffer+1 && *(s-1)!= slash; s--) + EMPTY_LOOP; + continue; + } + } + if ((s==string_buffer)||(*(s-1)!= slash)) + { + *s = slash; + s++; + } + while (*fname &&(*fname!= slash)) + { + *s = *fname++; + if ((size_t)((++s)-string_buffer) > maxlen) + { + G_THROW( ERR_MSG("GURL.big_name") ); + } + } + *s = 0; + for(;fname[0]== slash;fname++) + EMPTY_LOOP; + } + } + if (!fname || !fname[0]) + { + for(;s>string_buffer+1 && *(s-1) == slash; s--) + EMPTY_LOOP; + *s = 0; + } +#elif defined (WIN32) // WIN32 implementation + // Handle base + strcpy(string_buffer, (char const *)(from ? expand_name(from) : GOS::cwd())); + // GNativeString native; + if (fname) + { + char *s = string_buffer; + char drv[4]; + // Handle absolute part of fname + // Put absolute part of the file name in string_buffer, and + // the relative part pointed to by fname. + if (fname[0]== slash || fname[0]== backslash) + { + if (fname[1]== slash || fname[1]== backslash) + { // Case "//abcd" + s[0]=s[1]= backslash; s[2]=0; + } + else + { // Case "/abcd" or "/" + // File is at the root of the current drive. Delete the + // slash at the beginning of the filename and leave + // an explicit identification of the root of the drive in + // string_buffer. + fname++; + s[3] = '\0'; + } + } + else if (fname[0] && fname[1]==colon) + { + if (fname[2]!= slash && fname[2]!= backslash) + { // Case "x:abcd" + if ( toupper((unsigned char)s[0]) != toupper((unsigned char)fname[0]) + || s[1]!=colon) + { + drv[0]=fname[0]; + drv[1]=colon; + drv[2]= dot ; + drv[3]=0; + GetFullPathName(drv, maxlen, string_buffer, &s); + strcpy(string_buffer,(const char *)GUTF8String(string_buffer).getNative2UTF8()); + s = string_buffer; + } + fname += 2; + } + else if (fname[3]!= slash && fname[3]!= backslash) + { // Case "x:/abcd" + s[0]=toupper((unsigned char)fname[0]); + s[1]=colon; + s[2]=backslash; + s[3]=0; + fname += 3; + } + else + { // Case "x://abcd" + s[0]=s[1]=backslash; + s[2]=0; + fname += 4; + } + } + // Process path components + for(;*fname== slash || *fname==backslash;fname++) + EMPTY_LOOP; + while(*fname) + { + if (fname[0]== dot ) + { + if (fname[1]== slash || fname[1]==backslash || !fname[1]) + { + fname++; + continue; + }else if ((fname[1] == dot) + && (fname[2]== slash || fname[2]==backslash || !fname[2])) + { + fname += 2; + char *back=_tcsrchr(string_buffer,backslash); + char *forward=_tcsrchr(string_buffer,slash); + if(back>forward) + { + *back=0; + }else if(forward) + { + *forward=0; + } + s = string_buffer; + continue; + } + char* s2=s;//MBCS DBCS + for(;*s;s++) + EMPTY_LOOP; + char* back = _tcsrchr(s2,backslash);//MBCS DBCS + if ((s>string_buffer)&&(*(s-1)!= slash)&& + (back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS + { + *s = backslash; + s++; + } + while (*fname && *fname!= slash && *fname!=backslash) + { + *s = *fname++; + if ((size_t)((++s)-string_buffer) > maxlen) + G_THROW( ERR_MSG("GURL.big_name") ); + } + *s = 0; + } + char* s2=s;//MBCS DBCS + for(;*s;s++) + EMPTY_LOOP; + char* back = _tcsrchr(s2,backslash);//MBCS DBCS + if ((s>string_buffer)&&(*(s-1)!= slash) + &&(back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS + { + *s = backslash; + s++; + } + while (*fname && (*fname!= slash) && (*fname!=backslash)) + { + *s = *fname++; + if ((size_t)((++s)-string_buffer) > maxlen) + G_THROW( ERR_MSG("GURL.big_name") ); + } + *s = 0; + for(;(*fname== slash)||(*fname==backslash);fname++) + EMPTY_LOOP; + } + } +#elif defined(macintosh) // MACINTOSH implementation + strcpy(string_buffer, (const char *)(from?from:GOS::cwd())); + + if (!GStringRep::cmp(fname, string_buffer,strlen(string_buffer)) || is_file(fname)) + { + strcpy(string_buffer, "");//please don't expand, the logic of filename is chaos. + } + + // Process path components + char *s = string_buffer + strlen(string_buffer); + if(fname) + { + for(;fname[0]==colon;fname++) + EMPTY_LOOP; + while(fname[0]) + { + if (fname[0]== dot ) + { + if (fname[1]==colon || !fname[1]) + { + fname++; + continue; + } + if ((fname[1]== dot ) + &&(fname[2]==colon || fname[2]==0)) + { + fname +=2; + for(;(s>string_buffer+1)&&(*(s-1)==colon);s--) + EMPTY_LOOP; + for(;(s>string_buffer+1)&&(*(s-1)!=colon);s--) + EMPTY_LOOP; + continue; + } + } + if ((s==string_buffer)||(*(s-1)!=colon)) + { + *s = colon; + s++; + } + while (*fname!=0 && *fname!=colon) + { + *s = *fname++; + if ((++s)-string_buffer > maxlen) + G_THROW( ERR_MSG("GURL.big_name") ); + } + *s = 0; + for(;fname[0]==colon;fname++) + EMPTY_LOOP; + } + } + for(;(s>string_buffer+1) && (*(s-1)==colon);s--) + EMPTY_LOOP; + *s = 0; + return ((string_buffer[0]==colon)?(string_buffer+1):string_buffer); +#else +# error "Define something here for your operating system" +#endif + return retval; +} + +unsigned int +hash(const GURL & gurl) +{ + unsigned int retval; + const GUTF8String s(gurl.get_string()); + const int len=s.length(); + if(len && (s[len-1] == '/')) // Don't include the trailing slash as part of the hash. + { + retval=hash(s.substr(0,len-1)); + }else + { + retval=hash(s); + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GURL.h b/kviewshell/plugins/djvu/libdjvu/GURL.h new file mode 100644 index 00000000..eb3ed4bc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GURL.h @@ -0,0 +1,446 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GURL.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GURL_H_ +#define _GURL_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GString.h" +#include "Arrays.h" +#include "GThreads.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name GURL.h + Files #"GURL.h"# and #"GURL.cpp"# contain the implementation of the + \Ref{GURL} class used to store URLs in a system independent format. + @memo System independent URL representation. + @author Andrei Erofeev <eaf@geocities.com> + +// From: Leon Bottou, 1/31/2002 +// This has been heavily changed by Lizardtech. +// They decided to use URLs for everyting, including +// the most basic file access. The URL class now is a unholy +// mixture of code for syntactically parsing the urls (which is was) +// and file status code (only for local file: urls). + + @version #$Id: GURL.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# +*/ + +//@{ + +/** System independent URL representation. + + This class is used in the library to store URLs in a system independent + format. The idea to use a general class to hold URL arose after we + realized, that DjVu had to be able to access files both from the WEB + and from the local disk. While it is strange to talk about system + independence of HTTP URLs, file names formats obviously differ from + platform to platform. They may contain forward slashes, backward slashes, + colons as separators, etc. There maybe more than one URL corresponding + to the same file name. Compare #file:/dir/file.djvu# and + #file://localhost/dir/file.djvu#. + + To simplify a developer's life we have created this class, which contains + inside a canonical representation of URLs. + + File URLs are converted to internal format with the help of \Ref{GOS} class. + + All other URLs are modified to contain only forward slashes. +*/ + +class GURL +{ +public: + class Filename; + class UTF8; + class Native; +protected: + /** @name Constructors + Accept the string URL, check that it starts from #file:/# + or #http:/# and convert to internal system independent + representation. + */ + //@{ + /// + GURL(const char * url_string); + //@} + +public: + GURL(void); + + GURL(const GUTF8String & url_string); + + GURL(const GNativeString & url_string); + + GURL(const GUTF8String &xurl, const GURL &codebase); + + GURL(const GNativeString &xurl, const GURL &codebase); + + /// Copy constructor + GURL(const GURL & gurl); + + /// The destructor + virtual ~GURL(void) {} + +private: + // The 'class_lock' should be locked whenever you're accessing + // url, or cgi_name_arr, or cgi_value_arr. + GCriticalSection class_lock; +protected: + GUTF8String url; + DArray<GUTF8String> cgi_name_arr, cgi_value_arr; + bool validurl; + + void init(const bool nothrow=false); + void convert_slashes(void); + void beautify_path(void); + static GUTF8String beautify_path(GUTF8String url); + + static GUTF8String protocol(const GUTF8String& url); + void parse_cgi_args(void); + void store_cgi_args(void); +public: + /// Test if the URL is valid. If invalid, reinitialize. + bool is_valid(void) const; // const lies to the compiler because of dependency problems + + /// Extracts the {\em protocol} part from the URL and returns it + GUTF8String protocol(void) const; + + /** Returns string after the first '\#' with decoded + escape sequences. */ + GUTF8String hash_argument(void) const; + + /** Inserts the #arg# after a separating hash into the URL. + The function encodes any illegal character in #arg# using + \Ref{GOS::encode_reserved}(). */ + void set_hash_argument(const GUTF8String &arg); + + /** Returns the total number of CGI arguments in the URL. + CGI arguments follow '#?#' sign and are separated by '#&#' signs */ + int cgi_arguments(void) const; + + /** Returns the total number of DjVu-related CGI arguments (arguments + following #DJVUOPTS# in the URL). */ + int djvu_cgi_arguments(void) const; + + /** Returns that part of CGI argument number #num#, which is + before the equal sign. */ + GUTF8String cgi_name(int num) const; + + /** Returns that part of DjVu-related CGI argument number #num#, + which is before the equal sign. */ + GUTF8String djvu_cgi_name(int num) const; + + /** Returns that part of CGI argument number #num#, which is + after the equal sign. */ + GUTF8String cgi_value(int num) const; + + /** Returns that part of DjVu-related CGI argument number #num#, + which is after the equal sign. */ + GUTF8String djvu_cgi_value(int num) const; + + /** Returns array of all known CGI names (part of CGI argument before + the equal sign) */ + DArray<GUTF8String>cgi_names(void) const; + + /** Returns array of names of DjVu-related CGI arguments (arguments + following #DJVUOPTS# option. */ + DArray<GUTF8String>djvu_cgi_names(void) const; + + /** Returns array of all known CGI names (part of CGI argument before + the equal sign) */ + DArray<GUTF8String>cgi_values(void) const; + + /** Returns array of values of DjVu-related CGI arguments (arguments + following #DJVUOPTS# option. */ + DArray<GUTF8String>djvu_cgi_values(void) const; + + /// Erases everything after the first '\#' or '?' + void clear_all_arguments(void); + + /// Erases everything after the first '\#' + void clear_hash_argument(void); + + /// Erases DjVu CGI arguments (following "#DJVUOPTS#") + void clear_djvu_cgi_arguments(void); + + /// Erases all CGI arguments (following the first '?') + void clear_cgi_arguments(void); + + /** Appends the specified CGI argument. Will insert "#DJVUOPTS#" if + necessary */ + void add_djvu_cgi_argument(const GUTF8String &name, const char * value=0); + + /** Returns the URL corresponding to the directory containing + the document with this URL. The function basically takes the + URL and clears everything after the last slash. */ + GURL base(void) const; + + /// Returns the aboslute URL without the host part. + GUTF8String pathname(void) const; + + /** Returns the name part of this URL. + For example, if the URL is #http://www.lizardtech.com/file%201.djvu# then + this function will return #file%201.djvu#. \Ref{fname}() will + return #file 1.djvu# at the same time. */ + GUTF8String name(void) const; + + /** Returns the name part of this URL with escape sequences expanded. + For example, if the URL is #http://www.lizardtech.com/file%201.djvu# then + this function will return #file 1.djvu#. \Ref{name}() will + return #file%201.djvu# at the same time. */ + GUTF8String fname(void) const; + + /// Returns the extention part of name of document in this URL. + GUTF8String extension(void) const; + + /// Checks if this is an empty URL + bool is_empty(void) const; + + /// Checks if the URL is local (starts from #file:/#) or not + bool is_local_file_url(void) const; + + /** @name Concatenation operators + Concatenate the GURL with the passed {\em name}. If the {\em name} + is absolute (has non empty protocol prefix), we just return + #GURL(name)#. Otherwise the #name# is appended to the GURL after a + separating slash. + */ + //@{ + /// +// GURL operator+(const GUTF8String &name) const; + //@} + + /// Returns TRUE if #gurl1# and #gurl2# are the same + bool operator==(const GURL & gurl2) const; + + /// Returns TRUE if #gurl1# and #gurl2# are different + bool operator!=(const GURL & gurl2) const; + + /// Assignment operator + GURL & operator=(const GURL & url); + + /// Returns Internal URL representation + operator const char*(void) const { return url; }; + + /** Returns a string representing the URL. This function normally + returns a standard file URL as described in RFC 1738. + Some versions of MSIE do not support this standard syntax. + A brain damaged MSIE compatible syntax is generated + when the optional argument #useragent# contains string #"MSIE"# or + #"Microsoft"#. */ + GUTF8String get_string(const GUTF8String &useragent) const; + + GUTF8String get_string(const bool nothrow=false) const; + + /// Escape special characters + static GUTF8String encode_reserved(const GUTF8String &gs); + + /** Decodes reserved characters from the URL. + See also: \Ref{encode_reserved}(). */ + static GUTF8String decode_reserved(const GUTF8String &url); + + /// Test if this url is an existing file, directory, or device. + bool is_local_path(void) const; + + /// Test if this url is an existing file. + bool is_file(void) const; + + /// Test if this url is an existing directory. + bool is_dir(void) const; + + /// Follows symbolic links. + GURL follow_symlinks(void) const; + + /// Creates the specified directory. + int mkdir(void) const; + + /** Deletes file or directory. + Directories are not deleted unless the directory is empty. + Returns a negative number if an error occurs. */ + int deletefile(void) const; + + /** Recursively erases contents of directory. The directory + itself will not be removed. */ + int cleardir(const int timeout=0) const; + + /// Rename a file or directory. + int renameto(const GURL &newurl) const; + + /// List the contents of a directory. + GList<GURL> listdir(void) const; + + /** Returns a filename for a URL. Argument #url# must be a legal file URL. + This function applies heuristic rules to convert the URL into a valid + file name. It is guaranteed that this function can properly parse all + URLs generated by #filename_to_url#. The heuristics also work better when + the file actually exists. The empty string is returned when this + function cannot parse the URL or when the URL is not a file URL. + URL formats are as described in RFC 1738 plus the following alternative + formats for files on the local host: + + file://<letter>:/<path> + file://<letter>|/<path> + file:/<path> + + which are accepted because various browsers recognize them.*/ + GUTF8String UTF8Filename(void) const; + /// Same but returns a native string. + GNativeString NativeFilename(void) const; + + /** Hashing function. + @return hash suitable for usage in \Ref{GMap} */ + friend unsigned int hash(const GURL & gurl); + + /** Returns fully qualified file names. This functions constructs the fully + qualified name of file or directory #filename#. When provided, the + optional argument #fromdirname# is used as the current directory when + interpreting relative specifications in #filename#. Function + #expand_name# is very useful for logically concatenating file names. It + knows which separators should be used for each operating system and it + knows which syntactical rules apply. */ + static GUTF8String expand_name(const GUTF8String &filename, const char *fromdirname=0); +}; + +class GURL::UTF8 : public GURL +{ +public: + UTF8(const GUTF8String &xurl); + UTF8(const GUTF8String &xurl, const GURL &codebase); +}; + +class GURL::Native : public GURL +{ +public: + Native(const GNativeString &xurl); + Native(const GNativeString &xurl, const GURL &codebase); +}; + +class GURL::Filename : public GURL +{ +public: + Filename(const GUTF8String &filename); + Filename(const GNativeString &filename); + class UTF8; + class Native; +}; + +class GURL::Filename::UTF8 : public GURL::Filename +{ +public: + UTF8(const GUTF8String &filename); +}; + +class GURL::Filename::Native : public GURL::Filename +{ +public: + Native(const GNativeString &filename); +}; + + +inline bool +GURL::operator!=(const GURL & gurl2) const +{ + return !(*this == gurl2); +} + +inline GUTF8String +GURL::protocol(void) const +{ + return protocol(get_string()); +} + +inline bool +GURL::is_empty(void) const +{ + return !url.length()||!get_string().length(); +} + +// Test if the URL is valid. +// If invalid, reinitialize and return the result. +inline bool +GURL::is_valid(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(true); + return validurl; +} + + + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GUnicode.cpp b/kviewshell/plugins/djvu/libdjvu/GUnicode.cpp new file mode 100644 index 00000000..dbbefc5c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GUnicode.cpp @@ -0,0 +1,790 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GUnicode.cpp,v 1.11 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GString.h" +#if HAS_ICONV +#include <iconv.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +static unsigned char nill=0; + +static void const * +checkmarks(void const * const xbuf, + unsigned int &bufsize, + GStringRep::EncodeType &rep) +{ + unsigned char const *buf=(unsigned char const *)xbuf; + if(bufsize >= 2 || (xbuf && !bufsize && rep != GStringRep::XOTHER)) + { + const unsigned int s=(((unsigned int)buf[0])<<8)+(unsigned int)buf[1]; + switch(s) + { + case 0: + if((bufsize>=4)||(!bufsize && rep == GStringRep::XUCS4BE) + ||(!bufsize && rep == GStringRep::XUCS4_2143)) + { + const unsigned int s=(((unsigned int)buf[2])<<8)+(unsigned int)buf[3]; + if(s == 0xfeff) + { + rep=GStringRep::XUCS4BE; + buf+=4; + }else if(s == 0xfffe) + { + rep=GStringRep::XUCS4_2143; + buf+=4; + } + } + break; + case 0xfffe: + if(((bufsize>=4)||(!bufsize && rep == GStringRep::XUCS4LE)) + && !((unsigned char *)buf)[2] && !((unsigned char *)buf)[3]) + { + rep=GStringRep::XUCS4LE; + buf+=4; + }else + { + rep=GStringRep::XUTF16LE; + buf+=2; + } + break; + case 0xfeff: + if(((bufsize>=4)||(!bufsize && rep == GStringRep::XUCS4_3412)) + && !((unsigned char *)buf)[2] && !((unsigned char *)buf)[3]) + { + rep=GStringRep::XUCS4_3412; + buf+=4; + }else + { + rep=GStringRep::XUTF16LE; + buf+=2; + } + break; + case 0xefbb: + if(((bufsize>=3)||(!bufsize && GStringRep::XUTF8 == rep))&&(buf[2] == 0xbf)) + { + rep=GStringRep::XUTF8; + buf+=3; + } + break; + default: + break; + } + } + if(buf != xbuf) + { + if(bufsize) + { + const size_t s=(size_t)xbuf-(size_t)buf; + if(bufsize> s) + { + bufsize-=s; + }else + { + bufsize=0; + buf=(const unsigned char *)&nill; + } + } + } + return buf; +} + +class GStringRep::Unicode : public GStringRep::UTF8 +{ +public: + GP<GStringRep> encoding; + EncodeType encodetype; + void *remainder; + GPBufferBase gremainder; +public: + Unicode(void); + /// virtual destructor. + virtual ~Unicode(); + + static GP<GStringRep> create(const unsigned int sz); + static GP<GStringRep> create(void const * const buf, unsigned int bufsize, + const EncodeType, const GP<GStringRep> &encoding); + static GP<GStringRep> create( void const * const buf, + unsigned int size, const EncodeType encodetype ); + static GP<GStringRep> create( void const * const buf, + const unsigned int size, GP<GStringRep> encoding ); + static GP<GStringRep> create( void const * const buf, + const unsigned int size, const GP<Unicode> &remainder ); + +protected: + virtual void set_remainder( void const * const buf, const unsigned int size, + const EncodeType encodetype ); + virtual void set_remainder( void const * const buf, const unsigned int size, + const GP<GStringRep> &encoding ); + virtual void set_remainder( const GP<Unicode> &remainder ); + virtual GP<Unicode> get_remainder(void) const; +}; +// static unsigned long UTF8toUCS4(unsigned char const *&,void const * const); +static unsigned long xUTF16toUCS4(unsigned short const *&s,void const * const); +static unsigned long UTF16BEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UTF16LEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4BEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4LEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4_3412toUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4_2143toUCS4(unsigned char const *&s,void const * const); + +GP<GStringRep> +GStringRep::Unicode::create(const unsigned int sz) +{ + GP<GStringRep> gaddr; + if (sz > 0) + { + GStringRep *addr; + gaddr=(addr=new GStringRep::Unicode); + addr->data=(char *)(::operator new(sz+1)); + addr->size = sz; + addr->data[sz] = 0; + } + return gaddr; +} + +GStringRep::Unicode::Unicode(void) +: encodetype(XUTF8), gremainder(remainder,0,1) {} + +GStringRep::Unicode::~Unicode() {} + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + unsigned int bufsize, + const EncodeType t, + const GP<GStringRep> &encoding) +{ + return (encoding->size) + ?create(xbuf,bufsize,encoding) + :create(xbuf,bufsize,t); +} + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + const unsigned int bufsize, + const GP<Unicode> &xremainder ) +{ + Unicode *r=xremainder; + GP<GStringRep> retval; + if(r) + { + const int s=r->gremainder; + if(xbuf && bufsize) + { + if(s) + { + void *buf; + GPBufferBase gbuf(buf,s+bufsize,1); + memcpy(buf,r->remainder,s); + memcpy((void *)((size_t)buf+s),xbuf,bufsize); + retval=((r->encoding) + ?create(buf,s+bufsize,r->encoding) + :create(buf,s+bufsize,r->encodetype)); + }else + { + retval=((r->encoding) + ?create(xbuf,bufsize,r->encoding) + :create(xbuf,bufsize,r->encodetype)); + } + }else if(s) + { + void *buf; + GPBufferBase gbuf(buf,s,1); + memcpy(buf,r->remainder,s); + retval=((r->encoding) + ?create(buf,s,r->encoding) + :create(buf,s,r->encodetype)); + }else + { + retval=((r->encoding) + ?create(0,0,r->encoding) + :create(0,0,r->encodetype)); + } + }else + { + retval=create(xbuf,bufsize,XUTF8); + } + return retval; +} + +#if HAS_ICONV +/* This template works around incompatible iconv protoypes */ +template<typename _T> inline size_t +iconv_adaptor(size_t(*iconv_func)(iconv_t, _T, size_t *, char**, size_t*), + iconv_t cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + return iconv_func (cd, (_T)inbuf, inbytesleft, outbuf, outbytesleft); +} +#endif + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + unsigned int bufsize, + GP<GStringRep> encoding) +{ + GP<GStringRep> retval; + GStringRep *e=encoding; + if(e) + { + e=(encoding=e->upcase()); + } + if(!e || !e->size) + { + retval=create(xbuf,bufsize,XOTHER); + }else if(!e->cmp("UTF8") || !e->cmp("UTF-8")) + { + retval=create(xbuf,bufsize,XUTF8); + }else if(!e->cmp("UTF16")|| !e->cmp("UTF-16") + || !e->cmp("UCS2") || !e->cmp("UCS2")) + { + retval=create(xbuf,bufsize,XUTF16); + }else if(!e->cmp("UCS4") || !e->cmp("UCS-4")) + { + retval=create(xbuf,bufsize,XUCS4); + }else + { +#if HAS_ICONV + EncodeType t=XOTHER; + void const * const buf=checkmarks(xbuf,bufsize,t); + if(t != XOTHER) + { + retval=create(xbuf,bufsize,t); + }else if(buf && bufsize) + { + unsigned char const *eptr=(unsigned char *)buf; + unsigned int j=0; + for(j=0;(j<bufsize)&&*eptr;j++,eptr++) + EMPTY_LOOP; + if (j) + { + unsigned char const *ptr=(unsigned char *)buf; + if(e) + { + iconv_t cv=iconv_open("UTF-8",(const char *)e); + if(cv == (iconv_t)(-1)) + { + const int i=e->search('-'); + if(i>=0) + { + cv=iconv_open("UTF-8",e->data+i+1); + } + } + if(cv == (iconv_t)(-1)) + { + retval=create(0,0,XOTHER); + }else + { + size_t ptrleft=(eptr-ptr); + char *utf8buf; + size_t pleft=6*ptrleft+1; + GPBuffer<char> gutf8buf(utf8buf,pleft); + char *p=utf8buf; + unsigned char const *last=ptr; + for(;iconv_adaptor(iconv, cv, (char**)&ptr, &ptrleft, &p, &pleft);last=ptr) + EMPTY_LOOP; + iconv_close(cv); + retval=create(utf8buf,(size_t)last-(size_t)buf,t); + retval->set_remainder(last,(size_t)eptr-(size_t)last,e); + } + } + }else + { + retval=create(0,0,XOTHER); + retval->set_remainder(0,0,e); + } + } +#else + retval=create(xbuf,bufsize,XOTHER); +#endif + } + return retval; +} + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + unsigned int bufsize, + EncodeType t) +{ + GP<GStringRep> gretval; + GStringRep *retval=0; + void const * const buf=checkmarks(xbuf,bufsize,t); + if(buf && bufsize) + { + unsigned char const *eptr=(unsigned char *)buf; + unsigned int maxutf8size=0; + void const* const xeptr=(void const *)((size_t)eptr+bufsize); + switch(t) + { + case XUCS4: + case XUCS4BE: + case XUCS4LE: + case XUCS4_2143: + case XUCS4_3412: + { + for(unsigned long w; + (eptr<xeptr)&&(w=*(unsigned long const *)eptr); + eptr+=sizeof(unsigned long)) + { + maxutf8size+=(w>0x7f)?6:1; + } + break; + } + case XUTF16: + case XUTF16BE: + case XUTF16LE: + { + for(unsigned short w; + (eptr<xeptr)&&(w=*(unsigned short const *)eptr); + eptr+=sizeof(unsigned short)) + { + maxutf8size+=3; + } + break; + } + case XUTF8: + for(;(eptr<xeptr)&&*eptr;maxutf8size++,eptr++) + EMPTY_LOOP; + break; + case XEBCDIC: + for(;(eptr<xeptr)&&*eptr;eptr++) + { + maxutf8size+=(*eptr>0x7f)?2:1; + } + break; + default: + break; + } + unsigned char *utf8buf=0; + GPBuffer<unsigned char> gutf8buf(utf8buf,maxutf8size+1); + utf8buf[0]=0; + if (maxutf8size) + { + unsigned char *optr=utf8buf; + int len=0; + unsigned char const *iptr=(unsigned char *)buf; + unsigned long w; + switch(t) + { + case XUCS4: + for(; + (iptr<eptr)&&(w=*(unsigned long const *)iptr); + len++,iptr+=sizeof(unsigned long const)) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4BE: + for(;(w=UCS4BEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4LE: + for(;(w=UCS4LEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4_2143: + for(;(w=UCS4_2143toUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4_3412: + for(;(w=UCS4_3412toUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF16: + for(; + (w=xUTF16toUCS4((unsigned short const*&)iptr,eptr)); + len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF16BE: + for(;(w=UTF16BEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF16LE: + for(;(w=UTF16LEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF8: + for(;(w=UTF8toUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XEBCDIC: + for(;(iptr<eptr)&&(w=*iptr++);len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + default: + break; + } + const unsigned int size=(size_t)optr-(size_t)utf8buf; + if(size) + { + retval=(gretval=GStringRep::Unicode::create(size)); + memcpy(retval->data,utf8buf,size); + }else + { + retval=(gretval=GStringRep::Unicode::create(1)); + retval->size=size; + } + retval->data[size]=0; + gutf8buf.resize(0); + const size_t s=(size_t)eptr-(size_t)iptr; + retval->set_remainder(iptr,s,t); + } + } + if(!retval) + { + retval=(gretval=GStringRep::Unicode::create(1)); + retval->data[0]=0; + retval->size=0; + retval->set_remainder(0,0,t); + } + return gretval; +} + +static unsigned long +xUTF16toUCS4(unsigned short const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned short const * const r=s+1; + if(r <= eptr) + { + unsigned long const W1=s[0]; + if((W1<0xD800)||(W1>0xDFFF)) + { + if((U=W1)) + { + s=r; + } + }else if(W1<=0xDBFF) + { + unsigned short const * const rr=r+1; + if(rr <= eptr) + { + unsigned long const W2=s[1]; + if(((W2>=0xDC00)||(W2<=0xDFFF))&&((U=(0x1000+((W1&0x3ff)<<10))|(W2&0x3ff)))) + { + s=rr; + }else + { + U=(unsigned int)(-1)-W1; + s=r; + } + } + } + } + return U; +} + +static unsigned long +UTF16BEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+2; + if(r <= eptr) + { + unsigned long const C1MSB=s[0]; + if((C1MSB<0xD8)||(C1MSB>0xDF)) + { + if((U=((C1MSB<<8)|((unsigned long)s[1])))) + { + s=r; + } + }else if(C1MSB<=0xDB) + { + unsigned char const * const rr=r+2; + if(rr <= eptr) + { + unsigned long const C2MSB=s[2]; + if((C2MSB>=0xDC)||(C2MSB<=0xDF)) + { + U=0x10000+((unsigned long)s[1]<<10)+(unsigned long)s[3] + +(((C1MSB<<18)|(C2MSB<<8))&0xc0300); + s=rr; + }else + { + U=(unsigned int)(-1)-((C1MSB<<8)|((unsigned long)s[1])); + s=r; + } + } + } + } + return U; +} + +static unsigned long +UTF16LEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+2; + if(r <= eptr) + { + unsigned long const C1MSB=s[1]; + if((C1MSB<0xD8)||(C1MSB>0xDF)) + { + if((U=((C1MSB<<8)|((unsigned long)s[0])))) + { + s=r; + } + }else if(C1MSB<=0xDB) + { + unsigned char const * const rr=r+2; + if(rr <= eptr) + { + unsigned long const C2MSB=s[3]; + if((C2MSB>=0xDC)||(C2MSB<=0xDF)) + { + U=0x10000+((unsigned long)s[0]<<10)+(unsigned long)s[2] + +(((C1MSB<<18)|(C2MSB<<8))&0xc0300); + s=rr; + }else + { + U=(unsigned int)(-1)-((C1MSB<<8)|((unsigned long)s[1])); + s=r; + } + } + } + } + return U; +} + +static unsigned long +UCS4BEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[0]<<8)|(unsigned long)s[1])<<8)|(unsigned long)s[2])<<8)|(unsigned long)s[3]); + if(U) + { + s=r; + } + } + return U; +} + +static unsigned long +UCS4LEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[3]<<8)|(unsigned long)s[2])<<8)|(unsigned long)s[1])<<8)|(unsigned long)s[0]); + if(U) + { + s=r; + } + } + return U; +} + +static unsigned long +UCS4_2143toUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[1]<<8)|(unsigned long)s[0])<<8)|(unsigned long)s[3])<<8)|(unsigned long)s[2]); + if(U) + { + s=r; + } + } + return U; +} + +static unsigned long +UCS4_3412toUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[2]<<8)|(unsigned long)s[3])<<8)|(unsigned long)s[0])<<8)|(unsigned long)s[1]); + if(U) + { + s=r; + } + } + return U; +} + +void +GStringRep::Unicode::set_remainder( void const * const buf, + const unsigned int size, const EncodeType xencodetype ) +{ + gremainder.resize(size,1); + if(size) + memcpy(remainder,buf,size); + encodetype=xencodetype; + encoding=0; +} + +void +GStringRep::Unicode::set_remainder( void const * const buf, + const unsigned int size, const GP<GStringRep> &xencoding ) +{ + gremainder.resize(size,1); + if(size) + memcpy(remainder,buf,size); + encoding=xencoding; + encodetype=XOTHER; +} + +void +GStringRep::Unicode::set_remainder( const GP<GStringRep::Unicode> &xremainder ) +{ + if(xremainder) + { + const int size=xremainder->gremainder; + gremainder.resize(size,1); + if(size) + memcpy(remainder,xremainder->remainder,size); + encodetype=xremainder->encodetype; + }else + { + gremainder.resize(0,1); + encodetype=XUTF8; + } +} + +GP<GStringRep::Unicode> +GStringRep::Unicode::get_remainder( void ) const +{ + return const_cast<GStringRep::Unicode *>(this); +} + +GUTF8String +GUTF8String::create(void const * const buf,const unsigned int size, + const EncodeType encodetype, const GUTF8String &encoding) +{ + return encoding.length() + ?create(buf,size,encodetype) + :create(buf,size,encoding); +} + +GUTF8String +GUTF8String::create( void const * const buf, + unsigned int size, const EncodeType encodetype ) +{ + GUTF8String retval; + retval.init(GStringRep::Unicode::create(buf,size,encodetype)); + return retval; +} + +GUTF8String +GUTF8String::create( void const * const buf, + const unsigned int size, const GP<GStringRep::Unicode> &remainder) +{ + GUTF8String retval; + retval.init(GStringRep::Unicode::create(buf,size,remainder)); + return retval; +} + +GUTF8String +GUTF8String::create( void const * const buf, + const unsigned int size, const GUTF8String &encoding ) +{ + GUTF8String retval; + retval.init(GStringRep::Unicode::create(buf,size,encoding )); + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IFFByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.cpp new file mode 100644 index 00000000..9bf184bd --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.cpp @@ -0,0 +1,558 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IFFByteStream.cpp,v 1.10 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// -- Implementation of IFFByteStream +// - Author: Leon Bottou, 06/1998 + +// From: Leon Bottou, 1/31/2002 +// This has been changed by Lizardtech to fit better +// with their re-implementation of ByteStreams. + +#include <assert.h> +#include "IFFByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// Constructor +IFFByteStream::IFFByteStream(const GP<ByteStream> &xbs,const int xpos) +: ByteStream::Wrapper(xbs), has_magic(false), ctx(0), dir(0) +{ + offset = seekto = xpos; +} + +// Destructor +IFFByteStream::~IFFByteStream() +{ + while (ctx) + close_chunk(); +} + +GP<IFFByteStream> +IFFByteStream::create(const GP<ByteStream> &bs) +{ + const int pos=bs->tell(); + return new IFFByteStream(bs,pos); +} + + +// IFFByteStream::ready +// -- indicates if bytestream is ready for reading +// returns number of bytes available + +int +IFFByteStream::ready() +{ + if (ctx && dir < 0) + return ctx->offEnd - offset; + else if (ctx) + return 1; + else + return 0; +} + + +// IFFByteStream::composite +// -- indicates if bytestream is ready for putting or getting chunks + +int +IFFByteStream::composite() +{ + if (ctx && !ctx->bComposite) + return 0; + else + return 1; +} + + + + +// IFFByteStream::check_id +// -- checks if an id is legal + +int +IFFByteStream::check_id(const char *id) +{ + int i; + // check absence of null bytes + for (i=0; i<4; i++) + if (id[i]<0x20 || id[i]>0x7e) + return -1; + // check composite chunks + static char *szComposite[] = { "FORM", "LIST", "PROP", "CAT ", 0 }; + for (i=0; szComposite[i]; i++) + if (!memcmp(id, szComposite[i], 4)) + return 1; + // check reserved chunks + static char *szReserved[] = { "FOR", "LIS", "CAT", 0 }; + for (i=0; szReserved[i]; i++) + if (!memcmp(id, szReserved[i], 3) && id[3]>='1' && id[3]<='9') + return -1; + // regular chunk + return 0; +} + + + +// IFFByteStream::get_chunk +// -- get next chunk header + +int +IFFByteStream::get_chunk(GUTF8String &chkid, int *rawoffsetptr, int *rawsizeptr) +{ + int bytes; + char buffer[8]; + + // Check that we are allowed to read a chunk + if (dir > 0) + G_THROW( ERR_MSG("IFFByteStream.read_write") ); + if (ctx && !ctx->bComposite) + G_THROW( ERR_MSG("IFFByteStream.not_ready") ); + dir = -1; + + // Seek to end of previous chunk if necessary + if (seekto > offset) + { + bs->seek(seekto); + offset = seekto; + } + + // Skip padding byte + if (ctx && offset == ctx->offEnd) + return 0; + if (offset & 1) + { + bytes = bs->read( (void*)buffer, 1); + if (bytes==0 && !ctx) + return 0; + offset += bytes; + } + + // Record raw offset + int rawoffset = offset; + + // Read chunk id (skipping magic sequences inserted here to make + // DjVu files recognizable.) + for(;;) + { + if (ctx && offset == ctx->offEnd) + return 0; + if (ctx && offset+4 > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.corrupt_end") ); + bytes = bs->readall( (void*)&buffer[0], 4); + offset = seekto = offset + bytes; + if (bytes==0 && !ctx) + return 0; + if (bytes != 4) + G_THROW( ByteStream::EndOfFile ); + if(buffer[0] != 0x41 || buffer[1] != 0x54 || + buffer[2] != 0x26 || buffer[3] != 0x54 ) + break; + has_magic=true; + } + + // Read chunk size + if (ctx && offset+4 > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.corrupt_end2") ); + bytes = bs->readall( (void*)&buffer[4], 4); + offset = seekto = offset + bytes; + if (bytes != 4) + G_THROW( ByteStream::EndOfFile ); + long size = ((unsigned char)buffer[4]<<24) | + ((unsigned char)buffer[5]<<16) | + ((unsigned char)buffer[6]<<8) | + ((unsigned char)buffer[7]); + if (ctx && offset+size > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.corrupt_mangled") ); + + // Check if composite + int composite = check_id(buffer); + if (composite < 0) + G_THROW( ERR_MSG("IFFByteStream.corrupt_id") ); + + // Read secondary id of composite chunk + if (composite) + { + if (ctx && ctx->offEnd<offset+4) + G_THROW( ERR_MSG("IFFByteStream.corrupt_header") ); + bytes = bs->readall( (void*)&buffer[4], 4); + offset += bytes; + if (bytes != 4) + G_THROW( ByteStream::EndOfFile ); + if (check_id(&buffer[4])) + G_THROW( ERR_MSG("IFFByteStream.corrupt_2nd_id") ); + } + + // Create context record + IFFContext *nctx = new IFFContext; + G_TRY + { + nctx->next = ctx; + nctx->offStart = seekto; + nctx->offEnd = seekto + size; + if (composite) + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4); + nctx->bComposite = 1; + } + else + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memset( (void*)(nctx->idTwo), 0, 4); + nctx->bComposite = 0; + } + } + G_CATCH_ALL + { + delete nctx; + G_RETHROW; + } + G_ENDCATCH; + + // Install context record + ctx = nctx; + chkid = GUTF8String(ctx->idOne, 4); + if (composite) + chkid = chkid + ":" + GUTF8String(ctx->idTwo, 4); + + // Return + if (rawoffsetptr) + *rawoffsetptr = rawoffset; + if (rawsizeptr) + *rawsizeptr = ( ctx->offEnd - rawoffset + 1) & ~0x1; + return size; +} + + + +// IFFByteStream::put_chunk +// -- write new chunk header + +void +IFFByteStream::put_chunk(const char *chkid, int insert_magic) +{ + int bytes; + char buffer[8]; + + // Check that we are allowed to write a chunk + if (dir < 0) + G_THROW( ERR_MSG("IFFByteStream.read_write") ); + if (ctx && !ctx->bComposite) + G_THROW( ERR_MSG("IFFByteStream.not_ready2") ); + dir = +1; + + // Check primary id + int composite = check_id(chkid); + if ((composite<0) || (composite==0 && chkid[4]) + || (composite && (chkid[4]!=':' || check_id(&chkid[5]) || chkid[9])) ) + G_THROW( ERR_MSG("IFFByteStream.bad_chunk") ); + + // Write padding byte + assert(seekto <= offset); + memset((void*)buffer, 0, 8); + if (offset & 1) + offset += bs->write((void*)&buffer[4], 1); + + // Insert magic to make this file recognizable as DjVu + if (insert_magic) + { + // Don't change the way you do the file magic! + // I rely on these bytes letters in some places + // (like DjVmFile.cpp and djvm.cpp) -- eaf + buffer[0]=0x41; + buffer[1]=0x54; + buffer[2]=0x26; + buffer[3]=0x54; + offset += bs->writall((void*)&buffer[0], 4); + } + + // Write chunk header + memcpy((void*)&buffer[0], (void*)&chkid[0], 4); + bytes = bs->writall((void*)&buffer[0], 8); + offset = seekto = offset + bytes; + if (composite) + { + memcpy((void*)&buffer[4], (void*)&chkid[5], 4); + bytes = bs->writall((void*)&buffer[4], 4); + offset = offset + bytes; + } + + // Create new context record + IFFContext *nctx = new IFFContext; + G_TRY + { + nctx->next = ctx; + nctx->offStart = seekto; + nctx->offEnd = 0; + if (composite) + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4); + nctx->bComposite = 1; + } + else + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memset( (void*)(nctx->idTwo), 0, 4); + nctx->bComposite = 0; + } + } + G_CATCH_ALL + { + delete nctx; + G_RETHROW; + } + G_ENDCATCH; + // Install context record and leave + ctx = nctx; +} + + + +void +IFFByteStream::close_chunk() +{ + // Check that this is ok + if (!ctx) + G_THROW( ERR_MSG("IFFByteStream.cant_close") ); + // Patch size field in new chunk + if (dir > 0) + { + ctx->offEnd = offset; + long size = ctx->offEnd - ctx->offStart; + char buffer[4]; + buffer[0] = (unsigned char)(size>>24); + buffer[1] = (unsigned char)(size>>16); + buffer[2] = (unsigned char)(size>>8); + buffer[3] = (unsigned char)(size); + bs->seek(ctx->offStart - 4); + bs->writall((void*)buffer, 4); + bs->seek(offset); + } + // Arrange for reader to seek at next chunk + seekto = ctx->offEnd; + // Remove ctx record + IFFContext *octx = ctx; + ctx = octx->next; + assert(ctx==0 || ctx->bComposite); + delete octx; +} + +// This is the same as above, but adds a seek to the close +// Otherwise an EOF in this chunk won't be reported until we +// try to open the next chunk, which makes error recovery +// very difficult. +void +IFFByteStream::seek_close_chunk(void) +{ + close_chunk(); + if ((dir <= 0)&&((!ctx)||(ctx->bComposite))&&(seekto > offset)) + { + bs->seek(seekto); + offset = seekto; + } +} + +// IFFByteStream::short_id +// Returns the id of the current chunk + +void +IFFByteStream::short_id(GUTF8String &chkid) +{ + if (!ctx) + G_THROW( ERR_MSG("IFFByteStream.no_chunk_id") ); + if (ctx->bComposite) + chkid = GUTF8String(ctx->idOne, 4) + ":" + GUTF8String(ctx->idTwo, 4); + else + chkid = GUTF8String(ctx->idOne, 4); +} + + +// IFFByteStream::full_id +// Returns the full chunk id of the current chunk + +void +IFFByteStream::full_id(GUTF8String &chkid) +{ + short_id(chkid); + if (ctx->bComposite) + return; + // Search parent FORM or PROP chunk. + for (IFFContext *ct = ctx->next; ct; ct=ct->next) + if (memcmp(ct->idOne, "FOR", 3)==0 || + memcmp(ct->idOne, "PRO", 3)==0 ) + { + chkid = GUTF8String(ct->idTwo, 4) + "." + chkid; + break; + } +} + + + +// IFFByteStream::read +// -- read bytes from IFF file chunk + +size_t +IFFByteStream::read(void *buffer, size_t size) +{ + if (! (ctx && dir < 0)) + G_THROW( ERR_MSG("IFFByteStream.not_ready3") ); + // Seek if necessary + if (seekto > offset) { + bs->seek(seekto); + offset = seekto; + } + // Ensure that read does not extend beyond chunk + if (offset > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.bad_offset") ); + if (offset + (long)size > ctx->offEnd) + size = (size_t) (ctx->offEnd - offset); + // Read bytes + size_t bytes = bs->read(buffer, size); + offset += bytes; + return bytes; +} + + +// IFFByteStream::write +// -- write bytes to IFF file chunk + +size_t +IFFByteStream::write(const void *buffer, size_t size) +{ + if (! (ctx && dir > 0)) + G_THROW( ERR_MSG("IFFByteStream.not_ready4") ); + if (seekto > offset) + G_THROW( ERR_MSG("IFFByteStream.cant_write") ); + size_t bytes = bs->write(buffer, size); + offset += bytes; + return bytes; +} + +// IFFByteStream::tell +// -- tell position + +long +IFFByteStream::tell() const +{ + return (seekto>offset)?seekto:offset; +} + +bool +IFFByteStream::compare(IFFByteStream &iff) +{ + bool retval=(&iff == this); + if(!retval) + { + GUTF8String chkid1, chkid2; + int size; + while((size=get_chunk(chkid1)) == iff.get_chunk(chkid2)) + { + if(chkid1 != chkid2) + { + break; + } + if(!size) + { + retval=true; + break; + } + char buf[4096]; + int len; + while((len=read(buf,sizeof(buf)))) + { + int s=0; + char buf2[sizeof(buf)]; + while(s<len) + { + const int i=iff.read(buf2+s,len-s); + if(!i) + break; + s+=i; + } + if((s != len)||memcmp(buf,buf2,len)) + break; + } + if(len) + break; + iff.close_chunk(); + close_chunk(); + } + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IFFByteStream.h b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.h new file mode 100644 index 00000000..cb1fb616 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.h @@ -0,0 +1,312 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IFFByteStream.h,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _IFFBYTESTREAM_H_ +#define _IFFBYTESTREAM_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name IFFByteStream.h + + Files #"IFFByteStream.h"# and #"IFFByteStream.cpp"# implement a parser for + files structured according the Electronic Arts ``EA IFF 85 Interchange + File Format''. IFF files are composed of a sequence of data {\em chunks}. + Each chunk is identified by a four character {\em chunk identifier} + describing the type of the data stored in the chunk. A few special chunk + identifiers, for instance #"FORM"#, are reserved for {\em composite + chunks} which themselves contain a sequence of data chunks. This + conventions effectively provides IFF files with a convenient hierarchical + structure. Composite chunks are further identified by a secondary chunk + identifier. + + We found convenient to define a {\em extended chunk identifier}. In the + case of a regular chunk, the extended chunk identifier is simply the + chunk identifier, as in #"PM44"#. In the case of a composite chunk, the + extended chunk identifier is composed by concatenating the main chunk + identifier, a colon, and the secondary chunk identifier, as in + #"FORM:DJVU"#. + + Class \Ref{IFFByteStream} provides a way to read or write IFF structured + files. Member functions provide an easy mean to position the underlying + \Ref{ByteStream} at the beginning of each chunk and to read or write the + data until reaching the end of the chunk. The utility program + \Ref{djvuinfo} demonstrates how to use class #IFFByteStream#. + + {\bf IFF Files and ZP-Coder} --- + Class #IFFByteStream# repositions the underlying ByteStream whenever a new + chunk is accessed. It is possible to code chunk data with the ZP-Coder + without worrying about the final file position. See class \Ref{ZPCodec} + for more details. + + {\bf DjVu IFF Files} --- We had initially planned to exactly follow the + IFF specifications. Then we realized that certain versions of MSIE + recognize any IFF file as a Microsoft AIFF sound file and pop a message + box "Cannot play that sound". It appears that the structure of AIFF files + is entirely modeled after the IFF standard, with small variations + regarding the endianness of numbers and the padding rules. We eliminate + this problem by casting the octet protection spell. Our IFF files always + start with the four octets #0x41,0x54,0x26,0x54# followed by the fully + conformant IFF byte stream. Class #IFFByteStream# silently skips these + four octets when it encounters them. + + {\bf References} --- EA IFF 85 Interchange File Format specification:\\ + \URL{http://www.cica.indiana.edu/graphics/image_specs/ilbm.format.txt} or + \URL{http://www.tnt.uni-hannover.de/soft/compgraph/fileformats/docs/iff.pre} + + @memo + IFF file parser. + @author + L\'eon Bottou <leonb@research.att.com> + +// From: Leon Bottou, 1/31/2002 +// This has been changed by Lizardtech to fit better +// with their re-implementation of ByteStreams. + + @version + #$Id: IFFByteStream.h,v 1.10 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "DjVuGlobal.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "GException.h" +#include "GString.h" +#include "ByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** ByteStream interface for an IFF file. + + Class #IFFByteStream# augments the #ByteStream# interface with + functions for navigating from chunk to chunk. It works in relation + with a ByteStream specified at construction time. + + {\bf Reading an IFF file} --- You can read an IFF file by constructing an + #IFFByteStream# object attached to the ByteStream containing the IFF file. + Calling function \Ref{get_chunk} positions the file pointer at the + beginning of the first chunk. You can then use \Ref{ByteStream::read} to + access the chunk data. Function #read# will return #0# if you attempt to + read past the end of the chunk, just as if you were trying to read past + the end of a file. You can at any time call function \Ref{close_chunk} to + terminate reading data in this chunk. The following chunks can be + accessed by calling #get_chunk# and #close_chunk# repeatedly until you + reach the end of the file. Function #read# is not very useful when + accessing a composite chunk. You can instead make nested calls to + functions #get_chunk# and #close_chunk# in order to access the chunks + located inside the composite chunk. + + {\bf Writing an IFF file} --- You can write an IFF file by constructing an + #IFFByteStream# object attached to the seekable ByteStream object that + will contain the IFF file. Calling function \Ref{put_chunk} creates a + first chunk header and positions the file pointer at the beginning of the + chunk. You can then use \Ref{ByteStream::write} to store the chunk data. + Calling function \Ref{close_chunk} terminates the current chunk. You can + append more chunks by calling #put_chunk# and #close_chunk# repeatedly. + Function #write# is not very useful for writing a composite chunk. You + can instead make nested calls to function #put_chunk# and #close_chunk# in + order to create chunks located inside the composite chunk. + + Writing an IFF file requires a seekable ByteStream (see + \Ref{ByteStream::is_seekable}). This is not much of a problem because you + can always create the IFF file into a \Ref{MemoryByteStream} and then use + \Ref{ByteStream::copy} to transfer the IFF file into a non seekable + ByteStream. */ + +class IFFByteStream : protected ByteStream::Wrapper +{ +protected: + IFFByteStream(const GP<ByteStream> &bs, const int pos); +public: + /** Constructs an IFFByteStream object attached to ByteStream #bs#. + Any ByteStream can be used when reading an IFF file. Writing + an IFF file however requires a seekable ByteStream. */ + static GP<IFFByteStream> create(const GP<ByteStream> &bs); + // --- BYTESTREAM INTERFACE + ~IFFByteStream(); + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual long tell(void) const; + // -- NAVIGATING CHUNKS + /** Enters a chunk for reading. Function #get_chunk# returns zero when the + last chunk has already been accessed. Otherwise it parses a chunk + header, positions the IFFByteStream at the beginning of the chunk data, + stores the extended chunk identifier into string #chkid#, and returns + the non zero chunk size. The file offset of the chunk data may be + retrieved using function #tell#. The chunk data can then be read using + function #read# until reaching the end of the chunk. Advanced users may + supply two pointers to integer variables using arguments #rawoffsetptr# + and #rawsizeptr#. These variables will be overwritten with the offset + and the length of the file segment containing both the chunk header and + the chunk data. */ + int get_chunk(GUTF8String &chkid, int *rawoffsetptr=0, int *rawsizeptr=0); + /** Enters a chunk for writing. Function #put_chunk# prepares a chunk + header and positions the IFFByteStream at the beginning of the chunk + data. Argument #chkid# defines a extended chunk identifier for this + chunk. The chunk data can then be written using function #write#. The + chunk is terminated by a matching call to function #close_chunk#. When + #insertmagic# is non zero, function #put_chunk# inserts the bytes: + 0x41, 0x54, 0x26, 0x54 before the chunk header, as discussed in + \Ref{IFFByteStream.h}. */ + void put_chunk(const char *chkid, int insertmagic=0); + /** Leaves the current chunk. This function leaves the chunk previously + entered by a matching call to #get_chunk# and #put_chunk#. The + IFFByteStream is then ready to process the next chunk at the same + hierarchical level. */ + void close_chunk(); + /** This is identical to the above, plus it adds a seek to the start of + the next chunk. This way we catch EOF errors with the current chunk.*/ + void seek_close_chunk(); + /** Returns true when it is legal to call #read# or #write#. */ + int ready(); + /** Returns true when the current chunk is a composite chunk. */ + int composite(); + /** Returns the current chunk identifier of the current chunk. String + #chkid# is overwritten with the {\em extended chunk identifier} of the + current chunk. The extended chunk identifier of a regular chunk is + simply the chunk identifier, as in #"PM44"#. The extended chunk + identifier of a composite chunk is the concatenation of the chunk + identifier, of a semicolon #":"#, and of the secondary chunk identifier, + as in #"FORM:DJVU"#. */ + void short_id(GUTF8String &chkid); + /** Returns the qualified chunk identifier of the current chunk. String + #chkid# is overwritten with the {\em qualified chunk identifier} of the + current chunk. The qualified chunk identifier of a composite chunk is + equal to the extended chunk identifier. The qualified chunk identifier + of a regular chunk is composed by concatenating the secondary chunk + identifier of the closest #"FORM"# or #"PROP"# composite chunk + containing the current chunk, a dot #"."#, and the current chunk + identifier, as in #"DJVU.INFO"#. According to the EA IFF 85 identifier + scoping rules, the qualified chunk identifier uniquely defines how the + chunk data should be interpreted. */ + void full_id(GUTF8String &chkid); + /** Checks a potential chunk identifier. This function categorizes the + chunk identifier formed by the first four characters of string #chkid#. + It returns #0# if this is a legal identifier for a regular chunk. It + returns #+1# if this is a reserved composite chunk identifier. It + returns #-1# if this is an illegal or otherwise reserved identifier + which should not be used. */ + static int check_id(const char *id); + GP<ByteStream> get_bytestream(void) {return this;} + /** Copy data from another ByteStream. A maximum of #size# bytes are read + from the ByteStream #bsfrom# and are written to the ByteStream #*this# + at the current position. Less than #size# bytes may be written if an + end-of-file mark is reached on #bsfrom#. This function returns the + total number of bytes copied. Setting argument #size# to zero (the + default value) has a special meaning: the copying process will continue + until reaching the end-of-file mark on ByteStream #bsfrom#, regardless + of the number of bytes transferred. */ + size_t copy(ByteStream &bsfrom, size_t size=0) + { return get_bytestream()->copy(bsfrom,size); } + /** Flushes all buffers in the ByteStream. Calling this function + guarantees that pending data have been actually written (i.e. passed to + the operating system). Class #ByteStream# provides a default + implementation which does nothing. */ + virtual void flush(void) + { ByteStream::Wrapper::flush(); } + /** This is a simple compare method. The IFFByteStream may be read for + the sake of the comparison. Since IFFByteStreams are non-seekable, + the stream is not valid for use after comparing, regardless of the + result. */ + bool compare(IFFByteStream &iff); + /** #has_magic# is true if the stream has the DjVu file magic. + */ + bool has_magic; +private: + // private datatype + struct IFFContext + { + IFFContext *next; + long offStart; + long offEnd; + char idOne[4]; + char idTwo[4]; + char bComposite; + }; + // Implementation + IFFContext *ctx; + long offset; + long seekto; + int dir; + // Cancel C++ default stuff + IFFByteStream(const IFFByteStream &); + IFFByteStream & operator=(const IFFByteStream &); + static GP<IFFByteStream> create(ByteStream *bs); +}; + +//@} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp b/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp new file mode 100644 index 00000000..c63eda7d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp @@ -0,0 +1,1797 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IW44EncodeCodec.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 08/1998 +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split this file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#define IW44IMAGE_IMPLIMENTATION /* */ + + + +#include "IW44Image.h" +#include "ZPCodec.h" +#include "GBitmap.h" +#include "GPixmap.h" +#include "IFFByteStream.h" +#include "GRect.h" + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "MMX.h" +#undef IWTRANSFORM_TIMER +#ifdef IWTRANSFORM_TIMER +#include "GOS.h" +#endif + +#include <assert.h> +#include <string.h> +#include <math.h> + +#ifndef NEED_DECODER_ONLY + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +#define IWALLOCSIZE 4080 +#define IWCODEC_MAJOR 1 +#define IWCODEC_MINOR 2 +#define DECIBEL_PRUNE 5.0 + + +////////////////////////////////////////////////////// +// WAVELET DECOMPOSITION CONSTANTS +////////////////////////////////////////////////////// + +// Parameters for IW44 wavelet. +// - iw_norm: norm of all wavelets (for db estimation) +// - iw_shift: scale applied before decomposition + + +static const float iw_norm[16] = { + 2.627989e+03F, + 1.832893e+02F, 1.832959e+02F, 5.114690e+01F, + 4.583344e+01F, 4.583462e+01F, 1.279225e+01F, + 1.149671e+01F, 1.149712e+01F, 3.218888e+00F, + 2.999281e+00F, 2.999476e+00F, 8.733161e-01F, + 1.074451e+00F, 1.074511e+00F, 4.289318e-01F +}; + +static const int iw_shift = 6; +static const int iw_round = (1<<(iw_shift-1)); + +static const struct { int start; int size; } +bandbuckets[] = +{ + // Code first bucket and number of buckets in each band + { 0, 1 }, // -- band zero contains all lores info + { 1, 1 }, { 2, 1 }, { 3, 1 }, + { 4, 4 }, { 8, 4 }, { 12,4 }, + { 16,16 }, { 32,16 }, { 48,16 }, +}; + + +/** IW44 encoded gray-level image. This class provided functions for managing + a gray level image represented as a collection of IW44 wavelet + coefficients. The coefficients are stored in a memory efficient data + structure. Member function \Ref{get_bitmap} renders an arbitrary segment + of the image into a \Ref{GBitmap}. Member functions \Ref{decode_iff} and + \Ref{encode_iff} read and write DjVu IW44 files (see \Ref{IW44Image.h}). + Both the copy constructor and the copy operator are declared as private + members. It is therefore not possible to make multiple copies of instances + of this class. */ + +class IWBitmap::Encode : public IWBitmap +{ +public: + /// Destructor + virtual ~Encode(void); + /** Null constructor. Constructs an empty IWBitmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + Encode(void); + /** Initializes an IWBitmap with image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). */ + void init(const GBitmap &bm, const GP<GBitmap> mask=0); + // CODER + /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls + how much data is generated. The chunk data is written to ByteStream + #bs# with no IFF header. Successive calls to #encode_chunk# encode + successive chunks. You must call #close_codec# after encoding the last + chunk of a file. */ + virtual int encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parms); + /** Writes a gray level image into DjVu IW44 file. This function creates a + composite chunk (identifier #FORM:BM44#) composed of #nchunks# chunks + (identifier #BM44#). Data for each chunk is generated with + #encode_chunk# using the corresponding parameters in array #parms#. */ + virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); + /** Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); +protected: + Codec::Encode *ycodec_enc; +}; + +/** IW44 encoded color image. This class provided functions for managing a + color image represented as a collection of IW44 wavelet coefficients. The + coefficients are stored in a memory efficient data structure. Member + function \Ref{get_pixmap} renders an arbitrary segment of the image into a + \Ref{GPixmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} + read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy + constructor and the copy operator are declared as private members. It is + therefore not possible to make multiple copies of instances of this + class. */ + +class IWPixmap::Encode : public IWPixmap +{ +public: + enum CRCBMode { + CRCBnone=IW44Image::CRCBnone, + CRCBhalf=IW44Image::CRCBhalf, + CRCBnormal=IW44Image::CRCBnormal, + CRCBfull=IW44Image::CRCBfull }; + /// Destructor + virtual ~Encode(void); + /** Null constructor. Constructs an empty IWPixmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + Encode(void); + /** Initializes an IWPixmap with color image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). + Argument #crcbmode# specifies how the chrominance information should be + encoded (see \Ref{CRCBMode}). */ + void init(const GPixmap &bm, const GP<GBitmap> mask=0, CRCBMode crcbmode=CRCBnormal); + // CODER + /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls + how much data is generated. The chunk data is written to ByteStream + #bs# with no IFF header. Successive calls to #encode_chunk# encode + successive chunks. You must call #close_codec# after encoding the last + chunk of a file. */ + virtual int encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parms); + /** Writes a color image into a DjVu IW44 file. This function creates a + composite chunk (identifier #FORM:PM44#) composed of #nchunks# chunks + (identifier #PM44#). Data for each chunk is generated with + #encode_chunk# using the corresponding parameters in array #parms#. */ + virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); + /** Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); +protected: + Codec::Encode *ycodec_enc, *cbcodec_enc, *crcodec_enc; +}; + +class IW44Image::Map::Encode : public IW44Image::Map // DJVU_CLASS +{ +public: + Encode(const int w, const int h) : Map(w,h) {} + // creation (from image) + void create(const signed char *img8, int imgrowsize, + const signed char *msk8=0, int mskrowsize=0); + // slash resolution + void slashres(int res); +}; + +class IW44Image::Codec::Encode : public IW44Image::Codec +{ +public: + Encode(IW44Image::Map &map); + // Coding + virtual int code_slice(ZPCodec &zp); + float estimate_decibel(float frac); + // Data + void encode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, IW44Image::Block &eblk, int fbucket, int nbucket); + int encode_prepare(int band, int fbucket, int nbucket, IW44Image::Block &blk, IW44Image::Block &eblk); + IW44Image::Map emap; +}; + +IW44Image::Codec::Encode::Encode(IW44Image::Map &map) +: Codec(map), emap(map.iw,map.ih) {} + +////////////////////////////////////////////////////// +/** IW44Image::Transform::Encode +*/ + +class IW44Image::Transform::Encode : IW44Image::Transform +{ + public: + // WAVELET TRANSFORM + /** Forward transform. */ + static void forward(short *p, int w, int h, int rowsize, int begin, int end); + + // COLOR TRANSFORM + /** Extracts Y */ + static void RGB_to_Y(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize); + /** Extracts Cb */ + static void RGB_to_Cb(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize); + /** Extracts Cr */ + static void RGB_to_Cr(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize); +}; + + +////////////////////////////////////////////////////// +// MMX IMPLEMENTATION HELPERS +////////////////////////////////////////////////////// + + +// Note: +// MMX implementation for vertical transforms only. +// Speedup is basically related to faster memory transfer +// The IW44 transform is not CPU bound, it is memory bound. + +#ifdef MMX + +static const short w9[] = {9,9,9,9}; +static const short w1[] = {1,1,1,1}; +static const int d8[] = {8,8}; +static const int d16[] = {16,16}; + + +static inline void +mmx_fv_1 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+8)>>4); + q++; + } + while (q+3<e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d8,mm0); + MMXar( paddd, d8,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 4,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 4,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( psubw, mm0,mm7); // MM7=[ p3-x3, p2-x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + +static inline void +mmx_fv_2 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+16)>>5); + q ++; + } + while (q+3<e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d16,mm0); + MMXar( paddd, d16,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 5,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 5,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( paddw, mm0,mm7); // MM7=[ p3+x3, p2+x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + +#endif /* MMX */ + +////////////////////////////////////////////////////// +// NEW FILTERS +////////////////////////////////////////////////////// + +static void +filter_fv(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale*rowsize; + int s3 = s+s+s; + h = ((h-1)/scale)+1; + y += 1; + p += s; + while (y-3 < h) + { + // 1-Delta + { + short *q = p; + short *e = q+w; + if (y>=3 && y+3<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_fv_1(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+8)>>4); + q += scale; + } + } + else if (y<h) + { + // Special cases + short *q1 = (y+1<h ? q+s : q-s); + while (q<e) + { + int a = (int)q[-s] + (int)(*q1); + *q -= ((a+1)>>1); + q += scale; + q1 += scale; + } + } + } + // 2-Update + { + short *q = p-s3; + short *e = q+w; + if (y>=6 && y<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_fv_2(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+16)>>5); + q += scale; + } + } + else if (y>=3) + { + // Special cases + short *q1 = (y-2<h ? q+s : 0); + short *q3 = (y<h ? q+s3 : 0); + if (y>=6) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (int)q[-s3] + (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else if (y>=4) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else + { + while (q<e) + { + int a = (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + } + } + y += 2; + p += s+s; + } +} + +static void +filter_fh(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale; + int s3 = s+s+s; + rowsize *= scale; + while (y<h) + { + short *q = p+s; + short *e = p+w; + int a0=0, a1=0, a2=0, a3=0; + int b0=0, b1=0, b2=0, b3=0; + if (q < e) + { + // Special case: x=1 + a1 = a2 = a3 = q[-s]; + if (q+s<e) + a2 = q[s]; + if (q+s3<e) + a3 = q[s3]; + b3 = q[0] - ((a1+a2+1)>>1); + q[0] = b3; + q += s+s; + } + while (q+s3 < e) + { + // Generic case + a0=a1; + a1=a2; + a2=a3; + a3=q[s3]; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+8) >> 4); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); + q += s+s; + } + while (q < e) + { + // Special case: w-3 <= x < w + a1=a2; + a2=a3; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((a1+a2+1)>>1); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); + q += s+s; + } + while (q-s3 < e) + { + // Special case w <= x < w+3 + b0=b1; + b1=b2; + b2=b3; + b3=0; + if (q-s3 >= p) + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); + q += s+s; + } + y += scale; + p += rowsize; + } +} + + +////////////////////////////////////////////////////// +// WAVELET TRANSFORM +////////////////////////////////////////////////////// + + +//---------------------------------------------------- +// Function for applying bidimensional IW44 between +// scale intervals begin(inclusive) and end(exclusive) + +void +IW44Image::Transform::Encode::forward(short *p, int w, int h, int rowsize, int begin, int end) +{ + + // PREPARATION + filter_begin(w,h); + // LOOP ON SCALES + for (int scale=begin; scale<end; scale<<=1) + { +#ifdef IWTRANSFORM_TIMER + int tv,th; + th = tv = GOS::ticks(); +#endif + filter_fh(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks(); + tv = th - tv; +#endif + filter_fv(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks()-th; + DjVuPrintErrorUTF8("forw%d\tv=%dms h=%dms\n", scale,th,tv); +#endif + } + // TERMINATE + filter_end(); +} + +////////////////////////////////////////////////////// +// COLOR TRANSFORM +////////////////////////////////////////////////////// + +static const float +rgb_to_ycc[3][3] = +{ { 0.304348F, 0.608696F, 0.086956F }, + { 0.463768F, -0.405797F, -0.057971F }, + {-0.173913F, -0.347826F, 0.521739F } }; + + +/* Extracts Y */ +void +IW44Image::Transform::Encode::RGB_to_Y(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize) +{ + int rmul[256], gmul[256], bmul[256]; + for (int k=0; k<256; k++) + { + rmul[k] = (int)(k*0x10000*rgb_to_ycc[0][0]); + gmul[k] = (int)(k*0x10000*rgb_to_ycc[0][1]); + bmul[k] = (int)(k*0x10000*rgb_to_ycc[0][2]); + } + for (int i=0; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int y = rmul[p2->r] + gmul[p2->g] + bmul[p2->b] + 32768; + *out2 = (y>>16) - 128; + } + } +} + +#ifdef min +#undef min +#endif +static inline int min(const int x,const int y) {return (x<y)?x:y;} +#ifdef max +#undef max +#endif +static inline int max(const int x,const int y) {return (x>y)?x:y;} + +/* Extracts Cb */ +void +IW44Image::Transform::Encode::RGB_to_Cb(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize) +{ + int rmul[256], gmul[256], bmul[256]; + for (int k=0; k<256; k++) + { + rmul[k] = (int)(k*0x10000*rgb_to_ycc[2][0]); + gmul[k] = (int)(k*0x10000*rgb_to_ycc[2][1]); + bmul[k] = (int)(k*0x10000*rgb_to_ycc[2][2]); + } + for (int i=0; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int c = rmul[p2->r] + gmul[p2->g] + bmul[p2->b] + 32768; + *out2 = max(-128, min(127, c>>16)); + } + } +} + +/* Extracts Cr */ +void +IW44Image::Transform::Encode::RGB_to_Cr(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize) +{ + int rmul[256], gmul[256], bmul[256]; + for (int k=0; k<256; k++) + { + rmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][0]); + gmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][1]); + bmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][2]); + } + for (int i=0; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int c = rmul[p2->r] + gmul[p2->g] + bmul[p2->b] + 32768; + *out2 = max(-128, min(127, c>>16)); + } + } +} + + +////////////////////////////////////////////////////// +// MASKING DECOMPOSITION +////////////////////////////////////////////////////// + +//---------------------------------------------------- +// Function for applying bidimensional IW44 between +// scale intervals begin(inclusive) and end(exclusive) +// with a MASK bitmap + + +static void +interpolate_mask(short *data16, int w, int h, int rowsize, + const signed char *mask8, int mskrowsize) +{ + int i,j; + // count masked bits + short *count; + GPBuffer<short> gcount(count,w*h); + short *cp = count; + for (i=0; i<h; i++, cp+=w, mask8+=mskrowsize) + for (j=0; j<w; j++) + cp[j] = (mask8[j] ? 0 : 0x1000); + // copy image + short *sdata; + GPBuffer<short> gsdata(sdata,w*h); + short *p = sdata; + short *q = data16; + for (i=0; i<h; i++, p+=w, q+=rowsize) + for (j=0; j<w; j++) + p[j] = q[j]; + // iterate over resolutions + int split = 1; + int scale = 2; + int again = 1; + while (again && scale<w && scale<h) + { + again = 0; + p = data16; + q = sdata; + cp = count; + // iterate over block + for (i=0; i<h; i+=scale, cp+=w*scale, q+=w*scale, p+=rowsize*scale) + for (j=0; j<w; j+=scale) + { + int ii, jj; + int gotz = 0; + int gray = 0; + int npix = 0; + short *cpp = cp; + short *qq = q; + // look around when square goes beyond border + int istart = i; + if (istart+split>h) + { + istart -= scale; + cpp -= w*scale; + qq -= w*scale; + } + int jstart = j; + if (jstart+split>w) + jstart -= scale; + // compute gray level + for (ii=istart; ii<i+scale && ii<h; ii+=split, cpp+=w*split, qq+=w*split) + for (jj=jstart; jj<j+scale && jj<w; jj+=split) + { + if (cpp[jj]>0) + { + npix += cpp[jj]; + gray += cpp[jj] * qq[jj]; + } + else if (ii>=i && jj>=j) + { + gotz = 1; + } + } + // process result + if (npix == 0) + { + // continue to next resolution + again = 1; + cp[j] = 0; + } + else + { + gray = gray / npix; + // check whether initial image require fix + if (gotz) + { + cpp = cp; + qq = p; + for (ii=i; ii<i+scale && ii<h; ii+=1, cpp+=w, qq+=rowsize) + for (jj=j; jj<j+scale && jj<w; jj+=1) + if (cpp[jj] == 0) + { + qq[jj] = gray; + cpp[jj] = 1; + } + } + // store average for next iteration + cp[j] = npix>>2; + q[j] = gray; + } + } + // double resolution + split = scale; + scale = scale+scale; + } +} + + +static void +forward_mask(short *data16, int w, int h, int rowsize, int begin, int end, + const signed char *mask8, int mskrowsize ) +{ + int i,j; + signed char *m; + short *p; + short *d; + // Allocate buffers + short *sdata; + GPBuffer<short> gsdata(sdata,w*h); + signed char *smask; + GPBuffer<signed char> gsmask(smask,w*h); + // Copy mask + m = smask; + for (i=0; i<h; i+=1, m+=w, mask8+=mskrowsize) + memcpy((void*)m, (void*)mask8, w); + // Loop over scale + for (int scale=begin; scale<end; scale<<=1) + { + // Copy data into sdata buffer + p = data16; + d = sdata; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + d[j] = p[j]; + p += rowsize * scale; + d += w * scale; + } + // Decompose + IW44Image::Transform::Encode::forward(sdata, w, h, w, scale, scale+scale); + // Cancel masked coefficients + d = sdata; + m = smask; + for (i=0; i<h; i+=scale+scale) + { + for (j=scale; j<w; j+=scale+scale) + if (m[j]) + d[j] = 0; + d += w * scale; + m += w * scale; + if (i+scale < h) + { + for (j=0; j<w; j+=scale) + if (m[j]) + d[j] = 0; + d += w * scale; + m += w * scale; + } + } + // Reconstruct + IW44Image::Transform::Decode::backward(sdata, w, h, w, scale+scale, scale); + // Correct visible pixels + p = data16; + d = sdata; + m = smask; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + if (! m[j]) + d[j] = p[j]; + p += rowsize*scale; + m += w*scale; + d += w*scale; + } + // Decompose again (no need to iterate actually!) + IW44Image::Transform::Encode::forward(sdata, w, h, w, scale, scale+scale); + // Copy coefficients from sdata buffer + p = data16; + d = sdata; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + p[j] = d[j]; + p += rowsize * scale; + d += w * scale; + } + // Compute new mask for next scale + m = smask; + signed char *m0 = m; + signed char *m1 = m; + for (i=0; i<h; i+=scale+scale) + { + m0 = m1; + if (i+scale < h) + m1 = m + w*scale; + for (j=0; j<w; j+=scale+scale) + if (m[j] && m0[j] && m1[j] && (j<=0 || m[j-scale]) && (j+scale>=w || m[j+scale])) + m[j] = 1; + else + m[j] = 0; + m = m1 + w*scale; + } + } + // Free buffers +} + +void +IW44Image::Map::Encode::create(const signed char *img8, int imgrowsize, + const signed char *msk8, int mskrowsize ) +{ + int i, j; + // Progress + DJVU_PROGRESS_TASK(transf,"create iw44 map",3); + // Allocate decomposition buffer + short *data16; + GPBuffer<short> gdata16(data16,bw*bh); + // Copy pixels + short *p = data16; + const signed char *row = img8; + for (i=0; i<ih; i++) + { + for (j=0; j<iw; j++) + *p++ = (int)(row[j]) << iw_shift; + row += imgrowsize; + for (j=iw; j<bw; j++) + *p++ = 0; + } + for (i=ih; i<bh; i++) + for (j=0; j<bw; j++) + *p++ = 0; + // Handle bitmask + if (msk8) + { + // Interpolate pixels below mask + DJVU_PROGRESS_RUN(transf, 1); + interpolate_mask(data16, iw, ih, bw, msk8, mskrowsize); + // Multiscale iterative masked decomposition + DJVU_PROGRESS_RUN(transf, 3); + forward_mask(data16, iw, ih, bw, 1, 32, msk8, mskrowsize); + } + else + { + // Perform traditional decomposition + DJVU_PROGRESS_RUN(transf, 3); + IW44Image::Transform::Encode::forward(data16, iw, ih, bw, 1, 32); + } + // Copy coefficient into blocks + p = data16; + IW44Image::Block *block = blocks; + for (i=0; i<bh; i+=32) + { + for (j=0; j<bw; j+=32) + { + short liftblock[1024]; + // transfer coefficients at (p+j) into aligned block + short *pp = p + j; + short *pl = liftblock; + for (int ii=0; ii<32; ii++, pp+=bw) + for (int jj=0; jj<32; jj++) + *pl++ = pp[jj]; + // transfer into IW44Image::Block (apply zigzag and scaling) + block->read_liftblock(liftblock, this); + block++; + } + // next row of blocks + p += 32*bw; + } +} + +void +IW44Image::Map::Encode::slashres(int res) +{ + int minbucket = 1; + if (res < 2) + return; + else if (res < 4) + minbucket=16; + else if (res < 8) + minbucket=4; + for (int blockno=0; blockno<nb; blockno++) + for (int buckno=minbucket; buckno<64; buckno++) + blocks[blockno].zero(buckno); +} + +// encode_prepare +// -- compute the states prior to encoding the buckets +int +IW44Image::Codec::Encode::encode_prepare(int band, int fbucket, int nbucket, IW44Image::Block &blk, IW44Image::Block &eblk) +{ + int bbstate = 0; + // compute state of all coefficients in all buckets + if (band) + { + // Band other than zero + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + { + const short *pcoeff = blk.data(fbucket+buckno); + const short *epcoeff = eblk.data(fbucket+buckno); + int bstatetmp = 0; + if (! pcoeff) + { + bstatetmp = UNK; + // cstate[i] is not used and does not need initialization + } + else if (! epcoeff) + { + for (int i=0; i<16; i++) + { + int cstatetmp = UNK; + if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) + cstatetmp = NEW|UNK; + cstate[i] = cstatetmp; + bstatetmp |= cstatetmp; + } + } + else + { + for (int i=0; i<16; i++) + { + int cstatetmp = UNK; + if (epcoeff[i]) + cstatetmp = ACTIVE; + else if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) + cstatetmp = NEW|UNK; + cstate[i] = cstatetmp; + bstatetmp |= cstatetmp; + } + } + bucketstate[buckno] = bstatetmp; + bbstate |= bstatetmp; + } + } + else + { + // Band zero ( fbucket==0 implies band==zero and nbucket==1 ) + const short *pcoeff = blk.data(0, &map); + const short *epcoeff = eblk.data(0, &emap); + char *cstate = coeffstate; + for (int i=0; i<16; i++) + { + int thres = quant_lo[i]; + int cstatetmp = cstate[i]; + if (cstatetmp != ZERO) + { + cstatetmp = UNK; + if (epcoeff[i]) + cstatetmp = ACTIVE; + else if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) + cstatetmp = NEW|UNK; + } + cstate[i] = cstatetmp; + bbstate |= cstatetmp; + } + bucketstate[0] = bbstate; + } + return bbstate; +} + +// encode_buckets +// -- code a sequence of buckets in a given block +void +IW44Image::Codec::Encode::encode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, IW44Image::Block &eblk, + int fbucket, int nbucket) +{ + // compute state of all coefficients in all buckets + int bbstate = encode_prepare(band, fbucket, nbucket, blk, eblk); + + // code root bit + if ((nbucket<16) || (bbstate&ACTIVE)) + { + bbstate |= NEW; + } + else if (bbstate & UNK) + { + zp.encoder( (bbstate&NEW) ? 1 : 0 , ctxRoot); +#ifdef TRACE + DjVuPrintMessage("bbstate[bit=%d,band=%d] = %d\n", bit, band, bbstate); +#endif + } + + // code bucket bits + if (bbstate & NEW) + for (int buckno=0; buckno<nbucket; buckno++) + { + // Code bucket bit + if (bucketstate[buckno] & UNK) + { + // Context + int ctx = 0; +#ifndef NOCTX_BUCKET_UPPER + if (band>0) + { + int k = (fbucket+buckno)<<2; + const short *b = eblk.data(k>>4); + if (b) + { + k = k & 0xf; + if (b[k]) + ctx += 1; + if (b[k+1]) + ctx += 1; + if (b[k+2]) + ctx += 1; + if (ctx<3 && b[k+3]) + ctx += 1; + } + } +#endif +#ifndef NOCTX_BUCKET_ACTIVE + if (bbstate & ACTIVE) + ctx |= 4; +#endif + // Code + zp.encoder( (bucketstate[buckno]&NEW) ? 1 : 0, ctxBucket[band][ctx] ); +#ifdef TRACE + DjVuPrintMessage(" bucketstate[bit=%d,band=%d,buck=%d] = %d\n", + bit, band, buckno, bucketstate[buckno] & ~ZERO); +#endif + } + } + + // code new active coefficient (with their sign) + if (bbstate & NEW) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & NEW) + { + int i; +#ifndef NOCTX_EXPECT + int gotcha = 0; + const int maxgotcha = 7; + for (i=0; i<16; i++) + if (cstate[i] & UNK) + gotcha += 1; +#endif + const short *pcoeff = blk.data(fbucket+buckno); + short *epcoeff = eblk.data(fbucket+buckno, &emap); + // iterate within bucket + for (i=0; i<16; i++) + { + if (cstate[i] & UNK) + { + // Prepare context + int ctx = 0; +#ifndef NOCTX_EXPECT + if (gotcha>=maxgotcha) + ctx = maxgotcha; + else + ctx = gotcha; +#endif +#ifndef NOCTX_ACTIVE + if (bucketstate[buckno] & ACTIVE) + ctx |= 8; +#endif + // Code + zp.encoder( (cstate[i]&NEW) ? 1 : 0, ctxStart[ctx] ); + if (cstate[i] & NEW) + { + // Code sign + zp.IWencoder( (pcoeff[i]<0) ? 1 : 0 ); + // Set encoder state + if (band==0) + thres = quant_lo[i]; + epcoeff[i] = thres + (thres>>1); + } +#ifndef NOCTX_EXPECT + if (cstate[i] & NEW) + gotcha = 0; + else if (gotcha > 0) + gotcha -= 1; +#endif +#ifdef TRACE + DjVuPrintMessage(" coeffstate[bit=%d,band=%d,buck=%d,c=%d] = %d\n", + bit, band, buckno, i, cstate[i]); +#endif + } + } + } + } + + // code mantissa bits + if (bbstate & ACTIVE) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & ACTIVE) + { + const short *pcoeff = blk.data(fbucket+buckno); + short *epcoeff = eblk.data(fbucket+buckno, &emap); + for (int i=0; i<16; i++) + if (cstate[i] & ACTIVE) + { + // get coefficient + int coeff = pcoeff[i]; + int ecoeff = epcoeff[i]; + if (coeff < 0) + coeff = -coeff; + // get band zero thresholds + if (band == 0) + thres = quant_lo[i]; + // compute mantissa bit + int pix = 0; + if (coeff >= ecoeff) + pix = 1; + // encode second or lesser mantissa bit + if (ecoeff <= 3*thres) + zp.encoder(pix, ctxMant); + else + zp.IWencoder(!!pix); + // adjust epcoeff + epcoeff[i] = ecoeff - (pix ? 0 : thres) + (thres>>1); + } + } + } +} + +// IW44Image::Codec::estimate_decibel +// -- estimate encoding error (after code_slice) in decibels. +float +IW44Image::Codec::Encode::estimate_decibel(float frac) +{ + int i,j; + const float *q; + // Fill norm arrays + float norm_lo[16]; + float norm_hi[10]; + // -- lo coefficients + q = iw_norm; + for (i=j=0; i<4; j++) + norm_lo[i++] = *q++; + for (j=0; j<4; j++) + norm_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + norm_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + norm_lo[i++] = *q; + q += 1; + // -- hi coefficients + norm_hi[0] = 0; + for (j=1; j<10; j++) + norm_hi[j] = *q++; + // Initialize mse array + float *xmse; + GPBuffer<float> gxmse(xmse,map.nb); + // Compute mse in each block + for (int blockno=0; blockno<map.nb; blockno++) + { + float mse = 0; + // Iterate over bands + for (int bandno=0; bandno<10; bandno++) + { + int fbucket = bandbuckets[bandno].start; + int nbucket = bandbuckets[bandno].size; + IW44Image::Block &blk = map.blocks[blockno]; + IW44Image::Block &eblk = emap.blocks[blockno]; + float norm = norm_hi[bandno]; + for (int buckno=0; buckno<nbucket; buckno++) + { + const short *pcoeff = blk.data(fbucket+buckno); + const short *epcoeff = eblk.data(fbucket+buckno); + if (pcoeff) + { + if (epcoeff) + { + for (i=0; i<16; i++) + { + if (bandno == 0) + norm = norm_lo[i]; + float delta = (float)(pcoeff[i]<0 ? -pcoeff[i] : pcoeff[i]); + delta = delta - epcoeff[i]; + mse = mse + norm * delta * delta; + } + } + else + { + for (i=0; i<16; i++) + { + if (bandno == 0) + norm = norm_lo[i]; + float delta = (float)(pcoeff[i]); + mse = mse + norm * delta * delta; + } + } + } + } + } + xmse[blockno] = mse / 1024; + } + // Compute partition point + int n = 0; + int m = map.nb - 1; + int p = (int)floor(m*(1.0-frac)+0.5); + p = (p>m ? m : (p<0 ? 0 : p)); + float pivot = 0; + // Partition array + while (n < p) + { + int l = n; + int h = m; + if (xmse[l] > xmse[h]) { float tmp=xmse[l]; xmse[l]=xmse[h]; xmse[h]=tmp; } + pivot = xmse[(l+h)/2]; + if (pivot < xmse[l]) { float tmp=pivot; pivot=xmse[l]; xmse[l]=tmp; } + if (pivot > xmse[h]) { float tmp=pivot; pivot=xmse[h]; xmse[h]=tmp; } + while (l < h) + { + if (xmse[l] > xmse[h]) { float tmp=xmse[l]; xmse[l]=xmse[h]; xmse[h]=tmp; } + while (xmse[l]<pivot || (xmse[l]==pivot && l<h)) l++; + while (xmse[h]>pivot) h--; + } + if (p>=l) + n = l; + else + m = l-1; + } + // Compute average mse + float mse = 0; + for (i=p; i<map.nb; i++) + mse = mse + xmse[i]; + mse = mse / (map.nb - p); + // Return + float factor = 255 << iw_shift; + float decibel = (float)(10.0 * log ( factor * factor / mse ) / 2.302585125); + return decibel; +} + + + + +////////////////////////////////////////////////////// +// IW44IMAGE ENCODING ROUTINES +////////////////////////////////////////////////////// + + +void +IW44Image::PrimaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(serial); + gbs->write8(slices); +} + +void +IW44Image::SecondaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(major); + gbs->write8(minor); +} + +void +IW44Image::TertiaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(xhi); + gbs->write8(xlo); + gbs->write8(yhi); + gbs->write8(ylo); + gbs->write8(crcbdelay); +} + + + +GP<IW44Image> +IW44Image::create_encode(const ImageType itype) +{ + switch(itype) + { + case COLOR: + return new IWPixmap::Encode(); + case GRAY: + return new IWBitmap::Encode(); + default: + return 0; + } +} + +GP<IW44Image> +IW44Image::create_encode(const GBitmap &bm, const GP<GBitmap> mask) +{ + IWBitmap::Encode *bit=new IWBitmap::Encode(); + GP<IW44Image> retval=bit; + bit->init(bm, mask); + return retval; +} + + +IWBitmap::Encode::Encode(void) +: IWBitmap(), ycodec_enc(0) +{} + +IWBitmap::Encode::~Encode() +{ + close_codec(); +} + +void +IWBitmap::Encode::init(const GBitmap &bm, const GP<GBitmap> gmask) +{ + // Free + close_codec(); + delete ymap; + ymap = 0; + // Init + int i, j; + int w = bm.columns(); + int h = bm.rows(); + int g = bm.get_grays()-1; + signed char *buffer; + GPBuffer<signed char> gbuffer(buffer,w*h); + // Prepare gray level conversion table + signed char bconv[256]; + for (i=0; i<256; i++) + bconv[i] = max(0,min(255,i*255/g)) - 128; + // Perform decomposition + // Prepare mask information + const signed char *msk8 = 0; + int mskrowsize = 0; + GBitmap *mask=gmask; + if (gmask) + { + msk8 = (const signed char*)((*mask)[0]); + mskrowsize = mask->rowsize(); + } + // Prepare a buffer of signed bytes + for (i=0; i<h; i++) + { + signed char *bufrow = buffer + i*w; + const unsigned char *bmrow = bm[i]; + for (j=0; j<w; j++) + bufrow[j] = bconv[bmrow[j]]; + } + // Create map + Map::Encode *eymap=new Map::Encode(w,h); + ymap = eymap; + eymap->create(buffer, w, msk8, mskrowsize); +} + +void +IWBitmap::Encode::close_codec(void) +{ + delete ycodec_enc; + ycodec_enc = 0; + IWBitmap::close_codec(); +} + +int +IWBitmap::Encode::encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parm) +{ + // Check + if (parm.slices==0 && parm.bytes==0 && parm.decibels==0) + G_THROW( ERR_MSG("IW44Image.need_stop") ); + if (! ymap) + G_THROW( ERR_MSG("IW44Image.empty_object") ); + // Open codec + if (!ycodec_enc) + { + cslice = cserial = cbytes = 0; + ycodec_enc = new Codec::Encode(*ymap); + } + // Adjust cbytes + cbytes += sizeof(struct IW44Image::PrimaryHeader); + if (cserial == 0) + cbytes += sizeof(struct IW44Image::SecondaryHeader) + sizeof(struct IW44Image::TertiaryHeader); + // Prepare zcoded slices + int flag = 1; + int nslices = 0; + GP<ByteStream> gmbs=ByteStream::create(); + ByteStream &mbs=*gmbs; + DJVU_PROGRESS_TASK(chunk,"encode chunk",parm.slices-cslice); + { + float estdb = -1.0; + GP<ZPCodec> gzp=ZPCodec::create(gmbs, true, true); + ZPCodec &zp=*gzp; + while (flag) + { + if (parm.decibels>0 && estdb>=parm.decibels) + break; + if (parm.bytes>0 && mbs.tell()+cbytes>=parm.bytes) + break; + if (parm.slices>0 && nslices+cslice>=parm.slices) + break; + DJVU_PROGRESS_RUN(chunk, (1+nslices-cslice)|0xf); + flag = ycodec_enc->code_slice(zp); + if (flag && parm.decibels>0.0) + if (ycodec_enc->curband==0 || estdb>=parm.decibels-DECIBEL_PRUNE) + estdb = ycodec_enc->estimate_decibel(db_frac); + nslices++; + } + } + // Write primary header + struct IW44Image::PrimaryHeader primary; + primary.serial = cserial; + primary.slices = nslices; + primary.encode(gbs); + // Write auxilliary headers + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.major = IWCODEC_MAJOR + 0x80; + secondary.minor = IWCODEC_MINOR; + secondary.encode(gbs); + struct IW44Image::TertiaryHeader tertiary; + tertiary.xhi = (ymap->iw >> 8) & 0xff; + tertiary.xlo = (ymap->iw >> 0) & 0xff; + tertiary.yhi = (ymap->ih >> 8) & 0xff; + tertiary.ylo = (ymap->ih >> 0) & 0xff; + tertiary.crcbdelay = 0; + tertiary.encode(gbs); + } + // Write slices + mbs.seek(0); + gbs->copy(mbs); + // Return + cbytes += mbs.tell(); + cslice += nslices; + cserial += 1; + return flag; +} + +void +IWBitmap::Encode::encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms) +{ + if (ycodec_enc) + G_THROW( ERR_MSG("IW44Image.left_open1") ); + int flag = 1; + iff.put_chunk("FORM:BM44", 1); + DJVU_PROGRESS_TASK(iff,"encode iff chunk",nchunks); + for (int i=0; flag && i<nchunks; i++) + { + DJVU_PROGRESS_RUN(iff,i+1); + iff.put_chunk("BM44"); + flag = encode_chunk(iff.get_bytestream(),parms[i]); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +GP<IW44Image> +IW44Image::create_encode( + const GPixmap &pm, const GP<GBitmap> gmask, CRCBMode crcbmode) +{ + IWPixmap::Encode *pix=new IWPixmap::Encode(); + GP<IW44Image> retval=pix; + pix->init(pm, gmask,(IWPixmap::Encode::CRCBMode)crcbmode); + return retval; +} + +IWPixmap::Encode::Encode(void) +: IWPixmap(), ycodec_enc(0), cbcodec_enc(0), crcodec_enc(0) +{} + +IWPixmap::Encode::~Encode() +{ + close_codec(); +} + +void +IWPixmap::Encode::init(const GPixmap &pm, const GP<GBitmap> gmask, CRCBMode crcbmode) +{ + /* Free */ + close_codec(); + delete ymap; + delete cbmap; + delete crmap; + ymap = cbmap = crmap = 0; + /* Create */ + int w = pm.columns(); + int h = pm.rows(); + signed char *buffer; + GPBuffer<signed char> gbuffer(buffer,w*h); + // Create maps + Map::Encode *eymap = new Map::Encode(w,h); + ymap = eymap; + // Handle CRCB mode + switch (crcbmode) + { + case CRCBnone: crcb_half=1; crcb_delay=-1; break; + case CRCBhalf: crcb_half=1; crcb_delay=10; break; + case CRCBnormal: crcb_half=0; crcb_delay=10; break; + case CRCBfull: crcb_half=0; crcb_delay= 0; break; + } + // Prepare mask information + const signed char *msk8 = 0; + int mskrowsize = 0; + GBitmap *mask=gmask; + if (mask) + { + msk8 = (signed char const *)((*mask)[0]); + mskrowsize = mask->rowsize(); + } + // Fill buffer with luminance information + DJVU_PROGRESS_TASK(create,"initialize pixmap",3); + DJVU_PROGRESS_RUN(create,(crcb_delay>=0 ? 1 : 3)); + Transform::Encode::RGB_to_Y(pm[0], w, h, pm.rowsize(), buffer, w); + if (crcb_delay < 0) + { + // Stupid inversion for gray images + signed char *e = buffer + w*h; + for (signed char *b=buffer; b<e; b++) + *b = 255 - *b; + } + // Create YMAP + eymap->create(buffer, w, msk8, mskrowsize); + // Create chrominance maps + if (crcb_delay >= 0) + { + Map::Encode *ecbmap = new Map::Encode(w,h); + cbmap = ecbmap; + Map::Encode *ecrmap = new Map::Encode(w,h); + crmap = ecrmap; + // Process CB information + DJVU_PROGRESS_RUN(create,2); + Transform::Encode::RGB_to_Cb(pm[0], w, h, pm.rowsize(), buffer, w); + ecbmap->create(buffer, w, msk8, mskrowsize); + // Process CR information + DJVU_PROGRESS_RUN(create,3); + Transform::Encode::RGB_to_Cr(pm[0], w, h, pm.rowsize(), buffer, w); + ecrmap->create(buffer, w, msk8, mskrowsize); + // Perform chrominance reduction (CRCBhalf) + if (crcb_half) + { + ecbmap->slashres(2); + ecrmap->slashres(2); + } + } +} + +void +IWPixmap::Encode::encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms) +{ + if (ycodec_enc) + G_THROW( ERR_MSG("IW44Image.left_open3") ); + int flag = 1; + iff.put_chunk("FORM:PM44", 1); + DJVU_PROGRESS_TASK(iff,"encode pixmap chunk", nchunks); + for (int i=0; flag && i<nchunks; i++) + { + DJVU_PROGRESS_RUN(iff,i+1); + iff.put_chunk("PM44"); + flag = encode_chunk(iff.get_bytestream(), parms[i]); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +void +IWPixmap::Encode::close_codec(void) +{ + delete ycodec_enc; + delete cbcodec_enc; + delete crcodec_enc; + ycodec_enc = crcodec_enc = cbcodec_enc = 0; + IWPixmap::close_codec(); +} + +int +IWPixmap::Encode::encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parm) +{ + // Check + if (parm.slices==0 && parm.bytes==0 && parm.decibels==0) + G_THROW( ERR_MSG("IW44Image.need_stop2") ); + if (!ymap) + G_THROW( ERR_MSG("IW44Image.empty_object2") ); + // Open + if (!ycodec_enc) + { + cslice = cserial = cbytes = 0; + ycodec_enc = new Codec::Encode(*ymap); + if (crmap && cbmap) + { + cbcodec_enc = new Codec::Encode(*cbmap); + crcodec_enc = new Codec::Encode(*crmap); + } + } + + // Adjust cbytes + cbytes += sizeof(struct IW44Image::PrimaryHeader); + if (cserial == 0) + cbytes += sizeof(struct IW44Image::SecondaryHeader) + sizeof(struct IW44Image::TertiaryHeader); + // Prepare zcodec slices + int flag = 1; + int nslices = 0; + GP<ByteStream> gmbs=ByteStream::create(); + ByteStream &mbs=*gmbs; + DJVU_PROGRESS_TASK(chunk, "encode pixmap chunk", parm.slices-cslice); + { + float estdb = -1.0; + GP<ZPCodec> gzp=ZPCodec::create(gmbs, true, true); + ZPCodec &zp=*gzp; + while (flag) + { + if (parm.decibels>0 && estdb>=parm.decibels) + break; + if (parm.bytes>0 && mbs.tell()+cbytes>=parm.bytes) + break; + if (parm.slices>0 && nslices+cslice>=parm.slices) + break; + DJVU_PROGRESS_RUN(chunk,(1+nslices-cslice)|0xf); + flag = ycodec_enc->code_slice(zp); + if (flag && parm.decibels>0) + if (ycodec_enc->curband==0 || estdb>=parm.decibels-DECIBEL_PRUNE) + estdb = ycodec_enc->estimate_decibel(db_frac); + if (crcodec_enc && cbcodec_enc && cslice+nslices>=crcb_delay) + { + flag |= cbcodec_enc->code_slice(zp); + flag |= crcodec_enc->code_slice(zp); + } + nslices++; + } + } + // Write primary header + struct IW44Image::PrimaryHeader primary; + primary.serial = cserial; + primary.slices = nslices; + primary.encode(gbs); + // Write secondary header + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.major = IWCODEC_MAJOR; + secondary.minor = IWCODEC_MINOR; + if (! (crmap && cbmap)) + secondary.major |= 0x80; + secondary.encode(gbs); + struct IW44Image::TertiaryHeader tertiary; + tertiary.xhi = (ymap->iw >> 8) & 0xff; + tertiary.xlo = (ymap->iw >> 0) & 0xff; + tertiary.yhi = (ymap->ih >> 8) & 0xff; + tertiary.ylo = (ymap->ih >> 0) & 0xff; + tertiary.crcbdelay = (crcb_half ? 0x00 : 0x80); + tertiary.crcbdelay |= (crcb_delay>=0 ? crcb_delay : 0x00); + tertiary.encode(gbs); + } + // Write slices + mbs.seek(0); + gbs->copy(mbs); + // Return + cbytes += mbs.tell(); + cslice += nslices; + cserial += 1; + return flag; +} + +// code_slice +// -- read/write a slice of datafile + +int +IW44Image::Codec::Encode::code_slice(ZPCodec &zp) +{ + // Check that code_slice can still run + if (curbit < 0) + return 0; + // Perform coding + if (! is_null_slice(curbit, curband)) + { + for (int blockno=0; blockno<map.nb; blockno++) + { + const int fbucket = bandbuckets[curband].start; + const int nbucket = bandbuckets[curband].size; + encode_buckets(zp, curbit, curband, + map.blocks[blockno], emap.blocks[blockno], + fbucket, nbucket); + } + } + return finish_code_slice(zp); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif // NEED_DECODER_ONLY + diff --git a/kviewshell/plugins/djvu/libdjvu/IW44Image.cpp b/kviewshell/plugins/djvu/libdjvu/IW44Image.cpp new file mode 100644 index 00000000..2cadf4f9 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IW44Image.cpp @@ -0,0 +1,1935 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IW44Image.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 08/1998 + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split this file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#define IW44IMAGE_IMPLIMENTATION /* */ +// -------------------^ not my spelling mistake (Leon Bottou) + +#include "IW44Image.h" +#include "ZPCodec.h" +#include "GBitmap.h" +#include "GPixmap.h" +#include "IFFByteStream.h" +#include "GRect.h" + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "MMX.h" +#undef IWTRANSFORM_TIMER +#ifdef IWTRANSFORM_TIMER +#include "GOS.h" +#endif + +#include <assert.h> +#include <string.h> +#include <math.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#define IWALLOCSIZE 4080 +#define IWCODEC_MAJOR 1 +#define IWCODEC_MINOR 2 +#define DECIBEL_PRUNE 5.0 + + +////////////////////////////////////////////////////// +// WAVELET DECOMPOSITION CONSTANTS +////////////////////////////////////////////////////// + +// Parameters for IW44 wavelet. +// - iw_quant: quantization for all 16 sub-bands +// - iw_norm: norm of all wavelets (for db estimation) +// - iw_border: pixel border required to run filters +// - iw_shift: scale applied before decomposition + + +static const int iw_quant[16] = { + 0x004000, + 0x008000, 0x008000, 0x010000, + 0x010000, 0x010000, 0x020000, + 0x020000, 0x020000, 0x040000, + 0x040000, 0x040000, 0x080000, + 0x040000, 0x040000, 0x080000 +}; + +static const float iw_norm[16] = { + 2.627989e+03F, + 1.832893e+02F, 1.832959e+02F, 5.114690e+01F, + 4.583344e+01F, 4.583462e+01F, 1.279225e+01F, + 1.149671e+01F, 1.149712e+01F, 3.218888e+00F, + 2.999281e+00F, 2.999476e+00F, 8.733161e-01F, + 1.074451e+00F, 1.074511e+00F, 4.289318e-01F +}; + +static const int iw_border = 3; +static const int iw_shift = 6; +static const int iw_round = (1<<(iw_shift-1)); + +class IW44Image::Codec::Decode : public IW44Image::Codec +{ +public: + // Construction + Decode(IW44Image::Map &map) : Codec(map) {} + // Coding + virtual int code_slice(ZPCodec &zp); +}; + +////////////////////////////////////////////////////// +// MMX IMPLEMENTATION HELPERS +////////////////////////////////////////////////////// + + +// Note: +// MMX implementation for vertical transforms only. +// Speedup is basically related to faster memory transfer +// The IW44 transform is not CPU bound, it is memory bound. + +#ifdef MMX + +static const short w9[] = {9,9,9,9}; +static const short w1[] = {1,1,1,1}; +static const int d8[] = {8,8}; +static const int d16[] = {16,16}; + +static void +mmx_bv_1 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+16)>>5); + q ++; + } + while (q+3 < e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d16,mm0); + MMXar( paddd, d16,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 5,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 5,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( psubw, mm0,mm7); // MM7=[ p3-x3, p2-x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + + +static void +mmx_bv_2 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+8)>>4); + q ++; + } + while (q+3 < e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d8,mm0); + MMXar( paddd, d8,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 4,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 4,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( paddw, mm0,mm7); // MM7=[ p3+x3, p2+x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} +#endif /* MMX */ + +static void +filter_bv(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale*rowsize; + int s3 = s+s+s; + h = ((h-1)/scale)+1; + while (y-3 < h) + { + // 1-Lifting + { + short *q = p; + short *e = q+w; + if (y>=3 && y+3<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_bv_1(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + } + } + else if (y<h) + { + // Special cases + short *q1 = (y+1<h ? q+s : 0); + short *q3 = (y+3<h ? q+s3 : 0); + if (y>=3) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (int)q[-s3] + (q3 ? (int)(*q3) : 0); + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else if (y>=1) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else + { + while (q<e) + { + int a = (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + } + } + // 2-Interpolation + { + short *q = p-s3; + short *e = q+w; + if (y>=6 && y<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_bv_2(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+8)>>4); + q += scale; + } + } + else if (y>=3) + { + // Special cases + short *q1 = (y-2<h ? q+s : q-s); + while (q<e) + { + int a = (int)q[-s] + (int)(*q1); + *q += ((a+1)>>1); + q += scale; + q1 += scale; + } + } + } + y += 2; + p += s+s; + } +} + +static void +filter_bh(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale; + int s3 = s+s+s; + rowsize *= scale; + while (y<h) + { + short *q = p; + short *e = p+w; + int a0=0, a1=0, a2=0, a3=0; + int b0=0, b1=0, b2=0, b3=0; + if (q<e) + { + // Special case: x=0 + if (q+s < e) + a2 = q[s]; + if (q+s3 < e) + a3 = q[s3]; + b2 = b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q += s+s; + } + if (q<e) + { + // Special case: x=2 + a0 = a1; + a1 = a2; + a2 = a3; + if (q+s3 < e) + a3 = q[s3]; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q += s+s; + } + if (q<e) + { + // Special case: x=4 + b1 = b2; + b2 = b3; + a0 = a1; + a1 = a2; + a2 = a3; + if (q+s3 < e) + a3 = q[s3]; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q[-s3] = q[-s3] + ((b1+b2+1)>>1); + q += s+s; + } + while (q+s3 < e) + { + // Generic case + a0=a1; + a1=a2; + a2=a3; + a3=q[s3]; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+8) >> 4); + q += s+s; + } + while (q < e) + { + // Special case: w-3 <= x < w + a0=a1; + a1=a2; + a2=a3; + a3=0; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+8) >> 4); + q += s+s; + } + while (q-s3 < e) + { + // Special case w <= x < w+3 + b0=b1; + b1=b2; + b2=b3; + if (q-s3 >= p) + q[-s3] = q[-s3] + ((b1+b2+1)>>1); + q += s+s; + } + y += scale; + p += rowsize; + } +} + + +////////////////////////////////////////////////////// +// REPRESENTATION OF WAVELET DECOMPOSED IMAGES +////////////////////////////////////////////////////// + + + +//--------------------------------------------------------------- +// Zig zag location in a 1024 liftblock. +// These numbers have been generated with the following program: +// +// int x=0, y=0; +// for (int i=0; i<5; i++) { +// x = (x<<1) | (n&1); n >>= 1; +// y = (y<<1) | (n&1); n >>= 1; +// } + + +static int zigzagloc[1024] = { + 0, 16, 512, 528, 8, 24, 520, 536, 256, 272, 768, 784, 264, 280, 776, 792, + 4, 20, 516, 532, 12, 28, 524, 540, 260, 276, 772, 788, 268, 284, 780, 796, + 128, 144, 640, 656, 136, 152, 648, 664, 384, 400, 896, 912, 392, 408, 904, 920, + 132, 148, 644, 660, 140, 156, 652, 668, 388, 404, 900, 916, 396, 412, 908, 924, + 2, 18, 514, 530, 10, 26, 522, 538, 258, 274, 770, 786, 266, 282, 778, 794, + 6, 22, 518, 534, 14, 30, 526, 542, 262, 278, 774, 790, 270, 286, 782, 798, + 130, 146, 642, 658, 138, 154, 650, 666, 386, 402, 898, 914, 394, 410, 906, 922, + 134, 150, 646, 662, 142, 158, 654, 670, 390, 406, 902, 918, 398, 414, 910, 926, + 64, 80, 576, 592, 72, 88, 584, 600, 320, 336, 832, 848, 328, 344, 840, 856, + 68, 84, 580, 596, 76, 92, 588, 604, 324, 340, 836, 852, 332, 348, 844, 860, + 192, 208, 704, 720, 200, 216, 712, 728, 448, 464, 960, 976, 456, 472, 968, 984, + 196, 212, 708, 724, 204, 220, 716, 732, 452, 468, 964, 980, 460, 476, 972, 988, + 66, 82, 578, 594, 74, 90, 586, 602, 322, 338, 834, 850, 330, 346, 842, 858, + 70, 86, 582, 598, 78, 94, 590, 606, 326, 342, 838, 854, 334, 350, 846, 862, + 194, 210, 706, 722, 202, 218, 714, 730, 450, 466, 962, 978, 458, 474, 970, 986, + 198, 214, 710, 726, 206, 222, 718, 734, 454, 470, 966, 982, 462, 478, 974, 990, // 255 + 1, 17, 513, 529, 9, 25, 521, 537, 257, 273, 769, 785, 265, 281, 777, 793, + 5, 21, 517, 533, 13, 29, 525, 541, 261, 277, 773, 789, 269, 285, 781, 797, + 129, 145, 641, 657, 137, 153, 649, 665, 385, 401, 897, 913, 393, 409, 905, 921, + 133, 149, 645, 661, 141, 157, 653, 669, 389, 405, 901, 917, 397, 413, 909, 925, + 3, 19, 515, 531, 11, 27, 523, 539, 259, 275, 771, 787, 267, 283, 779, 795, + 7, 23, 519, 535, 15, 31, 527, 543, 263, 279, 775, 791, 271, 287, 783, 799, + 131, 147, 643, 659, 139, 155, 651, 667, 387, 403, 899, 915, 395, 411, 907, 923, + 135, 151, 647, 663, 143, 159, 655, 671, 391, 407, 903, 919, 399, 415, 911, 927, + 65, 81, 577, 593, 73, 89, 585, 601, 321, 337, 833, 849, 329, 345, 841, 857, + 69, 85, 581, 597, 77, 93, 589, 605, 325, 341, 837, 853, 333, 349, 845, 861, + 193, 209, 705, 721, 201, 217, 713, 729, 449, 465, 961, 977, 457, 473, 969, 985, + 197, 213, 709, 725, 205, 221, 717, 733, 453, 469, 965, 981, 461, 477, 973, 989, + 67, 83, 579, 595, 75, 91, 587, 603, 323, 339, 835, 851, 331, 347, 843, 859, + 71, 87, 583, 599, 79, 95, 591, 607, 327, 343, 839, 855, 335, 351, 847, 863, + 195, 211, 707, 723, 203, 219, 715, 731, 451, 467, 963, 979, 459, 475, 971, 987, + 199, 215, 711, 727, 207, 223, 719, 735, 455, 471, 967, 983, 463, 479, 975, 991, // 511 + 32, 48, 544, 560, 40, 56, 552, 568, 288, 304, 800, 816, 296, 312, 808, 824, + 36, 52, 548, 564, 44, 60, 556, 572, 292, 308, 804, 820, 300, 316, 812, 828, + 160, 176, 672, 688, 168, 184, 680, 696, 416, 432, 928, 944, 424, 440, 936, 952, + 164, 180, 676, 692, 172, 188, 684, 700, 420, 436, 932, 948, 428, 444, 940, 956, + 34, 50, 546, 562, 42, 58, 554, 570, 290, 306, 802, 818, 298, 314, 810, 826, + 38, 54, 550, 566, 46, 62, 558, 574, 294, 310, 806, 822, 302, 318, 814, 830, + 162, 178, 674, 690, 170, 186, 682, 698, 418, 434, 930, 946, 426, 442, 938, 954, + 166, 182, 678, 694, 174, 190, 686, 702, 422, 438, 934, 950, 430, 446, 942, 958, + 96, 112, 608, 624, 104, 120, 616, 632, 352, 368, 864, 880, 360, 376, 872, 888, + 100, 116, 612, 628, 108, 124, 620, 636, 356, 372, 868, 884, 364, 380, 876, 892, + 224, 240, 736, 752, 232, 248, 744, 760, 480, 496, 992,1008, 488, 504,1000,1016, + 228, 244, 740, 756, 236, 252, 748, 764, 484, 500, 996,1012, 492, 508,1004,1020, + 98, 114, 610, 626, 106, 122, 618, 634, 354, 370, 866, 882, 362, 378, 874, 890, + 102, 118, 614, 630, 110, 126, 622, 638, 358, 374, 870, 886, 366, 382, 878, 894, + 226, 242, 738, 754, 234, 250, 746, 762, 482, 498, 994,1010, 490, 506,1002,1018, + 230, 246, 742, 758, 238, 254, 750, 766, 486, 502, 998,1014, 494, 510,1006,1022, // 767 + 33, 49, 545, 561, 41, 57, 553, 569, 289, 305, 801, 817, 297, 313, 809, 825, + 37, 53, 549, 565, 45, 61, 557, 573, 293, 309, 805, 821, 301, 317, 813, 829, + 161, 177, 673, 689, 169, 185, 681, 697, 417, 433, 929, 945, 425, 441, 937, 953, + 165, 181, 677, 693, 173, 189, 685, 701, 421, 437, 933, 949, 429, 445, 941, 957, + 35, 51, 547, 563, 43, 59, 555, 571, 291, 307, 803, 819, 299, 315, 811, 827, + 39, 55, 551, 567, 47, 63, 559, 575, 295, 311, 807, 823, 303, 319, 815, 831, + 163, 179, 675, 691, 171, 187, 683, 699, 419, 435, 931, 947, 427, 443, 939, 955, + 167, 183, 679, 695, 175, 191, 687, 703, 423, 439, 935, 951, 431, 447, 943, 959, + 97, 113, 609, 625, 105, 121, 617, 633, 353, 369, 865, 881, 361, 377, 873, 889, + 101, 117, 613, 629, 109, 125, 621, 637, 357, 373, 869, 885, 365, 381, 877, 893, + 225, 241, 737, 753, 233, 249, 745, 761, 481, 497, 993,1009, 489, 505,1001,1017, + 229, 245, 741, 757, 237, 253, 749, 765, 485, 501, 997,1013, 493, 509,1005,1021, + 99, 115, 611, 627, 107, 123, 619, 635, 355, 371, 867, 883, 363, 379, 875, 891, + 103, 119, 615, 631, 111, 127, 623, 639, 359, 375, 871, 887, 367, 383, 879, 895, + 227, 243, 739, 755, 235, 251, 747, 763, 483, 499, 995,1011, 491, 507,1003,1019, + 231, 247, 743, 759, 239, 255, 751, 767, 487, 503, 999,1015, 495, 511,1007,1023, // 1023 +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Alloc [declaration] + +struct IW44Image::Alloc // DJVU_CLASS +{ + Alloc *next; + short data[IWALLOCSIZE]; +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Block [implementation] + + +IW44Image::Block::Block(void) +{ + pdata[0] = pdata[1] = pdata[2] = pdata[3] = 0; +} + +void +IW44Image::Block::zero(int n) +{ + if (pdata[n>>4]) + pdata[n>>4][n&15] = 0; +} + +void +IW44Image::Block::read_liftblock(const short *coeff, IW44Image::Map *map) +{ + int n=0; + for (int n1=0; n1<64; n1++) + { + short *d = data(n1,map); + for (int n2=0; n2<16; n2++,n++) + d[n2] = coeff[zigzagloc[n]]; + } +} + +void +IW44Image::Block::write_liftblock(short *coeff, int bmin, int bmax) const +{ + int n = bmin<<4; + memset(coeff, 0, 1024*sizeof(short)); + for (int n1=bmin; n1<bmax; n1++) + { + const short *d = data(n1); + if (d == 0) + n += 16; + else + for (int n2=0; n2<16; n2++,n++) + coeff[zigzagloc[n]] = d[n2]; + } +} + +//--------------------------------------------------------------- +// *** Class IW44Image::Map [implementation] + + +IW44Image::Map::Map(int w, int h) + : blocks(0), iw(w), ih(h), chain(0) +{ + bw = (w+0x20-1) & ~0x1f; + bh = (h+0x20-1) & ~0x1f; + nb = (bw * bh) / (32 * 32); + blocks = new IW44Image::Block[nb]; + top = IWALLOCSIZE; +} + +IW44Image::Map::~Map() +{ + while (chain) + { + IW44Image::Alloc *next = chain->next; + delete chain; + chain = next; + } + delete [] blocks; +} + +short * +IW44Image::Map::alloc(int n) +{ + if (top+n > IWALLOCSIZE) + { + IW44Image::Alloc *n = new IW44Image::Alloc; + n->next = chain; + chain = n; + top = 0; + } + short *ans = chain->data + top; + top += n; + memset((void*)ans, 0, sizeof(short)*n); + return ans; +} + +short ** +IW44Image::Map::allocp(int n) +{ + // Allocate enough room for pointers plus alignment + short *p = alloc( (n+1) * sizeof(short*) / sizeof(short) ); + // Align on pointer size + while ( ((long)p) & (sizeof(short*)-1) ) + p += 1; + // Cast and return + return (short**)p; +} + +int +IW44Image::Map::get_bucket_count(void) const +{ + int buckets = 0; + for (int blockno=0; blockno<nb; blockno++) + for (int buckno=0; buckno<64; buckno++) + if (blocks[blockno].data(buckno)) + buckets += 1; + return buckets; +} + +unsigned int +IW44Image::Map::get_memory_usage(void) const +{ + unsigned int usage = sizeof(Map); + usage += sizeof(IW44Image::Block) * nb; + for (IW44Image::Alloc *n = chain; n; n=n->next) + usage += sizeof(IW44Image::Alloc); + return usage; +} + + + + +void +IW44Image::Map::image(signed char *img8, int rowsize, int pixsep, int fast) +{ + // Allocate reconstruction buffer + short *data16; + GPBuffer<short> gdata16(data16,bw*bh); + // Copy coefficients + int i; + short *p = data16; + const IW44Image::Block *block = blocks; + for (i=0; i<bh; i+=32) + { + for (int j=0; j<bw; j+=32) + { + short liftblock[1024]; + // transfer into IW44Image::Block (apply zigzag and scaling) + block->write_liftblock(liftblock); + block++; + // transfer into coefficient matrix at (p+j) + short *pp = p + j; + short *pl = liftblock; + for (int ii=0; ii<32; ii++, pp+=bw,pl+=32) + memcpy((void*)pp, (void*)pl, 32*sizeof(short)); + } + // next row of blocks + p += 32*bw; + } + // Reconstruction + if (fast) + { + IW44Image::Transform::Decode::backward(data16, iw, ih, bw, 32, 2); + p = data16; + for (i=0; i<bh; i+=2,p+=bw) + for (int jj=0; jj<bw; jj+=2,p+=2) + p[bw] = p[bw+1] = p[1] = p[0]; + } + else + { + IW44Image::Transform::Decode::backward(data16, iw, ih, bw, 32, 1); + } + // Copy result into image + p = data16; + signed char *row = img8; + for (i=0; i<ih; i++) + { + signed char *pix = row; + for (int j=0; j<iw; j+=1,pix+=pixsep) + { + int x = (p[j] + iw_round) >> iw_shift; + if (x < -128) + x = -128; + else if (x > 127) + x = 127; + *pix = x; + } + row += rowsize; + p += bw; + } +} + +void +IW44Image::Map::image(int subsample, const GRect &rect, + signed char *img8, int rowsize, int pixsep, int fast) +{ + int i; + // Compute number of decomposition levels + int nlevel = 0; + while (nlevel<5 && (32>>nlevel)>subsample) + nlevel += 1; + int boxsize = 1<<nlevel; + // Parameter check + if (subsample!=(32>>nlevel)) + G_THROW( ERR_MSG("IW44Image.sample_factor") ); + if (rect.isempty()) + G_THROW( ERR_MSG("IW44Image.empty_rect") ); + GRect irect(0,0,(iw+subsample-1)/subsample,(ih+subsample-1)/subsample); + if (rect.xmin<0 || rect.ymin<0 || rect.xmax>irect.xmax || rect.ymax>irect.ymax) + G_THROW( ERR_MSG("IW44Image.bad_rect") ); + // Multiresolution rectangles + // -- needed[i] tells which coeffs are required for the next step + // -- recomp[i] tells which coeffs need to be computed at this level + GRect needed[8]; + GRect recomp[8]; + int r = 1; + needed[nlevel] = rect; + recomp[nlevel] = rect; + for (i=nlevel-1; i>=0; i--) + { + needed[i] = recomp[i+1]; + needed[i].inflate(iw_border*r, iw_border*r); + needed[i].intersect(needed[i], irect); + r += r; + recomp[i].xmin = (needed[i].xmin + r-1) & ~(r-1); + recomp[i].xmax = (needed[i].xmax) & ~(r-1); + recomp[i].ymin = (needed[i].ymin + r-1) & ~(r-1); + recomp[i].ymax = (needed[i].ymax) & ~(r-1); + } + // Working rectangle + // -- a rectangle large enough to hold all the data + GRect work; + work.xmin = (needed[0].xmin) & ~(boxsize-1); + work.ymin = (needed[0].ymin) & ~(boxsize-1); + work.xmax = ((needed[0].xmax-1) & ~(boxsize-1) ) + boxsize; + work.ymax = ((needed[0].ymax-1) & ~(boxsize-1) ) + boxsize; + // -- allocate work buffer + int dataw = work.xmax - work.xmin; // Note: cannot use inline width() or height() + int datah = work.ymax - work.ymin; // because Intel C++ compiler optimizes it wrong ! + short *data; + GPBuffer<short> gdata(data,dataw*datah); + // Fill working rectangle + // -- loop over liftblocks rows + short *ldata = data; + int blkw = (bw>>5); + const IW44Image::Block *lblock = blocks + (work.ymin>>nlevel)*blkw + (work.xmin>>nlevel); + for (int by=work.ymin; by<work.ymax; by+=boxsize) + { + // -- loop over liftblocks in row + const IW44Image::Block *block = lblock; + short *rdata = ldata; + for (int bx=work.xmin; bx<work.xmax; bx+=boxsize) + { + // -- decide how much to load + int mlevel = nlevel; + if (nlevel>2) + if (bx+31<needed[2].xmin || bx>needed[2].xmax || + by+31<needed[2].ymin || by>needed[2].ymax ) + mlevel = 2; + int bmax = ((1<<(mlevel+mlevel))+15)>>4; + int ppinc = (1<<(nlevel-mlevel)); + int ppmod1 = (dataw<<(nlevel-mlevel)); + int ttmod0 = (32 >> mlevel); + int ttmod1 = (ttmod0 << 5); + // -- get current block + short liftblock[1024]; + block->write_liftblock(liftblock, 0, bmax ); + // -- copy liftblock into image + short *tt = liftblock; + short *pp = rdata; + for (int ii=0; ii<boxsize; ii+=ppinc,pp+=ppmod1,tt+=ttmod1-32) + for (int jj=0; jj<boxsize; jj+=ppinc,tt+=ttmod0) + pp[jj] = *tt; + // -- next block in row + rdata += boxsize; + block += 1; + } + // -- next row of blocks + ldata += dataw << nlevel; + lblock += blkw; + } + // Perform reconstruction + r = boxsize; + for (i=0; i<nlevel; i++) + { + GRect comp = needed[i]; + comp.xmin = comp.xmin & ~(r-1); + comp.ymin = comp.ymin & ~(r-1); + comp.translate(-work.xmin, -work.ymin); + // Fast mode shortcuts finer resolution + if (fast && i>=4) + { + short *pp = data + comp.ymin*dataw; + for (int ii=comp.ymin; ii<comp.ymax; ii+=2, pp+=dataw+dataw) + for (int jj=comp.xmin; jj<comp.xmax; jj+=2) + pp[jj+dataw] = pp[jj+dataw+1] = pp[jj+1] = pp[jj]; + break; + } + else + { + short *pp = data + comp.ymin*dataw + comp.xmin; + IW44Image::Transform::Decode::backward(pp, comp.width(), comp.height(), dataw, r, r>>1); + } + r = r>>1; + } + // Copy result into image + GRect nrect = rect; + nrect.translate(-work.xmin, -work.ymin); + short *p = data + nrect.ymin*dataw; + signed char *row = img8; + for (i=nrect.ymin; i<nrect.ymax; i++) + { + int j; + signed char *pix = row; + for (j=nrect.xmin; j<nrect.xmax; j+=1,pix+=pixsep) + { + int x = (p[j] + iw_round) >> iw_shift; + if (x < -128) + x = -128; + else if (x > 127) + x = 127; + *pix = x; + } + row += rowsize; + p += dataw; + } +} + + + + +////////////////////////////////////////////////////// +// ENCODING/DECODING WAVELET COEFFICIENTS +// USING HIERARCHICAL SET DIFFERENCE +////////////////////////////////////////////////////// + + +//----------------------------------------------- +// Class IW44Image::Codec [implementation] +// Maintains information shared while encoding or decoding + + +// Constant + +static const struct { int start; int size; } +bandbuckets[] = +{ + // Code first bucket and number of buckets in each band + { 0, 1 }, // -- band zero contains all lores info + { 1, 1 }, { 2, 1 }, { 3, 1 }, + { 4, 4 }, { 8, 4 }, { 12,4 }, + { 16,16 }, { 32,16 }, { 48,16 }, +}; + + +// IW44Image::Codec constructor + +IW44Image::Codec::Codec(IW44Image::Map &xmap) + : map(xmap), + curband(0), + curbit(1) +{ + // Initialize quantification + int j; + int i = 0; + const int *q = iw_quant; + // -- lo coefficients + for (j=0; i<4; j++) + quant_lo[i++] = *q++; + for (j=0; j<4; j++) + quant_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + quant_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + quant_lo[i++] = *q; + q += 1; + // -- hi coefficients + quant_hi[0] = 0; + for (j=1; j<10; j++) + quant_hi[j] = *q++; + // Initialize coding contexts + memset((void*)ctxStart, 0, sizeof(ctxStart)); + memset((void*)ctxBucket, 0, sizeof(ctxBucket)); + ctxMant = 0; + ctxRoot = 0; +} + + +// IW44Image::Codec destructor + +IW44Image::Codec::~Codec() {} + +// is_null_slice +// -- check if data can be produced for this band/mask +// -- also fills the sure_zero array + +int +IW44Image::Codec::is_null_slice(int bit, int band) +{ + if (band == 0) + { + int is_null = 1; + for (int i=0; i<16; i++) + { + int threshold = quant_lo[i]; + coeffstate[i] = ZERO; + if (threshold>0 && threshold<0x8000) + { + coeffstate[i] = UNK; + is_null = 0; + } + } + return is_null; + } + else + { + int threshold = quant_hi[band]; + return (! (threshold>0 && threshold<0x8000)); + } +} + + +// code_slice +// -- read/write a slice of datafile + +int +IW44Image::Codec::Decode::code_slice(ZPCodec &zp) +{ + // Check that code_slice can still run + if (curbit < 0) + return 0; + // Perform coding + if (! is_null_slice(curbit, curband)) + { + for (int blockno=0; blockno<map.nb; blockno++) + { + int fbucket = bandbuckets[curband].start; + int nbucket = bandbuckets[curband].size; + decode_buckets(zp, curbit, curband, + map.blocks[blockno], + fbucket, nbucket); + } + } + return finish_code_slice(zp); +} + +// code_slice +// -- read/write a slice of datafile + +int +IW44Image::Codec::finish_code_slice(ZPCodec &zp) +{ + // Reduce quantization threshold + quant_hi[curband] = quant_hi[curband] >> 1; + if (curband == 0) + for (int i=0; i<16; i++) + quant_lo[i] = quant_lo[i] >> 1; + // Proceed to the next slice + if (++curband >= (int)(sizeof(bandbuckets)/sizeof(bandbuckets[0]))) + { + curband = 0; + curbit += 1; + if (quant_hi[(sizeof(bandbuckets)/sizeof(bandbuckets[0]))-1] == 0) + { + // All quantization thresholds are null + curbit = -1; + return 0; + } + } + return 1; +} + +// decode_prepare +// -- prepare the states before decoding buckets + +int +IW44Image::Codec::decode_prepare(int fbucket, int nbucket, IW44Image::Block &blk) +{ + int bbstate = 0; + char *cstate = coeffstate; + if (fbucket) + { + // Band other than zero + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + { + int bstatetmp = 0; + const short *pcoeff = blk.data(fbucket+buckno); + if (! pcoeff) + { + // cstate[0..15] will be filled later + bstatetmp = UNK; + } + else + { + for (int i=0; i<16; i++) + { + int cstatetmp = UNK; + if (pcoeff[i]) + cstatetmp = ACTIVE; + cstate[i] = cstatetmp; + bstatetmp |= cstatetmp; + } + } + bucketstate[buckno] = bstatetmp; + bbstate |= bstatetmp; + } + } + else + { + // Band zero ( fbucket==0 implies band==zero and nbucket==1 ) + const short *pcoeff = blk.data(0); + if (! pcoeff) + { + // cstate[0..15] will be filled later + bbstate = UNK; + } + else + { + for (int i=0; i<16; i++) + { + int cstatetmp = cstate[i]; + if (cstatetmp != ZERO) + { + cstatetmp = UNK; + if (pcoeff[i]) + cstatetmp = ACTIVE; + } + cstate[i] = cstatetmp; + bbstate |= cstatetmp; + } + } + bucketstate[0] = bbstate; + } + return bbstate; +} + + +// decode_buckets +// -- code a sequence of buckets in a given block + +void +IW44Image::Codec::decode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, + int fbucket, int nbucket) +{ + // compute state of all coefficients in all buckets + int bbstate = decode_prepare(fbucket, nbucket, blk); + // code root bit + if ((nbucket<16) || (bbstate&ACTIVE)) + { + bbstate |= NEW; + } + else if (bbstate & UNK) + { + if (zp.decoder(ctxRoot)) + bbstate |= NEW; +#ifdef TRACE + DjVuPrintMessage("bbstate[bit=%d,band=%d] = %d\n", bit, band, bbstate); +#endif + } + + // code bucket bits + if (bbstate & NEW) + for (int buckno=0; buckno<nbucket; buckno++) + { + // Code bucket bit + if (bucketstate[buckno] & UNK) + { + // Context + int ctx = 0; +#ifndef NOCTX_BUCKET_UPPER + if (band>0) + { + int k = (fbucket+buckno)<<2; + const short *b = blk.data(k>>4); + if (b) + { + k = k & 0xf; + if (b[k]) + ctx += 1; + if (b[k+1]) + ctx += 1; + if (b[k+2]) + ctx += 1; + if (ctx<3 && b[k+3]) + ctx += 1; + } + } +#endif // NOCTX_BUCKET_UPPER +#ifndef NOCTX_BUCKET_ACTIVE + if (bbstate & ACTIVE) + ctx |= 4; +#endif + // Code + if (zp.decoder( ctxBucket[band][ctx] )) + bucketstate[buckno] |= NEW; +#ifdef TRACE + DjVuPrintMessage(" bucketstate[bit=%d,band=%d,buck=%d] = %d\n", + bit, band, buckno, bucketstate[buckno]); +#endif + } + } + + // code new active coefficient (with their sign) + if (bbstate & NEW) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & NEW) + { + int i; + short *pcoeff = (short*)blk.data(fbucket+buckno); + if (!pcoeff) + { + pcoeff = blk.data(fbucket+buckno, &map); + // time to fill cstate[0..15] + if (fbucket == 0) // band zero + { + for (i=0; i<16; i++) + if (cstate[i] != ZERO) + cstate[i] = UNK; + } + else + { + for (i=0; i<16; i++) + cstate[i] = UNK; + } + } +#ifndef NOCTX_EXPECT + int gotcha = 0; + const int maxgotcha = 7; + for (i=0; i<16; i++) + if (cstate[i] & UNK) + gotcha += 1; +#endif + for (i=0; i<16; i++) + { + if (cstate[i] & UNK) + { + // find lores threshold + if (band == 0) + thres = quant_lo[i]; + // prepare context + int ctx = 0; +#ifndef NOCTX_EXPECT + if (gotcha>=maxgotcha) + ctx = maxgotcha; + else + ctx = gotcha; +#endif +#ifndef NOCTX_ACTIVE + if (bucketstate[buckno] & ACTIVE) + ctx |= 8; +#endif + // code difference bit + if (zp.decoder( ctxStart[ctx] )) + { + cstate[i] |= NEW; + int halfthres = thres>>1; + int coeff = thres+halfthres-(halfthres>>2); + if (zp.IWdecoder()) + pcoeff[i] = -coeff; + else + pcoeff[i] = coeff; + } +#ifndef NOCTX_EXPECT + if (cstate[i] & NEW) + gotcha = 0; + else if (gotcha > 0) + gotcha -= 1; +#endif +#ifdef TRACE + DjVuPrintMessage(" coeffstate[bit=%d,band=%d,buck=%d,c=%d] = %d\n", + bit, band, buckno, i, cstate[i]); +#endif + } + } + } + } + + // code mantissa bits + if (bbstate & ACTIVE) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & ACTIVE) + { + short *pcoeff = (short*)blk.data(fbucket+buckno); + for (int i=0; i<16; i++) + if (cstate[i] & ACTIVE) + { + int coeff = pcoeff[i]; + if (coeff < 0) + coeff = -coeff; + // find lores threshold + if (band == 0) + thres = quant_lo[i]; + // adjust coefficient + if (coeff <= 3*thres) + { + // second mantissa bit + coeff = coeff + (thres>>2); + if (zp.decoder(ctxMant)) + coeff = coeff + (thres>>1); + else + coeff = coeff - thres + (thres>>1); + } + else + { + if (zp.IWdecoder()) + coeff = coeff + (thres>>1); + else + coeff = coeff - thres + (thres>>1); + } + // store coefficient + if (pcoeff[i] > 0) + pcoeff[i] = coeff; + else + pcoeff[i] = -coeff; + } + } + } +} + + +////////////////////////////////////////////////////// +// UTILITIES +////////////////////////////////////////////////////// + + +#ifdef min +#undef min +#endif +static inline int +min(const int x, const int y) +{ + return (x <= y) ? x : y; +} + +#ifdef max +#undef max +#endif +static inline int +max(const int x, const int y) +{ + return (y <= x) ? x : y; +} + + +void +IW44Image::PrimaryHeader::decode(GP<ByteStream> gbs) +{ + serial = gbs->read8(); + slices = gbs->read8(); +} + +void +IW44Image::SecondaryHeader::decode(GP<ByteStream> gbs) +{ + major = gbs->read8(); + minor = gbs->read8(); +} + +void +IW44Image::TertiaryHeader::decode(GP<ByteStream> gbs, int major, int minor) +{ + xhi = gbs->read8(); + xlo = gbs->read8(); + yhi = gbs->read8(); + ylo = gbs->read8(); + crcbdelay = 0; + if (major== 1 && minor>=2) + crcbdelay = gbs->read8(); +} + + + +////////////////////////////////////////////////////// +// CLASS IW44Image +////////////////////////////////////////////////////// + +IW44Image::IW44Image(void) + : db_frac(1.0), + ymap(0), cbmap(0), crmap(0), + cslice(0), cserial(0), cbytes(0) +{} + +IW44Image::~IW44Image() +{ + delete ymap; + delete cbmap; + delete crmap; +} + +GP<IW44Image> +IW44Image::create_decode(const ImageType itype) +{ + switch(itype) + { + case COLOR: + return new IWPixmap(); + case GRAY: + return new IWBitmap(); + default: + return 0; + } +} + +int +IW44Image::encode_chunk(GP<ByteStream>, const IWEncoderParms &) +{ + G_THROW( ERR_MSG("IW44Image.codec_open2") ); + return 0; +} + +void +IW44Image::encode_iff(IFFByteStream &, int nchunks, const IWEncoderParms *) +{ + G_THROW( ERR_MSG("IW44Image.codec_open2") ); +} + + +void +IWBitmap::close_codec(void) +{ + delete ycodec; + ycodec = 0; + cslice = cbytes = cserial = 0; +} + +void +IWPixmap::close_codec(void) +{ + delete ycodec; + delete cbcodec; + delete crcodec; + ycodec = crcodec = cbcodec = 0; + cslice = cbytes = cserial = 0; +} + +int +IW44Image::get_width(void) const +{ + return (ymap)?(ymap->iw):0; +} + +int +IW44Image::get_height(void) const +{ + return (ymap)?(ymap->ih):0; +} + + +////////////////////////////////////////////////////// +// CLASS IWBITMAP +////////////////////////////////////////////////////// + +IWBitmap::IWBitmap(void ) +: IW44Image(), ycodec(0) +{} + +IWBitmap::~IWBitmap() +{ + close_codec(); +} + +int +IWBitmap::get_percent_memory(void) const +{ + int buckets = 0; + int maximum = 0; + if (ymap) + { + buckets += ymap->get_bucket_count(); + maximum += 64 * ymap->nb; + } + return 100*buckets/ (maximum ? maximum : 1); +} + +unsigned int +IWBitmap::get_memory_usage(void) const +{ + unsigned int usage = sizeof(GBitmap); + if (ymap) + usage += ymap->get_memory_usage(); + return usage; +} + + +GP<GBitmap> +IWBitmap::get_bitmap(void) +{ + // Check presence of data + if (ymap == 0) + return 0; + // Perform wavelet reconstruction + int w = ymap->iw; + int h = ymap->ih; + GP<GBitmap> pbm = GBitmap::create(h, w); + ymap->image((signed char*)(*pbm)[0],pbm->rowsize()); + // Shift image data + for (int i=0; i<h; i++) + { + unsigned char *urow = (*pbm)[i]; + signed char *srow = (signed char*)urow; + for (int j=0; j<w; j++) + urow[j] = (int)(srow[j]) + 128; + } + pbm->set_grays(256); + return pbm; +} + + +GP<GBitmap> +IWBitmap::get_bitmap(int subsample, const GRect &rect) +{ + if (ymap == 0) + return 0; + // Allocate bitmap + int w = rect.width(); + int h = rect.height(); + GP<GBitmap> pbm = GBitmap::create(h,w); + ymap->image(subsample, rect, (signed char*)(*pbm)[0],pbm->rowsize()); + // Shift image data + for (int i=0; i<h; i++) + { + unsigned char *urow = (*pbm)[i]; + signed char *srow = (signed char*)urow; + for (int j=0; j<w; j++) + urow[j] = (int)(srow[j]) + 128; + } + pbm->set_grays(256); + return pbm; +} + + +int +IWBitmap::decode_chunk(GP<ByteStream> gbs) +{ + // Open + if (! ycodec) + { + cslice = cserial = 0; + delete ymap; + ymap = 0; + } + // Read primary header + struct IW44Image::PrimaryHeader primary; + primary.decode(gbs); + if (primary.serial != cserial) + G_THROW( ERR_MSG("IW44Image.wrong_serial") ); + int nslices = cslice + primary.slices; + // Read auxilliary headers + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.decode(gbs); + if ((secondary.major & 0x7f) != IWCODEC_MAJOR) + G_THROW( ERR_MSG("IW44Image.incompat_codec") ); + if (secondary.minor > IWCODEC_MINOR) + G_THROW( ERR_MSG("IW44Image.recent_codec") ); + // Read tertiary header + struct IW44Image::TertiaryHeader tertiary; + tertiary.decode(gbs, secondary.major & 0x7f, secondary.minor); + if (! (secondary.major & 0x80)) + G_THROW( ERR_MSG("IW44Image.has_color") ); + // Create ymap and ycodec + int w = (tertiary.xhi << 8) | tertiary.xlo; + int h = (tertiary.yhi << 8) | tertiary.ylo; + assert(! ymap); + ymap = new Map(w, h); + assert(! ycodec); + ycodec = new Codec::Decode(*ymap); + } + // Read data + assert(ymap); + assert(ycodec); + GP<ZPCodec> gzp=ZPCodec::create(gbs, false, true); + ZPCodec &zp=*gzp; + int flag = 1; + while (flag && cslice<nslices) + { + flag = ycodec->code_slice(zp); + cslice++; + } + // Return + cserial += 1; + return nslices; +} + +void +IWBitmap::parm_dbfrac(float frac) +{ + if (frac>0 && frac<=1) + db_frac = frac; + else + G_THROW( ERR_MSG("IW44Image.param_range") ); +} + + +int +IWBitmap::get_serial(void) +{ + return cserial; +} + +void +IWBitmap::decode_iff(IFFByteStream &iff, int maxchunks) +{ + if (ycodec) + G_THROW( ERR_MSG("IW44Image.left_open2") ); + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid != "FORM:BM44") + G_THROW( ERR_MSG("IW44Image.corrupt_BM44") ); + while (--maxchunks>=0 && iff.get_chunk(chkid)) + { + if (chkid == "BM44") + decode_chunk(iff.get_bytestream()); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + + + + +////////////////////////////////////////////////////// +// CLASS IWENCODERPARMS +////////////////////////////////////////////////////// + + +IWEncoderParms::IWEncoderParms(void) +{ + // Zero represent default values + memset((void*)this, 0, sizeof(IWEncoderParms)); +} + + + + + +////////////////////////////////////////////////////// +// CLASS IWPIXMAP +////////////////////////////////////////////////////// + + +IWPixmap::IWPixmap(void) +: IW44Image(), crcb_delay(10), crcb_half(0), ycodec(0), cbcodec(0), crcodec(0) +{} + +IWPixmap::~IWPixmap() +{ + close_codec(); +} + +int +IWPixmap::get_percent_memory(void) const +{ + int buckets = 0; + int maximum = 0; + if (ymap) + { + buckets += ymap->get_bucket_count(); + maximum += 64*ymap->nb; + } + if (cbmap) + { + buckets += cbmap->get_bucket_count(); + maximum += 64*cbmap->nb; + } + if (crmap) + { + buckets += crmap->get_bucket_count(); + maximum += 64*crmap->nb; + } + return 100*buckets/ (maximum ? maximum : 1); +} + +unsigned int +IWPixmap::get_memory_usage(void) const +{ + unsigned int usage = sizeof(GPixmap); + if (ymap) + usage += ymap->get_memory_usage(); + if (cbmap) + usage += cbmap->get_memory_usage(); + if (crmap) + usage += crmap->get_memory_usage(); + return usage; +} + + +GP<GPixmap> +IWPixmap::get_pixmap(void) +{ + // Check presence of data + if (ymap == 0) + return 0; + // Allocate pixmap + int w = ymap->iw; + int h = ymap->ih; + GP<GPixmap> ppm = GPixmap::create(h, w); + // Perform wavelet reconstruction + signed char *ptr = (signed char*) (*ppm)[0]; + int rowsep = ppm->rowsize() * sizeof(GPixel); + int pixsep = sizeof(GPixel); + ymap->image(ptr, rowsep, pixsep); + if (crmap && cbmap && crcb_delay >= 0) + { + cbmap->image(ptr+1, rowsep, pixsep, crcb_half); + crmap->image(ptr+2, rowsep, pixsep, crcb_half); + } + // Convert image data to RGB + if (crmap && cbmap && crcb_delay >= 0) + { + Transform::Decode::YCbCr_to_RGB((*ppm)[0], w, h, ppm->rowsize()); + } + else + { + for (int i=0; i<h; i++) + { + GPixel *pixrow = (*ppm)[i]; + for (int j=0; j<w; j++, pixrow++) + pixrow->b = pixrow->g = pixrow->r + = 127 - (int)(((signed char*)pixrow)[0]); + } + } + // Return + return ppm; +} + + + +GP<GPixmap> +IWPixmap::get_pixmap(int subsample, const GRect &rect) +{ + if (ymap == 0) + return 0; + // Allocate + int w = rect.width(); + int h = rect.height(); + GP<GPixmap> ppm = GPixmap::create(h,w); + // Perform wavelet reconstruction + signed char *ptr = (signed char*) (*ppm)[0]; + int rowsep = ppm->rowsize() * sizeof(GPixel); + int pixsep = sizeof(GPixel); + ymap->image(subsample, rect, ptr, rowsep, pixsep); + if (crmap && cbmap && crcb_delay >= 0) + { + cbmap->image(subsample, rect, ptr+1, rowsep, pixsep, crcb_half); + crmap->image(subsample, rect, ptr+2, rowsep, pixsep, crcb_half); + } + // Convert image data to RGB + if (crmap && cbmap && crcb_delay >= 0) + { + Transform::Decode::YCbCr_to_RGB((*ppm)[0], w, h, ppm->rowsize()); + } + else + { + for (int i=0; i<h; i++) + { + GPixel *pixrow = (*ppm)[i]; + for (int j=0; j<w; j++, pixrow++) + pixrow->b = pixrow->g = pixrow->r + = 127 - (int)(((signed char*)pixrow)[0]); + } + } + // Return + return ppm; +} + + +int +IWPixmap::decode_chunk(GP<ByteStream> gbs) +{ + // Open + if (! ycodec) + { + cslice = cserial = 0; + delete ymap; + ymap = 0; + } + + // Read primary header + struct IW44Image::PrimaryHeader primary; + primary.decode(gbs); + if (primary.serial != cserial) + G_THROW( ERR_MSG("IW44Image.wrong_serial2") ); + int nslices = cslice + primary.slices; + // Read secondary header + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.decode(gbs); + if ((secondary.major & 0x7f) != IWCODEC_MAJOR) + G_THROW( ERR_MSG("IW44Image.incompat_codec2") ); + if (secondary.minor > IWCODEC_MINOR) + G_THROW( ERR_MSG("IW44Image.recent_codec2") ); + // Read tertiary header + struct IW44Image::TertiaryHeader tertiary; + tertiary.decode(gbs, secondary.major & 0x7f, secondary.minor); + // Handle header information + int w = (tertiary.xhi << 8) | tertiary.xlo; + int h = (tertiary.yhi << 8) | tertiary.ylo; + crcb_delay = 0; + crcb_half = 0; + if (secondary.minor>=2) + crcb_delay = tertiary.crcbdelay & 0x7f; + if (secondary.minor>=2) + crcb_half = (tertiary.crcbdelay & 0x80 ? 0 : 1); + if (secondary.major & 0x80) + crcb_delay = -1; + // Create ymap and ycodec + assert(! ymap); + assert(! ycodec); + ymap = new Map(w, h); + ycodec = new Codec::Decode(*ymap); + if (crcb_delay >= 0) + { + cbmap = new Map(w, h); + crmap = new Map(w, h); + cbcodec = new Codec::Decode(*cbmap); + crcodec = new Codec::Decode(*crmap); + } + } + // Read data + assert(ymap); + assert(ycodec); + GP<ZPCodec> gzp=ZPCodec::create(gbs, false, true); + ZPCodec &zp=*gzp; + int flag = 1; + while (flag && cslice<nslices) + { + flag = ycodec->code_slice(zp); + if (crcodec && cbcodec && crcb_delay<=cslice) + { + flag |= cbcodec->code_slice(zp); + flag |= crcodec->code_slice(zp); + } + cslice++; + } + // Return + cserial += 1; + return nslices; +} + + +int +IWPixmap::parm_crcbdelay(const int parm) +{ + if (parm >= 0) + crcb_delay = parm; + return crcb_delay; +} + +void +IWPixmap::parm_dbfrac(float frac) +{ + if (frac>0 && frac<=1) + db_frac = frac; + else + G_THROW( ERR_MSG("IW44Image.param_range2") ); +} + +int +IWPixmap::get_serial(void) +{ + return cserial; +} + + +void +IWPixmap::decode_iff(IFFByteStream &iff, int maxchunks) +{ + if (ycodec) + G_THROW( ERR_MSG("IW44Image.left_open4") ); + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid!="FORM:PM44" && chkid!="FORM:BM44") + G_THROW( ERR_MSG("IW44Image.corrupt_BM44_2") ); + while (--maxchunks>=0 && iff.get_chunk(chkid)) + { + if (chkid=="PM44" || chkid=="BM44") + decode_chunk(iff.get_bytestream()); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +////////////////////////////////////////////////////// +// NEW FILTERS +////////////////////////////////////////////////////// + +void +IW44Image::Transform::filter_begin(int w, int h) +{ + if (MMXControl::mmxflag < 0) + MMXControl::enable_mmx(); +} + + +void +IW44Image::Transform::filter_end(void) +{ +#ifdef MMX + if (MMXControl::mmxflag > 0) + MMXemms; +#endif +} + + +////////////////////////////////////////////////////// +// WAVELET TRANSFORM +////////////////////////////////////////////////////// + + +//---------------------------------------------------- +// Function for applying bidimensional IW44 between +// scale intervals begin(inclusive) and end(exclusive) + +void +IW44Image::Transform::Decode::backward(short *p, int w, int h, int rowsize, int begin, int end) +{ + // PREPARATION + filter_begin(w,h); + // LOOP ON SCALES + for (int scale=begin>>1; scale>=end; scale>>=1) + { +#ifdef IWTRANSFORM_TIMER + int tv,th; + th = tv = GOS::ticks(); +#endif + filter_bv(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks(); + tv = th - tv; +#endif + filter_bh(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks()-th; + DjVuPrintErrorUTF8("back%d\tv=%dms h=%dms\n", scale,tv,th); +#endif + } + // TERMINATE + filter_end(); +} + + + + +////////////////////////////////////////////////////// +// COLOR TRANSFORM +////////////////////////////////////////////////////// + +/* Converts YCbCr to RGB. */ +void +IW44Image::Transform::Decode::YCbCr_to_RGB(GPixel *p, int w, int h, int rowsize) +{ + for (int i=0; i<h; i++,p+=rowsize) + { + GPixel *q = p; + for (int j=0; j<w; j++,q++) + { + signed char y = ((signed char*)q)[0]; + signed char b = ((signed char*)q)[1]; + signed char r = ((signed char*)q)[2]; + // This is the Pigeon transform + int t1 = b >> 2 ; + int t2 = r + (r >> 1); + int t3 = y + 128 - t1; + int tr = y + 128 + t2; + int tg = t3 - (t2 >> 1); + int tb = t3 + (b << 1); + q->r = max(0,min(255,tr)); + q->g = max(0,min(255,tg)); + q->b = max(0,min(255,tb)); + } + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IW44Image.h b/kviewshell/plugins/djvu/libdjvu/IW44Image.h new file mode 100644 index 00000000..56cf00fa --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IW44Image.h @@ -0,0 +1,761 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IW44Image.h,v 1.11 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef IW44IMAGE_H_ +#define IW44IMAGE_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name IW44Image.h + + Files #"IW44Image.h"# and #"IW44Image.cpp"# implement the DjVu IW44 wavelet + scheme for the compression of gray-level images (see class \Ref{IWBitmap}) + and color images (see class \Ref{IWPixmap}). Programs \Ref{c44} and + \Ref{d44} demonstrate how to encode and decode IW44 files. + + {\bf IW44 File Structure} --- The IW44 files are structured according to + the EA IFF85 specifications (see \Ref{IFFByteStream.h}). Gray level IW44 + Images consist of a single #"FORM:BM44"# chunk composed of an arbitrary + number of #"BM44"# data chunks. Color IW44 Images consist of a single + #"FORM:PM44"# chunk composed of an arbitrary number of #"PM44"# data + chunks. The successive #"PM44"# or #"BM44"# data chunks contain + successive refinements of the encoded image. Each chunk contains a + certain number of ``data slices''. The first chunk also contains a small + image header. You can use program \Ref{djvuinfo} to display all this + structural information: + \begin{verbatim} + % djvuinfo lag.iw4 + lag.iw4: + FORM:PM44 [62598] + PM44 [10807] #1 - 74 slices - v1.2 (color) - 684x510 + PM44 [23583] #2 - 13 slices + PM44 [28178] #3 - 10 slices + \end{verbatim} + + {\bf Embedded IW44 Images} --- These IW44 data chunks can also appear within + other contexts. Files representing a DjVu page, for instance, consist of + a single #"FORM:DJVU"# composite chunk. This composite chunk may contain + #"BG44"# chunks encoding the background layer and #"FG44"# chunks encoding + the foreground color layer. These #"BG44"# and #"FG44"# chunks are + actually regular IW44 data chunks with a different chunk identifier. This + information too can be displayed using program \Ref{djvuinfo}. + \begin{verbatim} + % djvuinfo graham1.djvu + graham1.djvu: + FORM:DJVU [32553] + INFO [5] 3156x2325, version 17 + Sjbz [17692] + BG44 [2570] #1 - 74 slices - v1.2 (color) - 1052x775 + FG44 [1035] #1 - 100 slices - v1.2 (color) - 263x194 + BG44 [3048] #2 - 10 slices + BG44 [894] #3 - 4 slices + BG44 [7247] #4 - 9 slices + \end{verbatim} + + {\bf Performance} --- The main design objective for the DjVu wavelets + consisted of allowing progressive rendering and smooth scrolling of large + images with limited memory requirements. Decoding functions process the + compressed data and update a memory efficient representation of the + wavelet coefficients. Imaging function then can quickly render an + arbitrary segment of the image using the available data. Both process can + be carried out in two threads of execution. This design plays an + important role in the DjVu system. We have investigated various + state-of-the-art wavelet compression schemes: although these schemes may + achieve slightly smaller file sizes, the decoding functions did not even + approach our requirements. + + The IW44 wavelets satisfy these requirements today. It performs very well + for quality settings resulting in high compression ratios. It should not + be used for quasi-lossless compression because certain design choices + deliberately sacrifice the IW44 quasi-lossless performance in order to + improve the image quality at high compression ratios. + + Little care however has been taken to make the IW44 encoder memory + efficient. This code uses two copies of the wavelet coefficient data + structure (one for the raw coefficients, one for the quantized + coefficients). A more sophisticated implementation should considerably + reduce the memory requirements. + + {\bf Masking} --- When we create a DjVu image, we often know that certain + pixels of the background image are going to be covered by foreground + objects like text or drawings. The DjVu IW44 wavelet decomposition + routine can use an optional bilevel image named the mask. Every non zero + pixel in the mask means the value of the corresponding pixel in the + background image is irrelevant. The wavelet decomposition code will + replace these masked pixels by a color value whose coding cost is minimal + (see \URL{http://www.research.att.com/~leonb/DJVU/mask}). + + {\bf ToDo} --- There are many improvements to be made. Besides better + quantization algorithms (such as trellis quantization and bitrate + allocation), we should allow for more wavelet transforms. These + improvements may be implemented in future version, if (and only if) they + can meet our decoding constraints. Future versions will probably split + file #"IW44Image.cpp"# which currently contains everything. + + @memo + Wavelet encoded images. + @author + L\'eon Bottou <leonb@research.att.com> + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + + @version + #$Id: IW44Image.h,v 1.11 2003/11/07 22:08:22 leonb Exp $# */ +//@{ + + +#include "GSmartPointer.h" +#include "ZPCodec.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GRect; +class IFFByteStream; +class ByteStream; +class GBitmap; +class GPixmap; + + + +/** IW44 encoding parameters. + This data structure gathers the quality specification parameters needed + for encoding each chunk of an IW44 file. Chunk data is generated until + meeting either the slice target, the size target or the decibel target. */ + +struct IWEncoderParms +{ + /** Slice target. Data generation for the current chunk stops if the total + number of slices (in this chunk and all the previous chunks) reaches + value #slice#. The default value #0# has a special meaning: data will + be generated regardless of the number of slices in the file. */ + int slices; + /** Size target. Data generation for the current chunk stops if the total + data size (in this chunk and all the previous chunks), expressed in + bytes, reaches value #size#. The default value #0# has a special + meaning: data will be generated regardless of the file size. */ + int bytes; + /** Decibel target. Data generation for the current chunk stops if the + estimated luminance error, expressed in decibels, reaches value + #decibel#. The default value #0# has a special meaning: data will be + generated regardless of the estimated luminance error. Specifying value + #0# in fact shortcuts the computation of the estimated luminance error + and sensibly speeds up the encoding process. */ + float decibels; + /** Constructor. Initializes the structure with the default values. */ + IWEncoderParms(void); +}; + + + +/** IW44 encoded gray-level and color images. This class acts as a base for + images represented as a collection of IW44 wavelet coefficients. The + coefficients are stored in a memory efficient data structure. Member + function \Ref{get_bitmap} renders an arbitrary segment of the image into + a \Ref{GBitmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} + read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy + constructor and the copy operator are declared as private members. It is + therefore not possible to make multiple copies of instances of this + class. */ + +class IW44Image : public GPEnabled +{ +public: + /** Chrominance processing selector. The following constants may be used as + argument to the following \Ref{IWPixmap} constructor to indicate how the + chrominance information should be processed. There are four possible values: + \begin{description} + \item[CRCBnone:] The wavelet transform will discard the chrominance + information and only keep the luminance. The image will show in shades of gray. + \item[CRCBhalf:] The wavelet transform will process the chrominance at only + half the image resolution. This option creates smaller files but may create + artifacts in highly colored images. + \item[CRCBnormal:] The wavelet transform will process the chrominance at full + resolution. This is the default. + \item[CRCBfull:] The wavelet transform will process the chrominance at full + resolution. This option also disables the chrominance encoding delay + (see \Ref{parm_crcbdelay}) which usually reduces the bitrate associated with the + chrominance information. + \end{description} */ + enum CRCBMode { + CRCBnone, + CRCBhalf, + CRCBnormal, + CRCBfull }; + class Transform; + class Map; + class Block; + class Codec; + struct Alloc; + struct PrimaryHeader; + struct SecondaryHeader; + struct TertiaryHeader; + enum ImageType { + GRAY=false, + COLOR=true }; +protected: + IW44Image(void); +public: + /** Null constructor. Constructs an empty IW44Image object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. You may not use \Ref{encode_iff} or + \Ref{encode_chunk}. */ + static GP<IW44Image> create_decode(const ImageType itype=COLOR); + /** Null constructor. Constructs an empty IW44Image object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. You may then use \Ref{encode_iff} + and \Ref{encode_chunk}. */ + static GP<IW44Image> create_encode(const ImageType itype=COLOR); + // virtual destructor + virtual ~IW44Image(); + /** Initializes an IWBitmap with image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). */ + static GP<IW44Image> create_encode(const GBitmap &bm, const GP<GBitmap> mask=0); + /** Initializes an IWPixmap with color image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). + Argument #crcbmode# specifies how the chrominance information should be + encoded (see \Ref{CRCBMode}). */ + static GP<IW44Image> create_encode(const GPixmap &bm, const GP<GBitmap> mask=0, CRCBMode crcbmode=CRCBnormal); + // ACCESS + /** Returns the width of the IWBitmap image. */ + int get_width(void) const; + /** Returns the height of the IWBitmap image. */ + int get_height(void) const; + /** Reconstructs the complete image. The reconstructed image + is then returned as a GBitmap object. */ + virtual GP<GBitmap> get_bitmap(void) {return 0;} + /** Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GBitmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GBitmap> get_bitmap(int subsample, const GRect &rect) {return 0;} + /** Reconstructs the complete image. The reconstructed image + is then returned as a GPixmap object. */ + virtual GP<GPixmap> get_pixmap(void) {return 0;} + /** Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GPixmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GPixmap> get_pixmap(int subsample, const GRect &rect) {return 0;} + /** Returns the amount of memory used by the wavelet coefficients. This + amount of memory is expressed in bytes. */ + virtual unsigned int get_memory_usage(void) const = 0; + /** Returns the filling ratio of the internal data structure. Wavelet + coefficients are stored in a sparse array. This function tells what + percentage of bins have been effectively allocated. */ + virtual int get_percent_memory(void) const = 0; + // CODER + /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls + how much data is generated. The chunk data is written to ByteStream + #bs# with no IFF header. Successive calls to #encode_chunk# encode + successive chunks. You must call #close_codec# after encoding the last + chunk of a file. */ + virtual int encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parms); + /** Writes a gray level image into DjVu IW44 file. This function creates a + composite chunk (identifier #FORM:BM44# or #FORM:PM44#) composed of + #nchunks# chunks (identifier #BM44# or #PM44#). Data for each chunk is + generated with #encode_chunk# using the corresponding parameters in + array #parms#. */ + virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); + // DECODER + /** Decodes one data chunk from ByteStream #bs#. Successive calls to + #decode_chunk# decode successive chunks. You must call #close_codec# + after decoding the last chunk of a file. Note that function + #get_bitmap# and #decode_chunk# may be called simultaneously from two + execution threads. */ + virtual int decode_chunk(GP<ByteStream> gbs) = 0; + /** This function enters a composite chunk (identifier #FORM:BM44#, or + #FORM:PM44#), and decodes a maximum of #maxchunks# data chunks + (identifier #BM44#). Data for each chunk is processed using the + function #decode_chunk#. */ + virtual void decode_iff(IFFByteStream &iff, int maxchunks=999) = 0; + // MISCELLANEOUS + /** Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void) = 0; + /** Returns the chunk serial number. This function returns the serial + number of the last chunk encoded with #encode_chunk# or decoded with + #decode_chunk#. The first chunk always has serial number #1#. Successive + chunks have increasing serial numbers. Value #0# is returned if this + function is called before calling #encode_chunk# or #decode_chunk# or + after calling #close_codec#. */ + virtual int get_serial(void) = 0; + /** Sets the chrominance delay parameter. This function can be called + before encoding the first color IW44 data chunk. Parameter #parm# is an + encoding delay which reduces the bitrate associated with the + chrominance information. The default chrominance encoding delay is 10. */ + virtual int parm_crcbdelay(const int parm) {return parm;} + /** Sets the #dbfrac# parameter. This function can be called before + encoding the first IW44 data chunk. Parameter #frac# modifies the + decibel estimation algorithm in such a way that the decibel target only + pertains to the average error of the fraction #frac# of the most + misrepresented 32x32 pixel blocks. Setting arguments #frac# to #1.0# + restores the normal behavior. */ + virtual void parm_dbfrac(float frac) = 0; +protected: + // Parameter + float db_frac; + // Data + Map *ymap, *cbmap, *crmap; + int cslice; + int cserial; + int cbytes; +private: + // Disable assignment semantic + IW44Image(const IW44Image &ref); + IW44Image& operator=(const IW44Image &ref); +}; + +#ifdef IW44IMAGE_IMPLIMENTATION + +/*x IW44 encoded gray-level image. This class provided functions for managing + a gray level image represented as a collection of IW44 wavelet + coefficients. The coefficients are stored in a memory efficient data + structure. Member function \Ref{get_bitmap} renders an arbitrary segment + of the image into a \Ref{GBitmap}. Member functions \Ref{decode_iff} and + \Ref{encode_iff} read and write DjVu IW44 files (see \Ref{IW44Image.h}). + Both the copy constructor and the copy operator are declared as private + members. It is therefore not possible to make multiple copies of instances + of this class. */ + +class IWBitmap : public IW44Image +{ +public: + friend class IW44Image; + class Encode; +protected: + /*x Null constructor. Constructs an empty IWBitmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + IWBitmap(void); +public: + //x virtual destructor + virtual ~IWBitmap(); + //x ACCESS + /*x Reconstructs the complete image. The reconstructed image + is then returned as a GBitmap object. */ + virtual GP<GBitmap> get_bitmap(void); + /*x Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GBitmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GBitmap> get_bitmap(int subsample, const GRect &rect); + /*x Returns the amount of memory used by the wavelet coefficients. This + amount of memory is expressed in bytes. */ + virtual unsigned int get_memory_usage(void) const; + /*x Returns the filling ratio of the internal data structure. Wavelet + coefficients are stored in a sparse array. This function tells what + percentage of bins have been effectively allocated. */ + virtual int get_percent_memory(void) const; + // DECODER + /*x Decodes one data chunk from ByteStream #bs#. Successive calls to + #decode_chunk# decode successive chunks. You must call #close_codec# + after decoding the last chunk of a file. Note that function + #get_bitmap# and #decode_chunk# may be called simultaneously from two + execution threads. */ + virtual int decode_chunk(GP<ByteStream> gbs); + /*x Reads a DjVu IW44 file as a gray level image. This function enters a + composite chunk (identifier #FORM:BM44#), and decodes a maximum of + #maxchunks# data chunks (identifier #BM44#). Data for each chunk is + processed using the function #decode_chunk#. */ + virtual void decode_iff(IFFByteStream &iff, int maxchunks=999); + // MISCELLANEOUS + /*x Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); + /*x Returns the chunk serial number. This function returns the serial + number of the last chunk encoded with #encode_chunk# or decoded with + #decode_chunk#. The first chunk always has serial number #1#. Successive + chunks have increasing serial numbers. Value #0# is returned if this + function is called before calling #encode_chunk# or #decode_chunk# or + after calling #close_codec#. */ + virtual int get_serial(void); + /*x Sets the #dbfrac# parameter. This function can be called before + encoding the first IW44 data chunk. Parameter #frac# modifies the + decibel estimation algorithm in such a way that the decibel target only + pertains to the average error of the fraction #frac# of the most + misrepresented 32x32 pixel blocks. Setting arguments #frac# to #1.0# + restores the normal behavior. */ + virtual void parm_dbfrac(float frac); +private: + Codec *ycodec; +}; + + +/*x IW44 encoded color image. This class provided functions for managing a + color image represented as a collection of IW44 wavelet coefficients. The + coefficients are stored in a memory efficient data structure. Member + function \Ref{get_pixmap} renders an arbitrary segment of the image into a + \Ref{GPixmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} + read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy + constructor and the copy operator are declared as private members. It is + therefore not possible to make multiple copies of instances of this + class. */ + +class IWPixmap : public IW44Image +{ +public: + friend class IW44Image; +protected: + class Encode; + /*x Null constructor. Constructs an empty IWPixmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + IWPixmap(void); +public: + // virtual destructor + virtual ~IWPixmap(); + // ACCESS + /*x Reconstructs the complete image. The reconstructed image + is then returned as a GPixmap object. */ + virtual GP<GPixmap> get_pixmap(void); + /*x Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GPixmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GPixmap> get_pixmap(int subsample, const GRect &rect); + /*x Returns the amount of memory used by the wavelet coefficients. This + amount of memory is expressed in bytes. */ + virtual unsigned int get_memory_usage(void) const; + /*x Returns the filling ratio of the internal data structure. Wavelet + coefficients are stored in a sparse array. This function tells what + percentage of bins have been effectively allocated. */ + virtual int get_percent_memory(void) const; + // DECODER + /*x Decodes one data chunk from ByteStream #bs#. Successive calls to + #decode_chunk# decode successive chunks. You must call #close_codec# + after decoding the last chunk of a file. Note that function + #get_bitmap# and #decode_chunk# may be called simultaneously from two + execution threads. */ + virtual int decode_chunk(GP<ByteStream> gbs); + /*x Reads a DjVu IW44 file as a color image. This function enters a + composite chunk (identifier #FORM:PM44# or #FORM:BM44#), and decodes a + maximum of #maxchunks# data chunks (identifier #PM44# or #BM44#). Data + for each chunk is processed using the function #decode_chunk#. */ + virtual void decode_iff(IFFByteStream &iff, int maxchunks=999); + // MISCELLANEOUS + /*x Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); + /*x Returns the chunk serial number. This function returns the serial + number of the last chunk encoded with #encode_chunk# or decoded with + #decode_chunk#. The first chunk always has serial number #1#. Successive + chunks have increasing serial numbers. Value #0# is returned if this + function is called before calling #encode_chunk# or #decode_chunk# or + after calling #close_codec#. */ + virtual int get_serial(void); + /*x Sets the chrominance delay parameter. This function can be called + before encoding the first IW44 data chunk. Parameter #parm# is an + encoding delay which reduces the bitrate associated with the + chrominance information. The default chrominance encoding delay is 10. */ + virtual int parm_crcbdelay(const int parm); + /*x Sets the #dbfrac# parameter. This function can be called before + encoding the first IW44 data chunk. Parameter #frac# modifies the + decibel estimation algorithm in such a way that the decibel target only + pertains to the average error of the fraction #frac# of the most + misrepresented 32x32 pixel blocks. Setting arguments #frac# to #1.0# + restores the normal behavior. */ + virtual void parm_dbfrac(float frac); +protected: + // Parameter + int crcb_delay; + int crcb_half; + // Data +private: + Codec *ycodec, *cbcodec, *crcodec; +}; + +/*x IW44Transform. +*/ +class IW44Image::Transform +{ +public: + class Decode; + class Encode; +protected: + static void filter_begin(int w, int h); + static void filter_end(void); +}; + +struct GPixel; +class IW44Image::Transform::Decode : public IW44Image::Transform +{ +public: + // WAVELET TRANSFORM + /*x Forward transform. */ + static void backward(short *p, int w, int h, int rowsize, int begin, int end); + + // COLOR TRANSFORM + /*x Converts YCbCr to RGB. */ + static void YCbCr_to_RGB(GPixel *p, int w, int h, int rowsize); +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Block [declaration] +// Represents a block of 32x32 coefficients after zigzagging and scaling + + +class IW44Image::Block // DJVU_CLASS +{ +public: + // creating + Block(void); + // accessing scaled coefficients + short get(int n) const; + void set(int n, int val, IW44Image::Map *map); + // converting from liftblock + void read_liftblock(const short *coeff, IW44Image::Map *map); + void write_liftblock(short *coeff, int bmin=0, int bmax=64) const; + // sparse array access + const short* data(int n) const; + short* data(int n, IW44Image::Map *map); + void zero(int n); + // sparse representation +private: + short **(pdata[4]); +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Map [declaration] +// Represents all the blocks of an image + +class IW44Image::Map // DJVU_CLASS +{ +public: + class Encode; + + // construction + Map(int w, int h); + ~Map(); + // image access + void image(signed char *img8, int rowsize, + int pixsep=1, int fast=0); + void image(int subsample, const GRect &rect, + signed char *img8, int rowsize, + int pixsep=1, int fast=0); + // array of blocks + IW44Image::Block *blocks; + // geometry + int iw, ih; + int bw, bh; + int nb; + // coefficient allocation stuff + short *alloc(int n); + short **allocp(int n); + IW44Image::Alloc *chain; + int top; + // statistics + int get_bucket_count(void) const; + unsigned int get_memory_usage(void) const; +}; + +////////////////////////////////////////////////////// +// ENCODING/DECODING WAVELET COEFFICIENTS +// USING HIERARCHICAL SET DIFFERENCE +////////////////////////////////////////////////////// + + +//----------------------------------------------- +// Class IW44Image::Codec [declaration+implementation] +// Maintains information shared while encoding or decoding + +class IW44Image::Codec +{ +public: + class Decode; + class Encode; + +protected: + // Construction + Codec(IW44Image::Map &map); +public: + virtual ~Codec(); + // Coding + int finish_code_slice(ZPCodec &zp); + virtual int code_slice(ZPCodec &zp) = 0; + // Data + IW44Image::Map ↦ // working map + // status + int curband; // current band + int curbit; // current bitplane + // quantization tables + int quant_hi[10]; // quantization for bands 1 to 9 + int quant_lo[16]; // quantization for band 0. + // bucket state + char coeffstate[256]; + char bucketstate[16]; + enum { ZERO = 1, // this coeff never hits this bit + ACTIVE = 2, // this coeff is already active + NEW = 4, // this coeff is becoming active + UNK = 8 }; // this coeff may become active + // coding context + BitContext ctxStart [32]; + BitContext ctxBucket[10][8]; + BitContext ctxMant; + BitContext ctxRoot; + // helper + int is_null_slice(int bit, int band); + int decode_prepare(int fbucket, int nbucket, IW44Image::Block &blk); + void decode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, int fbucket, int nbucket); +}; + +////////////////////////////////////////////////////// +// DEFINITION OF CHUNK HEADERS +////////////////////////////////////////////////////// + + +struct IW44Image::PrimaryHeader { + unsigned char serial; + unsigned char slices; + void encode(GP<ByteStream> gbs); + void decode(GP<ByteStream> gbs); +}; + +struct IW44Image::SecondaryHeader { + unsigned char major; + unsigned char minor; + void encode(GP<ByteStream> gbs); + void decode(GP<ByteStream> gbs); +}; + +struct IW44Image::TertiaryHeader { + unsigned char xhi, xlo; + unsigned char yhi, ylo; + unsigned char crcbdelay; + void encode(GP<ByteStream> gbs); + void decode(GP<ByteStream> gbs, int major=1, int minor=2); +}; + +inline const short* +IW44Image::Block::data(int n) const +{ + if (! pdata[n>>4]) + return 0; + return pdata[n>>4][n&15]; +} + +inline short* +IW44Image::Block::data(int n, IW44Image::Map *map) +{ + if (! pdata[n>>4]) + pdata[n>>4] = map->allocp(16); + if (! pdata[n>>4][n &15]) + pdata[n>>4][n &15] = map->alloc(16); + return pdata[n>>4][n&15]; +} + +inline short +IW44Image::Block::get(int n) const +{ + int n1 = (n>>4); + const short *d = data(n1); + if (! d) + return 0; + return d[n&15]; +} + +inline void +IW44Image::Block::set(int n, int val, IW44Image::Map *map) +{ + int n1 = (n>>4); + short* d = data(n1, map); + d[n&15] = val; +} + +#endif /* IW44IMAGE_IMPLIMENTATION */ + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/JB2EncodeCodec.cpp b/kviewshell/plugins/djvu/libdjvu/JB2EncodeCodec.cpp new file mode 100644 index 00000000..5a8092a0 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JB2EncodeCodec.cpp @@ -0,0 +1,564 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JB2EncodeCodec.cpp,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#ifndef NEED_DECODER_ONLY + +#include "JB2Image.h" +#include "GBitmap.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +//////////////////////////////////////// +//// CLASS JB2Codec::Encode: DECLARATION +//////////////////////////////////////// + +// This class is accessed via the encode +// functions of class JB2Image + + +//**** Class JB2Codec +// This class implements the JB2 coder. +// Contains all contextual information for encoding a JB2Image. + +class JB2Dict::JB2Codec::Encode : public JB2Dict::JB2Codec +{ +public: + Encode(void); + void init(const GP<ByteStream> &gbs); +//virtual + void code(const GP<JB2Image> &jim); + void code(JB2Image *jim) { const GP<JB2Image> gjim(jim);code(gjim); } + void code(const GP<JB2Dict> &jim); + void code(JB2Dict *jim) { const GP<JB2Dict> gjim(jim);code(gjim); } + +protected: + void CodeNum(const int num, const int lo, const int hi, NumContext &ctx); + void encode_libonly_shape(const GP<JB2Image> &jim, int shapeno); +// virtual + bool CodeBit(const bool bit, BitContext &ctx); + void code_comment(GUTF8String &comment); + void code_record_type(int &rectype); + int code_match_index(int &index, JB2Dict &jim); + void code_inherited_shape_count(JB2Dict &jim); + void code_image_size(JB2Dict &jim); + void code_image_size(JB2Image &jim); + void code_absolute_location(JB2Blit *jblt, int rows, int columns); + void code_absolute_mark_size(GBitmap &bm, int border=0); + void code_relative_mark_size(GBitmap &bm, int cw, int ch, int border=0); + void code_bitmap_directly(GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ); + int get_diff(const int x_diff,NumContext &rel_loc); + void code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ); + +private: + GP<ZPCodec> gzp; +}; + + +//////////////////////////////////////// +//// CLASS JB2DICT: IMPLEMENTATION +//////////////////////////////////////// + +void +JB2Dict::encode(const GP<ByteStream> &gbs) const +{ + JB2Codec::Encode codec; + codec.init(gbs); + codec.code(const_cast<JB2Dict *>(this)); +} + +//////////////////////////////////////// +//// CLASS JB2IMAGE: IMPLEMENTATION +//////////////////////////////////////// + +void +JB2Image::encode(const GP<ByteStream> &gbs) const +{ + JB2Codec::Encode codec; + codec.init(gbs); + codec.code(const_cast<JB2Image *>(this)); +} + +//////////////////////////////////////// +//// CLASS JB2CODEC : IMPLEMENTATION +//////////////////////////////////////// + +#define START_OF_DATA (0) +#define NEW_MARK (1) +#define NEW_MARK_LIBRARY_ONLY (2) +#define NEW_MARK_IMAGE_ONLY (3) +#define MATCHED_REFINE (4) +#define MATCHED_REFINE_LIBRARY_ONLY (5) +#define MATCHED_REFINE_IMAGE_ONLY (6) +#define MATCHED_COPY (7) +#define NON_MARK_DATA (8) +#define REQUIRED_DICT_OR_RESET (9) +#define PRESERVED_COMMENT (10) +#define END_OF_DATA (11) + +// STATIC DATA MEMBERS + +static const int BIGPOSITIVE = 262142; +static const int BIGNEGATIVE = -262143; +static const int CELLCHUNK = 20000; +static const int CELLEXTRA = 500; + +// CONSTRUCTOR + +JB2Dict::JB2Codec::Encode::Encode(void) +: JB2Dict::JB2Codec(1) {} + +void +JB2Dict::JB2Codec::Encode::init(const GP<ByteStream> &gbs) +{ + gzp=ZPCodec::create(gbs,true,true); +} + +inline bool +JB2Dict::JB2Codec::Encode::CodeBit(const bool bit, BitContext &ctx) +{ + gzp->encoder(bit?1:0, ctx); + return bit; +} + +void +JB2Dict::JB2Codec::Encode::CodeNum(int num, int low, int high, NumContext &ctx) +{ + if (num < low || num > high) + G_THROW( ERR_MSG("JB2Image.bad_number") ); + JB2Codec::CodeNum(low,high,&ctx,num); +} + +// CODE COMMENTS + +void +JB2Dict::JB2Codec::Encode::code_comment(GUTF8String &comment) +{ + // Encode size + int size=comment.length(); + CodeNum(size, 0, BIGPOSITIVE, dist_comment_length); + for (int i=0; i<size; i++) + { + CodeNum(comment[i], 0, 255, dist_comment_byte); + } +} + +// CODE SIMPLE VALUES + +inline void +JB2Dict::JB2Codec::Encode::code_record_type(int &rectype) +{ + CodeNum(rectype, START_OF_DATA, END_OF_DATA, dist_record_type); +} + +int +JB2Dict::JB2Codec::Encode::code_match_index(int &index, JB2Dict &jim) +{ + int match=shape2lib[index]; + CodeNum(match, 0, lib2shape.hbound(), dist_match_index); + return match; +} + +// CODE PAIRS + +void +JB2Dict::JB2Codec::Encode::code_inherited_shape_count(JB2Dict &jim) +{ + CodeNum(jim.get_inherited_shape_count(), + 0, BIGPOSITIVE, inherited_shape_count_dist); +} + +void +JB2Dict::JB2Codec::Encode::code_image_size(JB2Dict &jim) +{ + CodeNum(0, 0, BIGPOSITIVE, image_size_dist); + CodeNum(0, 0, BIGPOSITIVE, image_size_dist); + JB2Codec::code_image_size(jim); +} + +void +JB2Dict::JB2Codec::Encode::code_image_size(JB2Image &jim) +{ + image_columns = jim.get_width(); + CodeNum(image_columns, 0, BIGPOSITIVE, image_size_dist); + image_rows = jim.get_height(); + CodeNum(image_rows, 0, BIGPOSITIVE, image_size_dist); + JB2Codec::code_image_size(jim); +} + +inline int +JB2Dict::JB2Codec::Encode::get_diff(int x_diff,NumContext &rel_loc) +{ + CodeNum(x_diff, BIGNEGATIVE, BIGPOSITIVE, rel_loc); + return x_diff; +} + +void +JB2Dict::JB2Codec::Encode::code_absolute_location(JB2Blit *jblt, int rows, int columns) +{ + // Check start record + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + // Code TOP and LEFT + CodeNum(jblt->left+1, 1, image_columns, abs_loc_x); + CodeNum(jblt->bottom+rows-1+1, 1, image_rows, abs_loc_y); +} + +void +JB2Dict::JB2Codec::Encode::code_absolute_mark_size(GBitmap &bm, int border) +{ + CodeNum(bm.columns(), 0, BIGPOSITIVE, abs_size_x); + CodeNum(bm.rows(), 0, BIGPOSITIVE, abs_size_y); +} + +void +JB2Dict::JB2Codec::Encode::code_relative_mark_size(GBitmap &bm, int cw, int ch, int border) +{ + CodeNum(bm.columns()-cw, BIGNEGATIVE, BIGPOSITIVE, rel_size_x); + CodeNum(bm.rows()-ch, BIGNEGATIVE, BIGPOSITIVE, rel_size_y); +} + +// CODE BITMAP DIRECTLY + +void +JB2Dict::JB2Codec::Encode::code_bitmap_directly( + GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (encoding) + while (dy >= 0) + { + int context=get_direct_context(up2, up1, up0, 0); + for (int dx=0;dx < dw;) + { + int n = up0[dx++]; + zp.encoder(n, bitdist[context]); + context=shift_direct_context(context, n, up2, up1, up0, dx); + } + // next row + dy -= 1; + up2 = up1; + up1 = up0; + up0 = bm[dy]; + } +} + +// CODE BITMAP BY CROSS CODING + +void +JB2Dict::JB2Codec::Encode::code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (encoding) + while (dy >= 0) + { + int context=get_cross_context(up1, up0, xup1, xup0, xdn1, 0); + for(int dx=0;dx < dw;) + { + const int n = up0[dx++]; + zp.encoder(n, cbitdist[context]); + context=shift_cross_context(context, n, + up1, up0, xup1, xup0, xdn1, dx); + } + // next row + up1 = up0; + up0 = bm[--dy]; + xup1 = xup0; + xup0 = xdn1; + xdn1 = cbm[(--cy)-1] + xd2c; + } +} + +// CODE JB2DICT + +void +JB2Dict::JB2Codec::Encode::code(const GP<JB2Dict> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + // ------------------------- + // THIS IS THE ENCODING PART + // ------------------------- + int firstshape = jim.get_inherited_shape_count(); + int nshape = jim.get_shape_count(); + init_library(jim); + // Code headers. + int rectype = REQUIRED_DICT_OR_RESET; + if (jim.get_inherited_shape_count() > 0) + code_record(rectype, gjim, 0); + rectype = START_OF_DATA; + code_record(rectype, gjim, 0); + // Code Comment. + rectype = PRESERVED_COMMENT; + if (!! jim.comment) + code_record(rectype, gjim, 0); + // Encode every shape + int shapeno; + DJVU_PROGRESS_TASK(jb2code,"jb2 encode", nshape-firstshape); + for (shapeno=firstshape; shapeno<nshape; shapeno++) + { + DJVU_PROGRESS_RUN(jb2code, (shapeno-firstshape)|0xff); + // Code shape + JB2Shape &jshp = jim.get_shape(shapeno); + rectype=(jshp.parent >= 0) + ?MATCHED_REFINE_LIBRARY_ONLY:NEW_MARK_LIBRARY_ONLY; + code_record(rectype, gjim, &jshp); + add_library(shapeno, jshp); + // Check numcoder status + if (cur_ncell > CELLCHUNK) + { + rectype = REQUIRED_DICT_OR_RESET; + code_record(rectype, 0, 0); + } + } + // Code end of data record + rectype = END_OF_DATA; + code_record(rectype, gjim, 0); + gzp=0; +} + +// CODE JB2IMAGE + +void +JB2Dict::JB2Codec::Encode::code(const GP<JB2Image> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + // ------------------------- + // THIS IS THE ENCODING PART + // ------------------------- + int i; + init_library(jim); + int firstshape = jim.get_inherited_shape_count(); + int nshape = jim.get_shape_count(); + int nblit = jim.get_blit_count(); + // Initialize shape2lib + shape2lib.resize(0,nshape-1); + for (i=firstshape; i<nshape; i++) + shape2lib[i] = -1; + // Determine shapes that go into library (shapeno>=firstshape) + // shape2lib is -2 if used by one blit + // shape2lib is -3 if used by more than one blit + // shape2lib is -4 if used as a parent + for (i=0; i<nblit; i++) + { + JB2Blit *jblt = jim.get_blit(i); + int shapeno = jblt->shapeno; + if (shapeno < firstshape) + continue; + if (shape2lib[shapeno] >= -2) + shape2lib[shapeno] -= 1; + shapeno = jim.get_shape(shapeno).parent; + while (shapeno>=firstshape && shape2lib[shapeno]>=-3) + { + shape2lib[shapeno] = -4; + shapeno = jim.get_shape(shapeno).parent; + } + } + // Code headers. + int rectype = REQUIRED_DICT_OR_RESET; + if (jim.get_inherited_shape_count() > 0) + code_record(rectype, gjim, 0, 0); + rectype = START_OF_DATA; + code_record(rectype, gjim, 0, 0); + // Code Comment. + rectype = PRESERVED_COMMENT; + if (!! jim.comment) + code_record(rectype, gjim, 0, 0); + // Encode every blit + int blitno; + DJVU_PROGRESS_TASK(jb2code,"jb2 encode", nblit); + for (blitno=0; blitno<nblit; blitno++) + { + DJVU_PROGRESS_RUN(jb2code, blitno|0xff); + JB2Blit *jblt = jim.get_blit(blitno); + int shapeno = jblt->shapeno; + JB2Shape &jshp = jim.get_shape(shapeno); + // Tests if shape exists in library + if (shape2lib[shapeno] >= 0) + { + int rectype = MATCHED_COPY; + code_record(rectype, gjim, 0, jblt); + } + // Avoid coding null shapes/blits + else if (jshp.bits) + { + // Make sure all parents have been coded + if (jshp.parent>=0 && shape2lib[jshp.parent]<0) + encode_libonly_shape(gjim, jshp.parent); + // Allocate library entry when needed +#define LIBRARY_CONTAINS_ALL + int libraryp = 0; +#ifdef LIBRARY_CONTAINS_MARKS // baseline + if (jshp.parent >= -1) + libraryp = 1; +#endif +#ifdef LIBRARY_CONTAINS_SHARED // worse + if (shape2lib[shapeno] <= -3) + libraryp = 1; +#endif +#ifdef LIBRARY_CONTAINS_ALL // better + libraryp = 1; +#endif + // Test all blit cases + if (jshp.parent<-1 && !libraryp) + { + int rectype = NON_MARK_DATA; + code_record(rectype, gjim, &jshp, jblt); + } + else if (jshp.parent < 0) + { + int rectype = (libraryp ? NEW_MARK : NEW_MARK_IMAGE_ONLY); + code_record(rectype, gjim, &jshp, jblt); + } + else + { + int rectype = (libraryp ? MATCHED_REFINE : MATCHED_REFINE_IMAGE_ONLY); + code_record(rectype, gjim, &jshp, jblt); + } + // Add shape to library + if (libraryp) + add_library(shapeno, jshp); + } + // Check numcoder status + if (cur_ncell > CELLCHUNK) + { + rectype = REQUIRED_DICT_OR_RESET; + code_record(rectype, 0, 0); + } + } + // Code end of data record + rectype = END_OF_DATA; + code_record(rectype, gjim, 0, 0); + gzp=0; +} + +//////////////////////////////////////// +//// HELPERS +//////////////////////////////////////// + +void +JB2Dict::JB2Codec::Encode::encode_libonly_shape( + const GP<JB2Image> &gjim, int shapeno ) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + // Recursively encode parent shape + JB2Shape &jshp = jim.get_shape(shapeno); + if (jshp.parent>=0 && shape2lib[jshp.parent]<0) + encode_libonly_shape(gjim, jshp.parent); + // Test that library shape must be encoded + if (shape2lib[shapeno] < 0) + { + // Code library entry + int rectype=(jshp.parent >= 0) + ?NEW_MARK_LIBRARY_ONLY:MATCHED_REFINE_LIBRARY_ONLY; + code_record(rectype, gjim, &jshp, 0); + // Add shape to library + add_library(shapeno, jshp); + // Check numcoder status + if (cur_ncell > CELLCHUNK) + { + rectype = REQUIRED_DICT_OR_RESET; + code_record(rectype, 0, 0); + } + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif /* NEED_DECODER_ONLY */ + diff --git a/kviewshell/plugins/djvu/libdjvu/JB2Image.cpp b/kviewshell/plugins/djvu/libdjvu/JB2Image.cpp new file mode 100644 index 00000000..7aad9261 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JB2Image.cpp @@ -0,0 +1,1427 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JB2Image.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#include "JB2Image.h" +#include "GThreads.h" +#include "GRect.h" +#include "GBitmap.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +//////////////////////////////////////// +//// CLASS JB2Codec::Decode: DECLARATION +//////////////////////////////////////// + +// This class is accessed via the decode +// functions of class JB2Image + + +//**** Class JB2Codec +// This class implements the JB2 decoder. +// Contains all contextual information for decoding a JB2Image. + +class JB2Dict::JB2Codec::Decode : public JB2Dict::JB2Codec +{ +public: + Decode(void); + void init(const GP<ByteStream> &gbs); +// virtual + void code(const GP<JB2Image> &jim); + void code(JB2Image *jim) {const GP<JB2Image> gjim(jim);code(gjim);} + void code(const GP<JB2Dict> &jim); + void code(JB2Dict *jim) {const GP<JB2Dict> gjim(jim);code(gjim);} + void set_dict_callback(JB2DecoderCallback *cb, void *arg); +protected: + int CodeNum(const int lo, const int hi, NumContext &ctx); + +// virtual + bool CodeBit(const bool bit, BitContext &ctx); + void code_comment(GUTF8String &comment); + void code_record_type(int &rectype); + int code_match_index(int &index, JB2Dict &jim); + void code_inherited_shape_count(JB2Dict &jim); + void code_image_size(JB2Dict &jim); + void code_image_size(JB2Image &jim); + void code_absolute_location(JB2Blit *jblt, int rows, int columns); + void code_absolute_mark_size(GBitmap &bm, int border=0); + void code_relative_mark_size(GBitmap &bm, int cw, int ch, int border=0); + void code_bitmap_directly(GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ); + void code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ); + int get_diff(const int x_diff,NumContext &rel_loc); + +private: + GP<ZPCodec> gzp; + JB2DecoderCallback *cbfunc; + void *cbarg; +}; + +//////////////////////////////////////// +//// CLASS JB2DICT: IMPLEMENTATION +//////////////////////////////////////// + + +JB2Dict::JB2Dict() + : inherited_shapes(0) +{ +} + +void +JB2Dict::init() +{ + inherited_shapes = 0; + inherited_dict = 0; + shapes.empty(); +} + +JB2Shape & +JB2Dict::get_shape(const int shapeno) +{ + JB2Shape *retval; + if(shapeno >= inherited_shapes) + { + retval=&shapes[shapeno - inherited_shapes]; + }else if(inherited_dict) + { + retval=&(inherited_dict->get_shape(shapeno)); + }else + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + return *retval; +} + +const JB2Shape & +JB2Dict::get_shape(const int shapeno) const +{ + const JB2Shape *retval; + if(shapeno >= inherited_shapes) + { + retval=&shapes[shapeno - inherited_shapes]; + }else if(inherited_dict) + { + retval=&(inherited_dict->get_shape(shapeno)); + }else + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + return *retval; +} + +void +JB2Dict::set_inherited_dict(const GP<JB2Dict> &dict) +{ + if (shapes.size() > 0) + G_THROW( ERR_MSG("JB2Image.cant_set") ); + if (inherited_dict) + G_THROW( ERR_MSG("JB2Image.cant_change") ); + inherited_dict = dict; + inherited_shapes = dict->get_shape_count(); + // Make sure that inherited bitmaps are marked as shared + for (int i=0; i<inherited_shapes; i++) + { + JB2Shape &jshp = dict->get_shape(i); + if (jshp.bits) jshp.bits->share(); + } +} + +void +JB2Dict::compress() +{ + for (int i=shapes.lbound(); i<=shapes.hbound(); i++) + shapes[i].bits->compress(); +} + +unsigned int +JB2Dict::get_memory_usage() const +{ + unsigned int usage = sizeof(JB2Dict); + usage += sizeof(JB2Shape) * shapes.size(); + for (int i=shapes.lbound(); i<=shapes.hbound(); i++) + if (shapes[i].bits) + usage += shapes[i].bits->get_memory_usage(); + return usage; +} + +int +JB2Dict::add_shape(const JB2Shape &shape) +{ + if (shape.parent >= get_shape_count()) + G_THROW( ERR_MSG("JB2Image.bad_parent_shape") ); + int index = shapes.size(); + shapes.touch(index); + shapes[index] = shape; + return index + inherited_shapes; +} + +void +JB2Dict::decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb, void *arg) +{ + init(); + JB2Codec::Decode codec; + codec.init(gbs); + codec.set_dict_callback(cb,arg); + codec.code(this); +} + + + +//////////////////////////////////////// +//// CLASS JB2IMAGE: IMPLEMENTATION +//////////////////////////////////////// + + +JB2Image::JB2Image(void) + : width(0), height(0), reproduce_old_bug(false) +{ +} + +void +JB2Image::init(void) +{ + width = height = 0; + blits.empty(); + JB2Dict::init(); +} + +unsigned int +JB2Image::get_memory_usage() const +{ + unsigned int usage = JB2Dict::get_memory_usage(); + usage += sizeof(JB2Image) - sizeof(JB2Dict); + usage += sizeof(JB2Blit) * blits.size(); + return usage; +} + +void +JB2Image::set_dimension(int awidth, int aheight) +{ + width = awidth; + height = aheight; +} + +int +JB2Image::add_blit(const JB2Blit &blit) +{ + if (blit.shapeno >= (unsigned int)get_shape_count()) + G_THROW( ERR_MSG("JB2Image.bad_shape") ); + int index = blits.size(); + blits.touch(index); + blits[index] = blit; + return index; +} + +GP<GBitmap> +JB2Image::get_bitmap(int subsample, int align) const +{ + if (width==0 || height==0) + G_THROW( ERR_MSG("JB2Image.cant_create") ); + int swidth = (width + subsample - 1) / subsample; + int sheight = (height + subsample - 1) / subsample; + int border = ((swidth + align - 1) & ~(align - 1)) - swidth; + GP<GBitmap> bm = GBitmap::create(sheight, swidth, border); + bm->set_grays(1+subsample*subsample); + for (int blitno = 0; blitno < get_blit_count(); blitno++) + { + const JB2Blit *pblit = get_blit(blitno); + const JB2Shape &pshape = get_shape(pblit->shapeno); + if (pshape.bits) + bm->blit(pshape.bits, pblit->left, pblit->bottom, subsample); + } + return bm; +} + +GP<GBitmap> +JB2Image::get_bitmap(const GRect &rect, int subsample, int align, int dispy) const +{ + if (width==0 || height==0) + G_THROW( ERR_MSG("JB2Image.cant_create") ); + int rxmin = rect.xmin * subsample; + int rymin = rect.ymin * subsample; + int swidth = rect.width(); + int sheight = rect.height(); + int border = ((swidth + align - 1) & ~(align - 1)) - swidth; + GP<GBitmap> bm = GBitmap::create(sheight, swidth, border); + bm->set_grays(1+subsample*subsample); + for (int blitno = 0; blitno < get_blit_count(); blitno++) + { + const JB2Blit *pblit = get_blit(blitno); + const JB2Shape &pshape = get_shape(pblit->shapeno); + if (pshape.bits) + bm->blit(pshape.bits, pblit->left-rxmin, pblit->bottom-rymin+dispy, subsample); + } + return bm; +} + +void +JB2Image::decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb, void *arg) +{ + init(); + JB2Codec::Decode codec; + codec.init(gbs); + codec.set_dict_callback(cb,arg); + codec.code(this); +} + + + +//////////////////////////////////////// +//// CLASS JB2CODEC : IMPLEMENTATION +//////////////////////////////////////// + + + +#define START_OF_DATA (0) +#define NEW_MARK (1) +#define NEW_MARK_LIBRARY_ONLY (2) +#define NEW_MARK_IMAGE_ONLY (3) +#define MATCHED_REFINE (4) +#define MATCHED_REFINE_LIBRARY_ONLY (5) +#define MATCHED_REFINE_IMAGE_ONLY (6) +#define MATCHED_COPY (7) +#define NON_MARK_DATA (8) +#define REQUIRED_DICT_OR_RESET (9) +#define PRESERVED_COMMENT (10) +#define END_OF_DATA (11) + + + +// STATIC DATA MEMBERS + +static const int BIGPOSITIVE = 262142; +static const int BIGNEGATIVE = -262143; +static const int CELLCHUNK = 20000; +static const int CELLEXTRA = 500; + + +// CONSTRUCTOR + +JB2Dict::JB2Codec::Decode::Decode(void) +: JB2Dict::JB2Codec(0), cbfunc(0), cbarg(0) {} + +void +JB2Dict::JB2Codec::Decode::init(const GP<ByteStream> &gbs) +{ + gzp=ZPCodec::create(gbs,false,true); +} + +JB2Dict::JB2Codec::JB2Codec(const bool xencoding) + : encoding(xencoding), + cur_ncell(0), + gbitcells(bitcells,CELLCHUNK+CELLEXTRA), + gleftcell(leftcell,CELLCHUNK+CELLEXTRA), + grightcell(rightcell,CELLCHUNK+CELLEXTRA), + refinementp(false), + gotstartrecordp(0), + dist_comment_byte(0), + dist_comment_length(0), + dist_record_type(0), + dist_match_index(0), + dist_refinement_flag(0), + abs_loc_x(0), + abs_loc_y(0), + abs_size_x(0), + abs_size_y(0), + image_size_dist(0), + inherited_shape_count_dist(0), + offset_type_dist(0), + rel_loc_x_current(0), + rel_loc_x_last(0), + rel_loc_y_current(0), + rel_loc_y_last(0), + rel_size_x(0), + rel_size_y(0) +{ + memset(bitdist, 0, sizeof(bitdist)); + memset(cbitdist, 0, sizeof(cbitdist)); + // Initialize numcoder + bitcells[0] = 0; // dummy cell + leftcell[0] = rightcell[0] = 0; + cur_ncell = 1; +} + +JB2Dict::JB2Codec::~JB2Codec() {} + +void +JB2Dict::JB2Codec::reset_numcoder() +{ + dist_comment_byte = 0; + dist_comment_length = 0; + dist_record_type = 0; + dist_match_index = 0; + abs_loc_x = 0; + abs_loc_y = 0; + abs_size_x = 0; + abs_size_y = 0; + image_size_dist = 0; + inherited_shape_count_dist = 0; + rel_loc_x_current = 0; + rel_loc_x_last = 0; + rel_loc_y_current = 0; + rel_loc_y_last = 0; + rel_size_x = 0; + rel_size_y = 0; + gbitcells.clear(); + gleftcell.clear(); + grightcell.clear(); + cur_ncell = 1; +} + + +void +JB2Dict::JB2Codec::Decode::set_dict_callback(JB2DecoderCallback *cb, void *arg) +{ + cbfunc = cb; + cbarg = arg; +} + + +// CODE NUMBERS + +inline bool +JB2Dict::JB2Codec::Decode::CodeBit(const bool, BitContext &ctx) +{ + return gzp->decoder(ctx)?true:false; +} + +int +JB2Dict::JB2Codec::Decode::CodeNum(int low, int high, NumContext &ctx) +{ + return JB2Codec::CodeNum(low,high,&ctx,0); +} + +int +JB2Dict::JB2Codec::CodeNum(int low, int high, NumContext *pctx, int v) +{ + bool negative=false; + int cutoff; + // Check + if (!pctx || ((int)*pctx >= cur_ncell)) + G_THROW( ERR_MSG("JB2Image.bad_numcontext") ); + // Start all phases + cutoff = 0; + for(int phase=1,range=0xffffffff;range != 1;) + { + if (! *pctx) + { + const int max_ncell=gbitcells; + if (cur_ncell >= max_ncell) + { + const int nmax_ncell = max_ncell+CELLCHUNK; + gbitcells.resize(nmax_ncell); + gleftcell.resize(nmax_ncell); + grightcell.resize(nmax_ncell); + } + *pctx = cur_ncell ++; + bitcells[*pctx] = 0; + leftcell[*pctx] = rightcell[*pctx] = 0; + } + // encode + const bool decision = encoding + ? ((low < cutoff && high >= cutoff) + ? CodeBit((v>=cutoff),bitcells[*pctx]) + : (v >= cutoff)) + : ((low>=cutoff)||((high>=cutoff)&&CodeBit(false,bitcells[*pctx]))); + // context for new bit + pctx = decision?(&rightcell[*pctx]):(&leftcell[*pctx]); + // phase dependent part + switch (phase) + { + case 1: + negative = !decision; + if (negative) + { + if (encoding) + v = - v - 1; + const int temp = - low - 1; + low = - high - 1; + high = temp; + } + phase = 2; cutoff = 1; + break; + + case 2: + if (!decision) + { + phase = 3; + range = (cutoff + 1) / 2; + if (range == 1) + cutoff = 0; + else + cutoff -= range / 2; + } + else + { + cutoff += cutoff + 1; + } + break; + + case 3: + range /= 2; + if (range != 1) + { + if (!decision) + cutoff -= range / 2; + else + cutoff += range / 2; + } + else if (!decision) + { + cutoff --; + } + break; + } + } + return (negative)?(- cutoff - 1):cutoff; +} + + + +// CODE COMMENTS + +void +JB2Dict::JB2Codec::Decode::code_comment(GUTF8String &comment) +{ + int size=CodeNum(0, BIGPOSITIVE, dist_comment_length); + comment.empty(); + char *combuf = comment.getbuf(size); + for (int i=0; i<size; i++) + { + combuf[i]=CodeNum(0, 255, dist_comment_byte); + } + comment.getbuf(); +} + + +// LIBRARY + + +void +JB2Dict::JB2Codec::init_library(JB2Dict &jim) +{ + int nshape = jim.get_inherited_shape_count(); + shape2lib.resize(0,nshape-1); + lib2shape.resize(0,nshape-1); + libinfo.resize(0,nshape-1); + for (int i=0; i<nshape; i++) + { + shape2lib[i] = i; + lib2shape[i] = i; + JB2Shape &jshp = jim.get_shape(i); + libinfo[i].compute_bounding_box(*(jshp.bits)); + } +} + +int +JB2Dict::JB2Codec::add_library(const int shapeno, JB2Shape &jshp) +{ + const int libno = lib2shape.hbound() + 1; + lib2shape.touch(libno); + lib2shape[libno] = shapeno; + shape2lib.touch(shapeno); + shape2lib[shapeno] = libno; + libinfo.touch(libno); + libinfo[libno].compute_bounding_box(*(jshp.bits)); + return libno; +} + + +// CODE SIMPLE VALUES + +inline void +JB2Dict::JB2Codec::Decode::code_record_type(int &rectype) +{ + rectype=CodeNum( START_OF_DATA, END_OF_DATA, dist_record_type); +} + +int +JB2Dict::JB2Codec::Decode::code_match_index(int &index, JB2Dict &) +{ + int match=CodeNum(0, lib2shape.hbound(), dist_match_index); + index = lib2shape[match]; + return match; +} + + +// HANDLE SHORT LIST + +int +JB2Dict::JB2Codec::update_short_list(const int v) +{ + if (++ short_list_pos == 3) + short_list_pos = 0; + int * const s = short_list; + s[short_list_pos] = v; + + return (s[0] >= s[1]) + ?((s[0] > s[2])?((s[1] >= s[2])?s[1]:s[2]):s[0]) + :((s[0] < s[2])?((s[1] >= s[2])?s[2]:s[1]):s[0]); +} + + + +// CODE PAIRS + + +void +JB2Dict::JB2Codec::Decode::code_inherited_shape_count(JB2Dict &jim) +{ + int size=CodeNum(0, BIGPOSITIVE, inherited_shape_count_dist); + { + GP<JB2Dict> dict = jim.get_inherited_dict(); + if (!dict && size>0) + { + // Call callback function to obtain dictionary + if (cbfunc) + dict = (*cbfunc)(cbarg); + if (dict) + jim.set_inherited_dict(dict); + } + if (!dict && size>0) + G_THROW( ERR_MSG("JB2Image.need_dict") ); + if (dict && size!=dict->get_shape_count()) + G_THROW( ERR_MSG("JB2Image.bad_dict") ); + } +} + +void +JB2Dict::JB2Codec::Decode::code_image_size(JB2Dict &jim) +{ + int w=CodeNum(0, BIGPOSITIVE, image_size_dist); + int h=CodeNum(0, BIGPOSITIVE, image_size_dist); + if (w || h) + G_THROW( ERR_MSG("JB2Image.bad_dict2") ); + JB2Codec::code_image_size(jim); +} + +void +JB2Dict::JB2Codec::code_image_size(JB2Dict &) +{ + last_left = 1; + last_row_left = 0; + last_row_bottom = 0; + last_right = 0; + fill_short_list(last_row_bottom); + gotstartrecordp = 1; +} + +void +JB2Dict::JB2Codec::Decode::code_image_size(JB2Image &jim) +{ + image_columns=CodeNum(0, BIGPOSITIVE, image_size_dist); + image_rows=CodeNum(0, BIGPOSITIVE, image_size_dist); + if (!image_columns || !image_rows) + G_THROW( ERR_MSG("JB2Image.zero_dim") ); + jim.set_dimension(image_columns, image_rows); + JB2Codec::code_image_size(jim); +} + +void +JB2Dict::JB2Codec::code_image_size(JB2Image &) +{ + last_left = 1 + image_columns; + last_row_left = 0; + last_row_bottom = image_rows; + last_right = 0; + fill_short_list(last_row_bottom); + gotstartrecordp = 1; +} + +inline int +JB2Dict::JB2Codec::Decode::get_diff(int,NumContext &rel_loc) +{ + return CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_loc); +} + +void +JB2Dict::JB2Codec::code_relative_location(JB2Blit *jblt, int rows, int columns) +{ + // Check start record + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + // Find location + int bottom=0, left=0, top=0, right=0; + int x_diff, y_diff; + if (encoding) + { + left = jblt->left + 1; + bottom = jblt->bottom + 1; + right = left + columns - 1; + top = bottom + rows - 1; + } + // Code offset type + int new_row=CodeBit((left<last_left), offset_type_dist); + if (new_row) + { + // Begin a new row + x_diff=get_diff(left-last_row_left,rel_loc_x_last); + y_diff=get_diff(top-last_row_bottom,rel_loc_y_last); + if (!encoding) + { + left = last_row_left + x_diff; + top = last_row_bottom + y_diff; + right = left + columns - 1; + bottom = top - rows + 1; + } + last_left = last_row_left = left; + last_right = right; + last_bottom = last_row_bottom = bottom; + fill_short_list(bottom); + } + else + { + // Same row + x_diff=get_diff(left-last_right,rel_loc_x_current); + y_diff=get_diff(bottom-last_bottom,rel_loc_y_current); + if (!encoding) + { + left = last_right + x_diff; + bottom = last_bottom + y_diff; + right = left + columns - 1; + top = bottom + rows - 1; + } + last_left = left; + last_right = right; + last_bottom = update_short_list(bottom); + } + // Store in blit record + if (!encoding) + { + jblt->bottom = bottom - 1; + jblt->left = left - 1; + } +} + +void +JB2Dict::JB2Codec::Decode::code_absolute_location(JB2Blit *jblt, int rows, int columns) +{ + // Check start record + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + int left=CodeNum(1, image_columns, abs_loc_x); + int top=CodeNum(1, image_rows, abs_loc_y); + jblt->bottom = top - rows + 1 - 1; + jblt->left = left - 1; +} + +void +JB2Dict::JB2Codec::Decode::code_absolute_mark_size(GBitmap &bm, int border) +{ + int xsize=CodeNum(0, BIGPOSITIVE, abs_size_x); + int ysize=CodeNum(0, BIGPOSITIVE, abs_size_y); + if ((xsize!=(unsigned short)xsize) || (ysize!=(unsigned short)ysize)) + G_THROW( ERR_MSG("JB2Image.bad_number") ); + bm.init(ysize, xsize, border); +} + +void +JB2Dict::JB2Codec::Decode::code_relative_mark_size(GBitmap &bm, int cw, int ch, int border) +{ + int xdiff=CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_size_x); + int ydiff=CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_size_y); + int xsize = cw + xdiff; + int ysize = ch + ydiff; + if ((xsize!=(unsigned short)xsize) || (ysize!=(unsigned short)ysize)) + G_THROW( ERR_MSG("JB2Image.bad_number") ); + bm.init(ysize, xsize, border); +} + + + + +// CODE BITMAP DIRECTLY + +void +JB2Dict::JB2Codec::code_bitmap_directly (GBitmap &bm) +{ + // Make sure bitmap will not be disturbed + GMonitorLock lock(bm.monitor()); + // ensure borders are adequate + bm.minborder(3); + // initialize row pointers + int dy = bm.rows() - 1; + code_bitmap_directly(bm,bm.columns(),dy,bm[dy+2],bm[dy+1],bm[dy]); +} + +void +JB2Dict::JB2Codec::Decode::code_bitmap_directly( + GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (decoding) + while (dy >= 0) + { + int context=get_direct_context(up2, up1, up0, 0); + for(int dx=0;dx < dw;) + { + int n = zp.decoder(bitdist[context]); + up0[dx++] = n; + context=shift_direct_context(context, n, up2, up1, up0, dx); + } + // next row + dy -= 1; + up2 = up1; + up1 = up0; + up0 = bm[dy]; + } +#ifndef NDEBUG + bm.check_border(); +#endif +} + + + + + +// CODE BITMAP BY CROSS CODING + +void +JB2Dict::JB2Codec::code_bitmap_by_cross_coding (GBitmap &bm, GP<GBitmap> &cbm, const int libno) +{ + // Make sure bitmaps will not be disturbed + GP<GBitmap> copycbm=GBitmap::create(); + if (cbm->monitor()) + { + // Perform a copy when the bitmap is explicitely shared + GMonitorLock lock2(cbm->monitor()); + copycbm->init(*cbm); + cbm = copycbm; + } + GMonitorLock lock1(bm.monitor()); + // Center bitmaps + const int cw = cbm->columns(); + const int dw = bm.columns(); + const int dh = bm.rows(); + const LibRect &l = libinfo[libno]; + const int xd2c = (dw/2 - dw + 1) - ((l.right - l.left + 1)/2 - l.right); + const int yd2c = (dh/2 - dh + 1) - ((l.top - l.bottom + 1)/2 - l.top); + // Ensure borders are adequate + bm.minborder(2); + cbm->minborder(2-xd2c); + cbm->minborder(2+dw+xd2c-cw); + // Initialize row pointers + const int dy = dh - 1; + const int cy = dy + yd2c; +#ifndef NDEBUG + bm.check_border(); + cbm->check_border(); +#endif + code_bitmap_by_cross_coding (bm,*cbm, xd2c, dw, dy, cy, bm[dy+1], bm[dy], + (*cbm)[cy+1] + xd2c, (*cbm)[cy ] + xd2c, (*cbm)[cy-1] + xd2c); +} + +void +JB2Dict::JB2Codec::Decode::code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (decoding) + while (dy >= 0) + { + int context=get_cross_context( + up1, up0, xup1, xup0, xdn1, 0); + for(int dx=0;dx < dw;) + { + const int n = zp.decoder(cbitdist[context]); + up0[dx++] = n; + context=shift_cross_context(context, n, + up1, up0, xup1, xup0, xdn1, dx); + } + // next row + up1 = up0; + up0 = bm[--dy]; + xup1 = xup0; + xup0 = xdn1; + xdn1 = cbm[(--cy)-1] + xd2c; +#ifndef NDEBUG + bm.check_border(); +#endif + } +} + + + + +// CODE JB2DICT RECORD + +void +JB2Dict::JB2Codec::code_record( + int &rectype, const GP<JB2Dict> &gjim, JB2Shape *xjshp) +{ + GP<GBitmap> cbm; + GP<GBitmap> bm; + int shapeno = -1; + + // Code record type + code_record_type(rectype); + + // Pre-coding actions + switch(rectype) + { + case NEW_MARK_LIBRARY_ONLY: + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + if (!encoding) + { + jshp.bits = GBitmap::create(); + jshp.parent = -1; + } + bm = jshp.bits; + break; + } + } + // Coding actions + switch (rectype) + { + case START_OF_DATA: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + code_image_size (jim); + code_eventual_lossless_refinement (); + if (! encoding) + init_library(jim); + break; + } + case NEW_MARK_LIBRARY_ONLY: + { + code_absolute_mark_size (*bm, 4); + code_bitmap_directly (*bm); + break; + } + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + JB2Shape &jshp=*xjshp; + int match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + code_bitmap_by_cross_coding (*bm, cbm, jshp.parent); + break; + } + case PRESERVED_COMMENT: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + code_comment(jim.comment); + break; + } + case REQUIRED_DICT_OR_RESET: + { + if (! gotstartrecordp) + { + // Indicates need for a shape dictionary + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + code_inherited_shape_count(*gjim); + }else + // Reset all numerical contexts to zero + reset_numcoder(); + break; + } + case END_OF_DATA: + { + break; + } + default: + { + G_THROW( ERR_MSG("JB2Image.bad_type") ); + } + } + // Post-coding action + if (!encoding) + { + // add shape to dictionary + switch(rectype) + { + case NEW_MARK_LIBRARY_ONLY: + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + shapeno = gjim->add_shape(jshp); + add_library(shapeno, jshp); + break; + } + } + // make sure everything is compacted + // decompaction will occur automatically when needed + if (bm) + bm->compress(); + } +} + + +// CODE JB2DICT + +void +JB2Dict::JB2Codec::Decode::code(const GP<JB2Dict> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + // ------------------------- + // THIS IS THE DECODING PART + // ------------------------- + int rectype; + JB2Shape tmpshape; + do + { + code_record(rectype, gjim, &tmpshape); + } + while(rectype != END_OF_DATA); + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + jim.compress(); +} + + + +// CODE JB2IMAGE RECORD + +void +JB2Dict::JB2Codec::code_record( + int &rectype, const GP<JB2Image> &gjim, JB2Shape *xjshp, JB2Blit *jblt) +{ + GP<GBitmap> bm; + GP<GBitmap> cbm; + int shapeno = -1; + int match; + + // Code record type + code_record_type(rectype); + + // Pre-coding actions + switch(rectype) + { + case NEW_MARK: + case NEW_MARK_LIBRARY_ONLY: + case NEW_MARK_IMAGE_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_LIBRARY_ONLY: + case MATCHED_REFINE_IMAGE_ONLY: + case NON_MARK_DATA: + { + if(!xjshp) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + if (!encoding) + { + jshp.bits = GBitmap::create(); + jshp.parent = -1; + if (rectype == NON_MARK_DATA) + jshp.parent = -2; + } + bm = jshp.bits; + break; + } + } + // Coding actions + switch (rectype) + { + case START_OF_DATA: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + code_image_size (jim); + code_eventual_lossless_refinement (); + if (! encoding) + init_library(jim); + break; + } + case NEW_MARK: + { + code_absolute_mark_size (*bm, 4); + code_bitmap_directly (*bm); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case NEW_MARK_LIBRARY_ONLY: + { + code_absolute_mark_size (*bm, 4); + code_bitmap_directly (*bm); + break; + } + case NEW_MARK_IMAGE_ONLY: + { + code_absolute_mark_size (*bm, 3); + code_bitmap_directly (*bm); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case MATCHED_REFINE: + { + if(!xjshp || !gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + JB2Image &jim=*gjim; + match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + code_bitmap_by_cross_coding (*bm, cbm, match); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + JB2Shape &jshp=*xjshp; + match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + break; + } + case MATCHED_REFINE_IMAGE_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + JB2Shape &jshp=*xjshp; + match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + code_bitmap_by_cross_coding (*bm, cbm, match); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case MATCHED_COPY: + { + int temp; + if (encoding) temp = jblt->shapeno; + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + match = code_match_index (temp, jim); + if (!encoding) jblt->shapeno = temp; + bm = jim.get_shape(jblt->shapeno).bits; + LibRect &l = libinfo[match]; + jblt->left += l.left; + jblt->bottom += l.bottom; + if (jim.reproduce_old_bug) + code_relative_location (jblt, bm->rows(), bm->columns() ); + else + code_relative_location (jblt, l.top-l.bottom+1, l.right-l.left+1 ); + jblt->left -= l.left; + jblt->bottom -= l.bottom; + break; + } + case NON_MARK_DATA: + { + code_absolute_mark_size (*bm, 3); + code_bitmap_directly (*bm); + code_absolute_location (jblt, bm->rows(), bm->columns() ); + break; + } + case PRESERVED_COMMENT: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + code_comment(jim.comment); + break; + } + case REQUIRED_DICT_OR_RESET: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + if (! gotstartrecordp) + // Indicates need for a shape dictionary + code_inherited_shape_count(jim); + else + // Reset all numerical contexts to zero + reset_numcoder(); + break; + } + case END_OF_DATA: + { + break; + } + default: + { + G_THROW( ERR_MSG("JB2Image.unknown_type") ); + } + } + + // Post-coding action + if (!encoding) + { + // add shape to image + switch(rectype) + { + case NEW_MARK: + case NEW_MARK_LIBRARY_ONLY: + case NEW_MARK_IMAGE_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_LIBRARY_ONLY: + case MATCHED_REFINE_IMAGE_ONLY: + case NON_MARK_DATA: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + shapeno = gjim->add_shape(jshp); + shape2lib.touch(shapeno); + shape2lib[shapeno] = -1; + break; + } + } + // add shape to library + switch(rectype) + { + case NEW_MARK: + case NEW_MARK_LIBRARY_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_LIBRARY_ONLY: + if(!xjshp) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + add_library(shapeno, *xjshp); + break; + } + // make sure everything is compacted + // decompaction will occur automatically on cross-coding bitmaps + if (bm) + bm->compress(); + // add blit to image + switch (rectype) + { + case NEW_MARK: + case NEW_MARK_IMAGE_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_IMAGE_ONLY: + case NON_MARK_DATA: + jblt->shapeno = shapeno; + case MATCHED_COPY: + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + gjim->add_blit(* jblt); + break; + } + } +} + + +// CODE JB2IMAGE + +void +JB2Dict::JB2Codec::Decode::code(const GP<JB2Image> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + // ------------------------- + // THIS IS THE DECODING PART + // ------------------------- + int rectype; + JB2Blit tmpblit; + JB2Shape tmpshape; + do + { + code_record(rectype, gjim, &tmpshape, &tmpblit); + } + while(rectype!=END_OF_DATA); + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + jim.compress(); +} + + + +//////////////////////////////////////// +//// HELPERS +//////////////////////////////////////// + +void +JB2Dict::JB2Codec::LibRect::compute_bounding_box(const GBitmap &bm) +{ + // First lock the stuff. + GMonitorLock lock(bm.monitor()); + // Get size + const int w = bm.columns(); + const int h = bm.rows(); + const int s = bm.rowsize(); + // Right border + for(right=w-1;right >= 0;--right) + { + unsigned char const *p = bm[0] + right; + unsigned char const * const pe = p+(s*h); + for (;(p<pe)&&(!*p);p+=s) + continue; + if (p<pe) + break; + } + // Top border + for(top=h-1;top >= 0;--top) + { + unsigned char const *p = bm[top]; + unsigned char const * const pe = p+w; + for (;(p<pe)&&(!*p); ++p) + continue; + if (p<pe) + break; + } + // Left border + for (left=0;left <= right;++left) + { + unsigned char const *p = bm[0] + left; + unsigned char const * const pe=p+(s*h); + for (;(p<pe)&&(!*p);p+=s) + continue; + if (p<pe) + break; + } + // Bottom border + for(bottom=0;bottom <= top;++bottom) + { + unsigned char const *p = bm[bottom]; + unsigned char const * const pe = p+w; + for (;(p<pe)&&(!*p); ++p) + continue; + if (p<pe) + break; + } +} + +GP<JB2Dict> +JB2Dict::create(void) +{ + return new JB2Dict(); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/JB2Image.h b/kviewshell/plugins/djvu/libdjvu/JB2Image.h new file mode 100644 index 00000000..f55ae451 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JB2Image.h @@ -0,0 +1,805 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JB2Image.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _JB2IMAGE_H +#define _JB2IMAGE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name JB2Image.h + + Files #"JB2Image.h"# and #"JB2Image.cpp"# address the compression of + bilevel images using the JB2 soft pattern matching scheme. These files + provide the complete decoder and the decoder back-end. The JB2 scheme is + optimized for images containing a large number of self-similar small + components such as characters. Typical text images can be compressed into + files 3 to 5 times smaller than with G4/MMR and 2 to 4 times smaller than + with JBIG1. + + {\bf JB2 and JBIG2} --- JB2 has strong similarities with the forthcoming + JBIG2 standard developed by the "ISO/IEC JTC1 SC29 Working Group 1" which + is responsible for both the JPEG and JBIG standards. This is hardly + surprising since JB2 was our own proposal for the JBIG2 standard + and remained the only proposal for years. The full JBIG2 standard however + is significantly more complex and slighlty less efficient than JB2 because + it addresses a broader range of applications. Full JBIG2 compliance may + be implemented in the future. + + {\bf JB2 Images} --- Class \Ref{JB2Image} is the central data structure + implemented here. A #JB2Image# is composed of an array of shapes + and an array of blits. Each shape contains a small bitmap representing an + elementary blob of ink, such as a character or a segment of line art. + Each blit instructs the decoder to render a particular shape at a + specified position in the image. Some compression is already achieved + because several blits can refer to the same shape. A shape can also + contain a pointer to a parent shape. Additional compression is achieved + when both shapes are similar because each shape is encoded using the + parent shape as a model. A #"O"# shape for instance could be a parent for + both a #"C"# shape and a #"Q"# shape. + + {\bf JB2 Dictionary} --- Class \Ref{JB2Dict} is a peculiar kind of + JB2Image which only contains an array of shapes. These shapes can be + referenced from another JB2Dict/JB2Image. This is arranged by setting the + ``inherited dictionary'' of a JB2Dict/JB2Image using function + \Ref{JB2Dict::set_inherited_dict}. Several JB2Images can use shapes from a + same JB2Dict encoded separately. This is how several pages of a same + document can share information. + + {\bf Decoding JB2 data} --- The first step for decoding JB2 data consists of + creating an empty #JB2Image# object. Function \Ref{JB2Image::decode} then + reads the data and populates the #JB2Image# with the shapes and the blits. + Function \Ref{JB2Image::get_bitmap} finally produces an anti-aliased image. + + {\bf Encoding JB2 data} --- The first step for decoding JB2 data also + consists of creating an empty #JB2Image# object. You must then use + functions \Ref{JB2Image::add_shape} and \Ref{JB2Image::add_blit} to + populate the #JB2Image# object. Function \Ref{JB2Image::encode} finally + produces the JB2 data. Function #encode# sequentially encodes the blits + and the necessary shapes. The compression ratio depends on several + factors: + \begin{itemize} + \item Blits should reuse shapes as often as possible. + \item Blits should be sorted in reading order because this facilitates + the prediction of the blit coordinates. + \item Shapes should be sorted according to the order of first appearance + in the sequence of blits because this facilitates the prediction of the + shape indices. + \item Shapes should be compared to all previous shapes in the shape array. + The shape parent pointer should be set to a suitable parent shape if + such a parent shape exists. The parent shape should have almost the + same size and the same pixels. + \end{itemize} + All this is quite easy to achieve in the case of an electronically + produced document such as a DVI file or a PS file: we know what the + characters are and where they are located. If you only have a scanned + image however you must first locate the characters (connected component + analysis) and cut the remaining pieces of ink into smaller blobs. + Ordering the blits and matching the shapes is then an essentially + heuristic process. Although the quality of the heuristics substantially + effects the file size, misordering blits or mismatching shapes never + effects the quality of the image. The last refinement consists in + smoothing the shapes in order to reduce the noise and maximize the + similarities between shapes. + + {\bf JB2 extensions} --- Two extensions of the JB2 + encoding format have been introduced with DjVu files version 21. The first + extension addresses the shared shape dictionaries. The second extension + bounds the number of probability contexts used for coding numbers. + Both extensions maintain backward compatibility with JB2 as + described in the ICFDD proposal. A more complete discussion + can be found in section \Ref{JB2 extensions for version 21.}. + + {\bf References} + \begin{itemize} + \item Paul G. Howard : {\em Text image compression using soft + pattern matching}, Computer Journal, volume 40:2/3, 1997. + \item JBIG1 : \URL{http://www.jpeg.org/public/jbighomepage.htm}. + \item JBIG2 draft : \URL{http://www.jpeg.org/public/jbigpt2.htm}. + \item ICFDD Draft Proposed American National Standard, 1999-08-26. + \end{itemize} + + @version + #$Id: JB2Image.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# + @memo + Coding bilevel images with JB2. + @author + Paul Howard <pgh@research.att.com> -- JB2 design\\ + L\'eon Bottou <leonb@research.att.com> -- this implementation + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +*/ +//@{ + + +#include "GString.h" +#include "ZPCodec.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class JB2Dict; +class JB2Image; +class GRect; +class GBitmap; +class ByteStream; + +/** Blit data structure. A #JB2Image# contains an array of #JB2Blit# data + structures. Each array entry instructs the decoder to render a particular + shape at a particular location. Members #left# and #bottom# specify the + coordinates of the bottom left corner of the shape bitmap. All + coordinates are relative to the bottom left corner of the image. Member + #shapeno# is the subscript of the shape to be rendered. */ + +class JB2Blit { +public: + /** Horizontal coordinate of the blit. */ + unsigned short left; + /** Vertical coordinate of the blit. */ + unsigned short bottom; + /** Index of the shape to blit. */ + unsigned int shapeno; +}; + + +/** Shape data structure. A #JB2Image# contains an array of #JB2Shape# data + structures. Each array entry represents an elementary blob of ink such as + a character or a segment of line art. Member #bits# points to a bilevel + image representing the shape pixels. Member #parent# is the subscript of + the parent shape. */ + +class JB2Shape +{ +public: + /** Subscript of the parent shape. The parent shape must always be located + before the current shape in the shape array. A negative value indicates + that this shape has no parent. Any negative values smaller than #-1# + further indicates that this shape does not look like a character. This + is used to enable a few internal optimizations. This information is + saved into the JB2 file, but the actual value of the #parent# variable + is not. */ + int parent; + /** Bilevel image of the shape pixels. This must be a pointer to a bilevel + #GBitmap# image. This pointer can also be null. The encoder will just + silently discard all blits referring to a shape containing a null + bitmap. */ + GP<GBitmap> bits; + /** Private user data. This long word is provided as a convenience for users + of the JB2Image data structures. Neither the rendering functions nor + the coding functions ever access this value. */ + long userdata; +}; + + + +/** JB2 Dictionary callback. + The decoding function call this callback function when they discover that + the current JB2Image or JB2Dict needs a pre-existing shape dictionary. + The callback function must return a pointer to the dictionary or NULL + if none is found. */ + +typedef GP<JB2Dict> JB2DecoderCallback ( void* ); + + +/** Dictionary of JB2 shapes. */ + +class JB2Dict : public GPEnabled +{ +protected: + JB2Dict(void); +public: + class JB2Codec; + + // CONSTRUCTION + /** Default creator. Constructs an empty #JB2Dict# object. You can then + call the decoding function #decode#. You can also manually set the + image size using #add_shape#. */ + static GP<JB2Dict> create(void); + + // INITIALIZATION + /** Resets the #JB2Image# object. This function reinitializes both the shape + and the blit arrays. All allocated memory is freed. */ + void init(void); + + // INHERITED + /** Returns the inherited dictionary. */ + GP<JB2Dict> get_inherited_dict(void) const; + /** Returns the number of inherited shapes. */ + int get_inherited_shape_count(void) const; + /** Sets the inherited dictionary. */ + void set_inherited_dict(const GP<JB2Dict> &dict); + + // ACCESSING THE SHAPE LIBRARY + /** Returns the total number of shapes. + Shape indices range from #0# to #get_shape_count()-1#. */ + int get_shape_count(void) const; + /** Returns a pointer to shape #shapeno#. + The returned pointer directly points into the shape array. + This pointer can be used for reading or writing the shape data. */ + JB2Shape &get_shape(const int shapeno); + /** Returns a constant pointer to shape #shapeno#. + The returned pointer directly points into the shape array. + This pointer can only be used for reading the shape data. */ + const JB2Shape &get_shape(const int shapeno) const; + /** Appends a shape to the shape array. This function appends a copy of + shape #shape# to the shape array and returns the subscript of the new + shape. The subscript of the parent shape #shape.parent# must + actually designate an already existing shape. */ + int add_shape(const JB2Shape &shape); + + // MEMORY OPTIMIZATION + /** Compresses all shape bitmaps. This function reduces the memory required + by the #JB2Image# by calling \Ref{GBitmap::compress} on all shapes + bitmaps. This function is best called after decoding a #JB2Image#, + because function \Ref{get_bitmap} can directly use the compressed + bitmaps. */ + void compress(void); + /** Returns the total memory used by the JB2Image. + The returned value is expressed in bytes. */ + unsigned int get_memory_usage(void) const; + + // CODING + /** Encodes the JB2Dict into ByteStream #bs#. + This function generates the JB2 data stream without any header. */ + void encode(const GP<ByteStream> &gbs) const; + /** Decodes JB2 data from ByteStream #bs#. This function decodes the image + size and populates the shape and blit arrays. The callback function + #cb# is called when the decoder determines that the ByteStream data + requires a shape dictionary which has not been set with + \Ref{JB2Dict::set_inherited_dict}. The callback receives argument #arg# + and must return a suitable dictionary which will be installed as the + inherited dictionary. The callback should return null if no such + dictionary is found. */ + void decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb=0, void *arg=0); + + +public: + /** Comment string coded by JB2 file. */ + GUTF8String comment; + +private: + int inherited_shapes; + GP<JB2Dict> inherited_dict; + GArray<JB2Shape> shapes; + +}; + +/** Main JB2 data structure. Each #JB2Image# consists of an array of shapes + and an array of blits. These arrays can be populated by hand using + functions \Ref{add_shape} and \Ref{add_blit}, or by decoding JB2 data + using function \Ref{decode}. You can then use function \Ref{get_bitmap} + to render anti-aliased images, or use function \Ref{encode} to generate + JB2 data. */ + +class JB2Image : public JB2Dict +{ +protected: + JB2Image(void); +public: + + /** Creates an empty #JB2Image# object. You can then + call the decoding function #decode#. You can also manually set the + image size using #set_dimension# and populate the shape and blit arrays + using #add_shape# and #add_blit#. */ + static GP<JB2Image> create(void) { return new JB2Image(); } + + // INITIALIZATION + /** Resets the #JB2Image# object. This function reinitializes both the shape + and the blit arrays. All allocated memory is freed. */ + void init(void); + + // DIMENSION + /** Returns the width of the image. + This is the width value previously set with #set_dimension#. */ + int get_width(void) const; + /** Returns the height of the image. + This is the height value previously set with #set_dimension#. */ + int get_height(void) const; + /** Sets the size of the JB2Image. + This function can be called at any time. + The corresponding #width# and the #height# are stored + in the JB2 file. */ + void set_dimension(int width, int height); + + // RENDERING + /** Renders an anti-aliased gray level image. This function renders the + JB2Image as a bilevel or gray level image. Argument #subsample# + specifies the desired subsampling ratio in range #1# to #15#. The + returned image uses #1+subsample^2# gray levels for representing + anti-aliased edges. Argument #align# specified the alignment of the + rows of the returned images. Setting #align# to #4#, for instance, will + adjust the bitmap border in order to make sure that each row of the + returned image starts on a word (four byte) boundary. */ + GP<GBitmap> get_bitmap(int subsample = 1, int align = 1) const; + /** Renders an anti-aliased gray level sub-image. This function renders a + segment of the JB2Image as a bilevel or gray level image. Conceptually, + this function first renders the full JB2Image with subsampling ratio + #subsample# and then extracts rectangle #rect# in the subsampled image. + Both operations of course are efficiently performed simultaneously. + Argument #align# specified the alignment of the rows of the returned + images, as explained above. Argument #dispy# should remain null. */ + GP<GBitmap> get_bitmap(const GRect &rect, int subsample=1, int align=1, int dispy=0) const; + + // ACCESSING THE BLIT LIBRARY + /** Returns the total number of blits. + Blit indices range from #0# to #get_blit_count(void)-1#. */ + int get_blit_count(void) const; + /** Returns a pointer to blit #blitno#. + The returned pointer directly points into the blit array. + This pointer can be used for reading or writing the blit data. */ + JB2Blit *get_blit(int blitno); + /** Returns a constant pointer to blit #blitno#. + The returned pointer directly points into the shape array. + This pointer can only be used for reading the shape data. */ + const JB2Blit *get_blit(int blitno) const; + /** Appends a blit to the blit array. This function appends a copy of blit + #blit# to the blit array and returns the subscript of the new blit. The + shape subscript #blit.shapeno# must actually designate an already + existing shape. */ + int add_blit(const JB2Blit &blit); + + // MEMORY OPTIMIZATION + /** Returns the total memory used by the JB2Image. + The returned value is expressed in bytes. */ + unsigned int get_memory_usage(void) const; + + // CODING + /** Encodes the JB2Image into ByteStream #bs#. + This function generates the JB2 data stream without any header. */ + void encode(const GP<ByteStream> &gbs) const; + /** Decodes JB2 data from ByteStream #bs#. This function decodes the image + size and populates the shape and blit arrays. The callback function + #cb# is called when the decoder determines that the ByteStream data + requires a shape dictionary which has not been set with + \Ref{JB2Dict::set_inherited_dict}. The callback receives argument #arg# + and must return a suitable dictionary which will be installed as the + inherited dictionary. The callback should return null if no such + dictionary is found. */ + void decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb=0, void *arg=0); + +private: + // Implementation + int width; + int height; + GTArray<JB2Blit> blits; +public: + /** Reproduces a old bug. Setting this flag may be necessary for accurately + decoding DjVu files with version smaller than #18#. The default value + is of couse #false#. */ + bool reproduce_old_bug; +}; + + + +// JB2DICT INLINE FUNCTIONS + +inline int +JB2Dict::get_shape_count(void) const +{ + return inherited_shapes + shapes.size(); +} + +inline int +JB2Dict::get_inherited_shape_count(void) const +{ + return inherited_shapes; +} + +inline GP<JB2Dict> +JB2Dict::get_inherited_dict(void) const +{ + return inherited_dict; +} + +// JB2IMAGE INLINE FUNCTIONS + +inline int +JB2Image::get_width(void) const +{ + return width; +} + +inline int +JB2Image::get_height(void) const +{ + return height; +} + + +inline int +JB2Image::get_blit_count(void) const +{ + return blits.size(); +} + + +inline JB2Blit * +JB2Image::get_blit(int blitno) +{ + return & blits[blitno]; +} + +inline const JB2Blit * +JB2Image::get_blit(int blitno) const +{ + return & blits[blitno]; +} + + + + + +/** @name JB2 extensions for version 21. + + Two extensions of the JB2 encoding format have been introduced + with DjVu files version 21. Both extensions maintain significant + backward compatibility with previous version of the JB2 format. + These extensions are described below by reference to the ICFDD + proposal dated August 1999. Both extension make use of the unused + record type value #9# (cf. ICFDD page 24) which has been renamed + #REQUIRED_DICT_OR_RESET#. + + {\bf Shared Shape Dictionaries} --- This extension provides + support for sharing symbol definitions between the pages of a + document. To achieve this objective, the JB2 image data chunk + must be able to address symbols defined elsewhere by a JB2 + dictionary data chunk shared by all the pages of a document. + + The arithmetically encoded JB2 image data logically consist of a + sequence of records. The decoder processes these records in + sequence and maintains a library of symbols which can be addressed + by the following records. The first record usually is a ``Start + Of Image'' record describing the size of the image. + + Starting with version 21, a #REQUIRED_DICT_OR_RESET# (9) record + type can appear {\em before} the #START_OF_DATA# (0) record. The + record type field is followed by a single number arithmetically + encoded (cf. ICFDD page 26) using a sixteenth context (cf. ICFDD + page 25). This record appears when the JB2 data chunk requires + symbols encoded in a separate JB2 dictionary data chunk. The + number (the {\bf dictionary size}) indicates how many symbols + should have been defined by the JB2 dictionary data chunk. The + decoder should simply load these symbols in the symbol library and + proceed as usual. New symbols potentially defined by the + subsequent JB2 image data records will therefore be numbered with + integers greater or equal than the dictionary size. + + The JB2 dictionary data format is a pure subset of the JB2 image + data format. The #START_OF_DATA# (0) record always specifies an + image width of zero and an image height of zero. The only allowed + record types are those defining library symbols only + (#NEW_SYMBOL_LIBRARY_ONLY# (2) and #MATCHED_REFINE_LIBRARY_ONLY# + (5) cf. ICFDD page 24) followed by a final #END_OF_DATA# (11) + record. + + The JB2 dictionary data is usually located in an {\bf Djbz} chunk. + Each page {\bf FORM:DJVU} may directly contain a {\bf Djbz} chunk, + or may indirectly point to such a chunk using an {\bf INCL} chunk + (cf. \Ref{Multipage DjVu documents.}). + + + {\bf Numcoder Reset} --- This extension addresses a problem for + hardware implementations. The encoding of numbers (cf. ICFDD page + 26) potentially uses an unbounded number of binary coding + contexts. These contexts are normally allocated when they are used + for the first time (cf. ICFDD informative note, page 27). + + Starting with version 21, a #REQUIRED_DICT_OR_RESET# (9) record + type can appear {\em after} the #START_OF_DATA# (0) record. The + decoder should proceed with the next record after {\em clearing + all binary contexts used for coding numbers}. This operation + implies that all binary contexts previously allocated for coding + numbers can be deallocated. + + Starting with version 21, the JB2 encoder should insert a + #REQUIRED_DICT_OR_RESET# record type whenever the number of these + allocated binary contexts exceeds #20000#. Only very large + documents ever reach such a large number of allocated binary + contexts (e.g large maps). Hardware implementation however can + benefit greatly from a hard bound on the total number of binary + coding contexts. Old JB2 decoders will treat this record type as + an #END_OF_DATA# record and cleanly stop decoding (cf. ICFDD page + 30, Image refinement data). + + + {\bf References} --- + \begin{itemize} + \item ICFDD Draft Proposed American National Standard, 1999-08-26. + \item DjVu Specification, \URL{http://www.lizardtech.com/djvu/sci/djvuspec}. + \end{itemize} + + @memo Extensions to the JB2 format introduced in version 21. */ + +//@} + +//////////////////////////////////////// +//// CLASS JB2CODEC: DECLARATION +//////////////////////////////////////// + +// This class is accessed via the encode and decode +// functions of class JB2Image + + +//**** Class JB2Codec +// This class implements the base class for both the JB2 coder and decoder. +// The JB2Codec's Contains all contextual information for encoding/decoding +// a JB2Image. + +class JB2Dict::JB2Codec +{ +public: + class Decode; + class Encode; + typedef unsigned int NumContext; + struct LibRect + { + int top,left,right,bottom; + void compute_bounding_box(const GBitmap &cbm); + }; + virtual ~JB2Codec(); +protected: + // Constructors + JB2Codec(const bool xencoding=false); + // Forbidden assignment + JB2Codec(const JB2Codec &ref); + JB2Codec& operator=(const JB2Codec &ref); + + int CodeNum(int lo, int hi, NumContext *pctx,int v); + void reset_numcoder(void); + inline void code_eventual_lossless_refinement(void); + void init_library(JB2Dict &jim); + int add_library(const int shapeno, JB2Shape &jshp); + void code_relative_location(JB2Blit *jblt, int rows, int columns); + void code_bitmap_directly (GBitmap &bm); + void code_bitmap_by_cross_coding (GBitmap &bm, GP<GBitmap> &cbm, const int libno); + void code_record(int &rectype, const GP<JB2Dict> &jim, JB2Shape *jshp); + void code_record(int &rectype, const GP<JB2Image> &jim, JB2Shape *jshp, JB2Blit *jblt); + static void compute_bounding_box(GBitmap &cbm, LibRect &lrect); + static int get_direct_context( unsigned char const * const up2, + unsigned char const * const up1, unsigned char const * const up0, + const int column); + static int shift_direct_context ( const int context, + const int next, unsigned char const * const up2, + unsigned char const * const up1, unsigned char const * const up0, + const int column); + static int get_cross_context( unsigned char const * const up1, + unsigned char const * const up0, unsigned char const * const xup1, + unsigned char const * const xup0, unsigned char const * const xdn1, + const int column ); + static int shift_cross_context( const int context, + const int n, unsigned char const * const up1, + unsigned char const * const up0, unsigned char const * const xup1, + unsigned char const * const xup0, unsigned char const * const xdn1, + const int column ); + + virtual bool CodeBit(const bool bit, BitContext &ctx) = 0; + virtual void code_comment(GUTF8String &comment) = 0; + virtual void code_record_type(int &rectype) = 0; + virtual int code_match_index(int &index, JB2Dict &jim)=0; + virtual void code_inherited_shape_count(JB2Dict &jim)=0; + virtual void code_image_size(JB2Dict &jim); + virtual void code_image_size(JB2Image &jim); + virtual void code_absolute_location(JB2Blit *jblt, int rows, int columns)=0; + virtual void code_absolute_mark_size(GBitmap &bm, int border=0) = 0; + virtual void code_relative_mark_size(GBitmap &bm, int cw, int ch, int border=0) = 0; + virtual void code_bitmap_directly(GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 )=0; + virtual void code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 )=0; + // Code records + virtual int get_diff(const int x_diff,NumContext &rel_loc) = 0; + +private: + bool encoding; + +protected: + // NumCoder + int cur_ncell; + BitContext *bitcells; + GPBuffer<BitContext> gbitcells; + NumContext *leftcell; + GPBuffer<NumContext> gleftcell; + NumContext *rightcell; + GPBuffer<NumContext> grightcell; + // Info + bool refinementp; + char gotstartrecordp; + // Code comment + NumContext dist_comment_byte; + NumContext dist_comment_length; + // Code values + NumContext dist_record_type; + NumContext dist_match_index; + BitContext dist_refinement_flag; + // Library + GTArray<int> shape2lib; + GTArray<int> lib2shape; + GTArray<LibRect> libinfo; + // Code pairs + NumContext abs_loc_x; + NumContext abs_loc_y; + NumContext abs_size_x; + NumContext abs_size_y; + NumContext image_size_dist; + NumContext inherited_shape_count_dist; + BitContext offset_type_dist; + NumContext rel_loc_x_current; + NumContext rel_loc_x_last; + NumContext rel_loc_y_current; + NumContext rel_loc_y_last; + NumContext rel_size_x; + NumContext rel_size_y; + int last_bottom; + int last_left; + int last_right; + int last_row_bottom; + int last_row_left; + int image_columns; + int image_rows; + int short_list[3]; + int short_list_pos; + inline void fill_short_list(const int v); + int update_short_list(const int v); + // Code bitmaps + BitContext bitdist[1024]; + BitContext cbitdist[2048]; +}; + +inline void +JB2Dict::JB2Codec::code_eventual_lossless_refinement(void) +{ + refinementp=CodeBit(refinementp, dist_refinement_flag); +} + +inline void +JB2Dict::JB2Codec::fill_short_list(const int v) +{ + short_list[0] = short_list[1] = short_list[2] = v; + short_list_pos = 0; +} + +inline int +JB2Dict::JB2Codec::get_direct_context( unsigned char const * const up2, + unsigned char const * const up1, + unsigned char const * const up0, + const int column) +{ + return ( (up2[column - 1] << 9) | + (up2[column ] << 8) | + (up2[column + 1] << 7) | + (up1[column - 2] << 6) | + (up1[column - 1] << 5) | + (up1[column ] << 4) | + (up1[column + 1] << 3) | + (up1[column + 2] << 2) | + (up0[column - 2] << 1) | + (up0[column - 1] << 0) ); +} + +inline int +JB2Dict::JB2Codec::shift_direct_context(const int context, const int next, + unsigned char const * const up2, + unsigned char const * const up1, + unsigned char const * const up0, + const int column) +{ + return ( ((context << 1) & 0x37a) | + (up1[column + 2] << 2) | + (up2[column + 1] << 7) | + (next << 0) ); +} + +inline int +JB2Dict::JB2Codec::get_cross_context( unsigned char const * const up1, + unsigned char const * const up0, + unsigned char const * const xup1, + unsigned char const * const xup0, + unsigned char const * const xdn1, + const int column ) +{ + return ( ( up1[column - 1] << 10) | + ( up1[column ] << 9) | + ( up1[column + 1] << 8) | + ( up0[column - 1] << 7) | + (xup1[column ] << 6) | + (xup0[column - 1] << 5) | + (xup0[column ] << 4) | + (xup0[column + 1] << 3) | + (xdn1[column - 1] << 2) | + (xdn1[column ] << 1) | + (xdn1[column + 1] << 0) ); +} + +inline int +JB2Dict::JB2Codec::shift_cross_context( const int context, const int n, + unsigned char const * const up1, + unsigned char const * const up0, + unsigned char const * const xup1, + unsigned char const * const xup0, + unsigned char const * const xdn1, + const int column ) +{ + return ( ((context<<1) & 0x636) | + ( up1[column + 1] << 8) | + (xup1[column ] << 6) | + (xup0[column + 1] << 3) | + (xdn1[column + 1] << 0) | + (n << 7) ); +} + +// ---------- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.cpp b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.cpp new file mode 100644 index 00000000..3f611965 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.cpp @@ -0,0 +1,413 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JPEGDecoder.cpp,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#ifdef NEED_JPEG_DECODER + +#include "JPEGDecoder.h" + +#ifdef __cplusplus +extern "C" { +#endif +#undef HAVE_STDLIB_H +#undef HAVE_STDDEF_H +#include <stdio.h> +#include <jconfig.h> +#include <jpeglib.h> +#include <jerror.h> +#ifdef __cplusplus +} +#endif + +#include "ByteStream.h" +#include "GPixmap.h" +#ifdef LIBJPEGNAME +#include "DjVuDynamic.h" +#include "GString.h" +#endif // LIBJPEGNAME + + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class JPEGDecoder::Impl : public JPEGDecoder +{ +public: + static void jpeg_byte_stream_src(j_decompress_ptr, ByteStream &); +}; + +extern "C" +{ + +struct djvu_error_mgr +{ + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct djvu_error_mgr * djvu_error_ptr; + +METHODDEF(void) +djvu_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to a djvu_error_mgr struct, so coerce pointer */ + djvu_error_ptr djvuerr = (djvu_error_ptr) cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message) (cinfo); + + /* Return control to the setjmp point */ + longjmp(djvuerr->setjmp_buffer, 1); +} + +} + +GP<GPixmap> +JPEGDecoder::decode(ByteStream & bs ) +{ + GP<GPixmap> retval=GPixmap::create(); + G_TRY + { + decode(bs,*retval); + } G_CATCH_ALL + { + retval=0; + } + G_ENDCATCH; + return retval; +} + +void +JPEGDecoder::decode(ByteStream & bs,GPixmap &pix) +{ + struct jpeg_decompress_struct cinfo; + + /* We use our private extension JPEG error handler. */ + struct djvu_error_mgr jerr; + + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + int full_buf_size; + int isGrey,i; + + cinfo.err = jpeg_std_error(&jerr.pub); + + jerr.pub.error_exit = djvu_error_exit; + + if (setjmp(jerr.setjmp_buffer)) + { + + jpeg_destroy_decompress(&cinfo); + G_THROW( ERR_MSG("GPixmap.unk_PPM") ); + } + + jpeg_create_decompress(&cinfo); + + Impl::jpeg_byte_stream_src(&cinfo, bs); + + (void) jpeg_read_header(&cinfo, TRUE); + + jpeg_start_decompress(&cinfo); + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + full_buf_size = row_stride * cinfo.output_height; + + /* Make a one-row-high sample array that will go away when done with image */ + buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + GP<ByteStream> goutputBlock=ByteStream::create(); + ByteStream &outputBlock=*goutputBlock; + outputBlock.format("P6\n%d %d\n%d\n",cinfo.output_width, + cinfo.output_height,255); + + isGrey = ( cinfo.out_color_space == JCS_GRAYSCALE) ? 1 : 0; + + while (cinfo.output_scanline < cinfo.output_height) + { + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + + if ( isGrey == 1 ) + { + for (i=0; i<row_stride; i++) + { + outputBlock.write8((char)buffer[0][i]); + outputBlock.write8((char)buffer[0][i]); + outputBlock.write8((char)buffer[0][i]); + } + }else + { + for (i=0; i<row_stride; i++) + outputBlock.write8((char)buffer[0][i]); + } + } + + (void) jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + + outputBlock.seek(0,SEEK_SET); + + pix.init(outputBlock); +} + +/*** From here onwards code is to make ByteStream as the data + source for the JPEG library */ + +extern "C" +{ + +typedef struct +{ + struct jpeg_source_mgr pub; /* public fields */ + + ByteStream * byteStream; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_stream; +} byte_stream_src_mgr; + + +typedef byte_stream_src_mgr * byte_stream_src_ptr; + +#define INPUT_BUF_SIZE 4096 + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + byte_stream_src_ptr src = (byte_stream_src_ptr) cinfo->src; + + src->start_of_stream = TRUE; +} + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + byte_stream_src_ptr src = (byte_stream_src_ptr) cinfo->src; + size_t nbytes; + + nbytes = src->byteStream->readall(src->buffer, INPUT_BUF_SIZE); + + if (nbytes <= 0) + { + if (src->start_of_stream) /* Treat empty input as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_stream = FALSE; + + return TRUE; +} + + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + byte_stream_src_ptr src = (byte_stream_src_ptr) cinfo->src; + + if (num_bytes > (long) src->pub.bytes_in_buffer) + { + src->byteStream->seek((num_bytes - src->pub.bytes_in_buffer), SEEK_CUR); + (void) fill_input_buffer(cinfo); + }else + { + src->pub.bytes_in_buffer -= num_bytes; + src->pub.next_input_byte += num_bytes; + } +} + +METHODDEF(void) +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + +} + +void +JPEGDecoder::Impl::jpeg_byte_stream_src(j_decompress_ptr cinfo,ByteStream &bs) +{ + byte_stream_src_ptr src; + + if (cinfo->src == NULL) + { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(byte_stream_src_mgr)); + src = (byte_stream_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET)); + } + + src = (byte_stream_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->byteStream = &bs; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} + +#ifdef LIBJPEGNAME +void * +JPEGDecoder::jpeg_lookup(const GUTF8String &name) +{ + static DjVuDynamic lib(GUTF8String(LIBJPEGNAME)); + void *sym=lib.lookup(name); + if(!sym) + G_THROW(ERR_MSG("DjVuFile.JPEG_bg2")); + return sym; +} + +jpeg_error_mgr * +JPEGDecoder::jpeg_std_error(jpeg_error_mgr *x) +{ + static void *sym=jpeg_lookup("jpeg_std_error"); + return ((jpeg_error_mgr *(*)(jpeg_error_mgr *))sym)(x); +} + +void +JPEGDecoder::jpeg_CreateDecompress(jpeg_decompress_struct *x,int v, size_t s) +{ + static void *sym=jpeg_lookup("jpeg_CreateDecompress"); + ((void (*)(jpeg_decompress_struct *,int,size_t))sym)(x,v,s); +} + +void +JPEGDecoder::jpeg_destroy_decompress(j_decompress_ptr x) +{ + static void *sym=jpeg_lookup("jpeg_destroy_decompress"); + ((void (*)(j_decompress_ptr))sym)(x); +} + +int +JPEGDecoder::jpeg_read_header(j_decompress_ptr x,boolean y) +{ + static void *sym=jpeg_lookup("jpeg_read_header"); + return ((int (*)(j_decompress_ptr,boolean))sym)(x,y); +} + +JDIMENSION +JPEGDecoder::jpeg_read_scanlines(j_decompress_ptr x,JSAMPARRAY y,JDIMENSION z) +{ + static void *sym=jpeg_lookup("jpeg_read_scanlines"); + return ((JDIMENSION (*)(j_decompress_ptr,JSAMPARRAY,JDIMENSION))sym)(x,y,z); +} + +boolean +JPEGDecoder::jpeg_finish_decompress(j_decompress_ptr x) +{ + static void *sym=jpeg_lookup("jpeg_finish_decompress"); + return ((boolean (*)(j_decompress_ptr))sym)(x); +} + +boolean +JPEGDecoder::jpeg_resync_to_restart(jpeg_decompress_struct *x,int d) +{ + static void *sym=jpeg_lookup("jpeg_resync_to_restart"); + return ((boolean (*)(jpeg_decompress_struct *,int))sym)(x,d); +} + +boolean +JPEGDecoder::jpeg_start_decompress(j_decompress_ptr x) +{ + static void *sym=jpeg_lookup("jpeg_start_decompress"); + return ((boolean (*)(j_decompress_ptr))sym)(x); +} + +#endif // LIBJPEGNAME + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.h b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.h new file mode 100644 index 00000000..8a0ace5d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.h @@ -0,0 +1,133 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JPEGDecoder.h,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _JPEGDECODER_H_ +#define _JPEGDECODER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#ifdef NEED_JPEG_DECODER + +#include <string.h> +#include <setjmp.h> + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class GPixmap; + + +/** @name JPEGDecoder.h + Files #"JPEGDecoder.h"# and #"JPEGDecoder.cpp"# implement an + interface to the decoding subset of the IJG JPEG library. + @memo + Decoding interface to the IJG JPEG library. + @version + #$Id: JPEGDecoder.h,v 1.8 2003/11/07 22:08:22 leonb Exp $# + @author + Parag Deshmukh <parag@sanskrit.lz.att.com> +*/ +//@{ + +class GUTF8String; + +/** This class ensures namespace isolation. */ +class JPEGDecoder +{ +public: + class Impl; + + /** Decodes the JPEG formated ByteStream */ + static GP<GPixmap> decode(ByteStream & bs); + static void decode(ByteStream & bs,GPixmap &pix); +#ifdef LIBJPEGNAME + static void *jpeg_lookup(const GUTF8String &name); + static jpeg_error_mgr *jpeg_std_error(jpeg_error_mgr *x); + static void jpeg_CreateDecompress(jpeg_decompress_struct *x,int v, size_t s); + static void jpeg_destroy_decompress(j_decompress_ptr x); + static int jpeg_read_header(j_decompress_ptr x,boolean y); + static JDIMENSION jpeg_read_scanlines(j_decompress_ptr x,JSAMPARRAY y,JDIMENSION z); + static boolean jpeg_finish_decompress(j_decompress_ptr x); + static boolean jpeg_resync_to_restart(jpeg_decompress_struct *x,int d); + static boolean jpeg_start_decompress(j_decompress_ptr x); +#endif // LIBJPEGNAME +}; + + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif // NEED_JPEG_DECODER +#endif // _JPEGDECODER_H_ + diff --git a/kviewshell/plugins/djvu/libdjvu/MMRDecoder.cpp b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.cpp new file mode 100644 index 00000000..b14220d1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.cpp @@ -0,0 +1,961 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMRDecoder.cpp,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "MMRDecoder.h" +#include "JB2Image.h" +#include "ByteStream.h" +#include "GBitmap.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ---------------------------------------- +// MMR CODEBOOKS + +static const char invalid_mmr_data[]= ERR_MSG("MMRDecoder.bad_data"); + +struct VLCode +{ + unsigned short code; + short codelen; + short value; +}; + +enum MMRMode +{ + P=0, H=1, V0=2, VR1=3, VR2=4, VR3=5, VL1=6, VL2=7, VL3=8 +}; + +static const VLCode mrcodes[] = +{ // Codes on 7 bits + // 7 bit codes + { 0x08, 4, P }, // 0001 + { 0x10, 3, H }, // 001 + { 0x40, 1, V0 }, // 1 + { 0x30, 3, VR1 }, // 011 + { 0x06, 6, VR2 }, // 000011 + { 0x03, 7, VR3 }, // 0000011 + { 0x20, 3, VL1 }, // 010 + { 0x04, 6, VL2 }, // 000010 + { 0x02, 7, VL3 }, // 0000010 + { 0x00, 0, -1 } // Illegal entry +}; + + +static const VLCode wcodes[] = { + // 13 bit codes + { 0x06a0, 8, 0 }, // 00110101 + { 0x0380, 6, 1 }, // 000111 + { 0x0e00, 4, 2 }, // 0111 + { 0x1000, 4, 3 }, // 1000 + { 0x1600, 4, 4 }, // 1011 + { 0x1800, 4, 5 }, // 1100 + { 0x1c00, 4, 6 }, // 1110 + { 0x1e00, 4, 7 }, // 1111 + { 0x1300, 5, 8 }, // 10011 + { 0x1400, 5, 9 }, // 10100 + { 0x0700, 5, 10 }, // 00111 + { 0x0800, 5, 11 }, // 01000 + { 0x0400, 6, 12 }, // 001000 + { 0x0180, 6, 13 }, // 000011 + { 0x1a00, 6, 14 }, // 110100 + { 0x1a80, 6, 15 }, // 110101 + { 0x1500, 6, 16 }, // 101010 + { 0x1580, 6, 17 }, // 101011 + { 0x09c0, 7, 18 }, // 0100111 + { 0x0300, 7, 19 }, // 0001100 + { 0x0200, 7, 20 }, // 0001000 + { 0x05c0, 7, 21 }, // 0010111 + { 0x00c0, 7, 22 }, // 0000011 + { 0x0100, 7, 23 }, // 0000100 + { 0x0a00, 7, 24 }, // 0101000 + { 0x0ac0, 7, 25 }, // 0101011 + { 0x04c0, 7, 26 }, // 0010011 + { 0x0900, 7, 27 }, // 0100100 + { 0x0600, 7, 28 }, // 0011000 + { 0x0040, 8, 29 }, // 00000010 + { 0x0060, 8, 30 }, // 00000011 + { 0x0340, 8, 31 }, // 00011010 + { 0x0360, 8, 32 }, // 00011011 + { 0x0240, 8, 33 }, // 00010010 + { 0x0260, 8, 34 }, // 00010011 + { 0x0280, 8, 35 }, // 00010100 + { 0x02a0, 8, 36 }, // 00010101 + { 0x02c0, 8, 37 }, // 00010110 + { 0x02e0, 8, 38 }, // 00010111 + { 0x0500, 8, 39 }, // 00101000 + { 0x0520, 8, 40 }, // 00101001 + { 0x0540, 8, 41 }, // 00101010 + { 0x0560, 8, 42 }, // 00101011 + { 0x0580, 8, 43 }, // 00101100 + { 0x05a0, 8, 44 }, // 00101101 + { 0x0080, 8, 45 }, // 00000100 + { 0x00a0, 8, 46 }, // 00000101 + { 0x0140, 8, 47 }, // 00001010 + { 0x0160, 8, 48 }, // 00001011 + { 0x0a40, 8, 49 }, // 01010010 + { 0x0a60, 8, 50 }, // 01010011 + { 0x0a80, 8, 51 }, // 01010100 + { 0x0aa0, 8, 52 }, // 01010101 + { 0x0480, 8, 53 }, // 00100100 + { 0x04a0, 8, 54 }, // 00100101 + { 0x0b00, 8, 55 }, // 01011000 + { 0x0b20, 8, 56 }, // 01011001 + { 0x0b40, 8, 57 }, // 01011010 + { 0x0b60, 8, 58 }, // 01011011 + { 0x0940, 8, 59 }, // 01001010 + { 0x0960, 8, 60 }, // 01001011 + { 0x0640, 8, 61 }, // 00110010 + { 0x0660, 8, 62 }, // 00110011 + { 0x0680, 8, 63 }, // 00110100 + { 0x1b00, 5, 64 }, // 11011 + { 0x1200, 5, 128 }, // 10010 + { 0x0b80, 6, 192 }, // 010111 + { 0x0dc0, 7, 256 }, // 0110111 + { 0x06c0, 8, 320 }, // 00110110 + { 0x06e0, 8, 384 }, // 00110111 + { 0x0c80, 8, 448 }, // 01100100 + { 0x0ca0, 8, 512 }, // 01100101 + { 0x0d00, 8, 576 }, // 01101000 + { 0x0ce0, 8, 640 }, // 01100111 + { 0x0cc0, 9, 704 }, // 011001100 + { 0x0cd0, 9, 768 }, // 011001101 + { 0x0d20, 9, 832 }, // 011010010 + { 0x0d30, 9, 896 }, // 011010011 + { 0x0d40, 9, 960 }, // 011010100 + { 0x0d50, 9, 1024 }, // 011010101 + { 0x0d60, 9, 1088 }, // 011010110 + { 0x0d70, 9, 1152 }, // 011010111 + { 0x0d80, 9, 1216 }, // 011011000 + { 0x0d90, 9, 1280 }, // 011011001 + { 0x0da0, 9, 1344 }, // 011011010 + { 0x0db0, 9, 1408 }, // 011011011 + { 0x0980, 9, 1472 }, // 010011000 + { 0x0990, 9, 1536 }, // 010011001 + { 0x09a0, 9, 1600 }, // 010011010 + { 0x0c00, 6, 1664 }, // 011000 (what did they think?) + { 0x09b0, 9, 1728 }, // 010011011 + { 0x0020, 11, 1792 }, // 00000001000 + { 0x0030, 11, 1856 }, // 00000001100 + { 0x0034, 11, 1920 }, // 00000001101 + { 0x0024, 12, 1984 }, // 000000010010 + { 0x0026, 12, 2048 }, // 000000010011 + { 0x0028, 12, 2112 }, // 000000010100 + { 0x002a, 12, 2176 }, // 000000010101 + { 0x002c, 12, 2240 }, // 000000010110 + { 0x002e, 12, 2304 }, // 000000010111 + { 0x0038, 12, 2368 }, // 000000011100 + { 0x003a, 12, 2432 }, // 000000011101 + { 0x003c, 12, 2496 }, // 000000011110 + { 0x003e, 12, 2560 }, // 000000011111 + { 0x0000, 0, -1 } // Illegal entry +}; + + +static const VLCode bcodes[] = { + // 13 bit codes + { 0x01b8, 10, 0 }, // 0000110111 + { 0x0800, 3, 1 }, // 010 + { 0x1800, 2, 2 }, // 11 + { 0x1000, 2, 3 }, // 10 + { 0x0c00, 3, 4 }, // 011 + { 0x0600, 4, 5 }, // 0011 + { 0x0400, 4, 6 }, // 0010 + { 0x0300, 5, 7 }, // 00011 + { 0x0280, 6, 8 }, // 000101 + { 0x0200, 6, 9 }, // 000100 + { 0x0100, 7, 10 }, // 0000100 + { 0x0140, 7, 11 }, // 0000101 + { 0x01c0, 7, 12 }, // 0000111 + { 0x0080, 8, 13 }, // 00000100 + { 0x00e0, 8, 14 }, // 00000111 + { 0x0180, 9, 15 }, // 000011000 + { 0x00b8, 10, 16 }, // 0000010111 + { 0x00c0, 10, 17 }, // 0000011000 + { 0x0040, 10, 18 }, // 0000001000 + { 0x019c, 11, 19 }, // 00001100111 + { 0x01a0, 11, 20 }, // 00001101000 + { 0x01b0, 11, 21 }, // 00001101100 + { 0x00dc, 11, 22 }, // 00000110111 + { 0x00a0, 11, 23 }, // 00000101000 + { 0x005c, 11, 24 }, // 00000010111 + { 0x0060, 11, 25 }, // 00000011000 + { 0x0194, 12, 26 }, // 000011001010 + { 0x0196, 12, 27 }, // 000011001011 + { 0x0198, 12, 28 }, // 000011001100 + { 0x019a, 12, 29 }, // 000011001101 + { 0x00d0, 12, 30 }, // 000001101000 + { 0x00d2, 12, 31 }, // 000001101001 + { 0x00d4, 12, 32 }, // 000001101010 + { 0x00d6, 12, 33 }, // 000001101011 + { 0x01a4, 12, 34 }, // 000011010010 + { 0x01a6, 12, 35 }, // 000011010011 + { 0x01a8, 12, 36 }, // 000011010100 + { 0x01aa, 12, 37 }, // 000011010101 + { 0x01ac, 12, 38 }, // 000011010110 + { 0x01ae, 12, 39 }, // 000011010111 + { 0x00d8, 12, 40 }, // 000001101100 + { 0x00da, 12, 41 }, // 000001101101 + { 0x01b4, 12, 42 }, // 000011011010 + { 0x01b6, 12, 43 }, // 000011011011 + { 0x00a8, 12, 44 }, // 000001010100 + { 0x00aa, 12, 45 }, // 000001010101 + { 0x00ac, 12, 46 }, // 000001010110 + { 0x00ae, 12, 47 }, // 000001010111 + { 0x00c8, 12, 48 }, // 000001100100 + { 0x00ca, 12, 49 }, // 000001100101 + { 0x00a4, 12, 50 }, // 000001010010 + { 0x00a6, 12, 51 }, // 000001010011 + { 0x0048, 12, 52 }, // 000000100100 + { 0x006e, 12, 53 }, // 000000110111 + { 0x0070, 12, 54 }, // 000000111000 + { 0x004e, 12, 55 }, // 000000100111 + { 0x0050, 12, 56 }, // 000000101000 + { 0x00b0, 12, 57 }, // 000001011000 + { 0x00b2, 12, 58 }, // 000001011001 + { 0x0056, 12, 59 }, // 000000101011 + { 0x0058, 12, 60 }, // 000000101100 + { 0x00b4, 12, 61 }, // 000001011010 + { 0x00cc, 12, 62 }, // 000001100110 + { 0x00ce, 12, 63 }, // 000001100111 + { 0x0078, 10, 64 }, // 0000001111 + { 0x0190, 12, 128 }, // 000011001000 + { 0x0192, 12, 192 }, // 000011001001 + { 0x00b6, 12, 256 }, // 000001011011 + { 0x0066, 12, 320 }, // 000000110011 + { 0x0068, 12, 384 }, // 000000110100 + { 0x006a, 12, 448 }, // 000000110101 + { 0x006c, 13, 512 }, // 0000001101100 + { 0x006d, 13, 576 }, // 0000001101101 + { 0x004a, 13, 640 }, // 0000001001010 + { 0x004b, 13, 704 }, // 0000001001011 + { 0x004c, 13, 768 }, // 0000001001100 + { 0x004d, 13, 832 }, // 0000001001101 + { 0x0072, 13, 896 }, // 0000001110010 + { 0x0073, 13, 960 }, // 0000001110011 + { 0x0074, 13, 1024 }, // 0000001110100 + { 0x0075, 13, 1088 }, // 0000001110101 + { 0x0076, 13, 1152 }, // 0000001110110 + { 0x0077, 13, 1216 }, // 0000001110111 + { 0x0052, 13, 1280 }, // 0000001010010 + { 0x0053, 13, 1344 }, // 0000001010011 + { 0x0054, 13, 1408 }, // 0000001010100 + { 0x0055, 13, 1472 }, // 0000001010101 + { 0x005a, 13, 1536 }, // 0000001011010 + { 0x005b, 13, 1600 }, // 0000001011011 + { 0x0064, 13, 1664 }, // 0000001100100 + { 0x0065, 13, 1728 }, // 0000001100101 + { 0x0020, 11, 1792 }, // 00000001000 + { 0x0030, 11, 1856 }, // 00000001100 + { 0x0034, 11, 1920 }, // 00000001101 + { 0x0024, 12, 1984 }, // 000000010010 + { 0x0026, 12, 2048 }, // 000000010011 + { 0x0028, 12, 2112 }, // 000000010100 + { 0x002a, 12, 2176 }, // 000000010101 + { 0x002c, 12, 2240 }, // 000000010110 + { 0x002e, 12, 2304 }, // 000000010111 + { 0x0038, 12, 2368 }, // 000000011100 + { 0x003a, 12, 2432 }, // 000000011101 + { 0x003c, 12, 2496 }, // 000000011110 + { 0x003e, 12, 2560 }, // 000000011111 + { 0x0000, 0, -1 } // Illegal entry +}; + + + + +// ---------------------------------------- +// SOURCE OF BITS + +#define VLSBUFSIZE 64 + +class MMRDecoder::VLSource : public GPEnabled +{ +protected: + VLSource(GP<ByteStream> &inp); + void init(const bool striped); +public: + // Initializes a bit source on a bytestream + static GP<VLSource> create(GP<ByteStream> &inp, const bool striped); + + // Synchronize on the next stripe + void nextstripe(void); + // Returns a 32 bits integer with at least the + // next sixteen code bits in the high order bits. + inline unsigned int peek(void); + // Ensures that next #peek()# contains at least + // the next 24 code bits. + void preload(void); + // Consumes #n# bits. + void shift(const int n); +private: + GP<ByteStream> ginp; + ByteStream &inp; + unsigned char buffer[ VLSBUFSIZE ]; + unsigned int codeword; + int lowbits; + int bufpos; + int bufmax; + int readmax; +}; + +MMRDecoder::VLSource::VLSource(GP<ByteStream> &xinp) +: ginp(xinp), inp(*ginp), codeword(0), + lowbits(0), bufpos(0), bufmax(0), + readmax(-1) +{} + +void +MMRDecoder::VLSource::init(const bool striped) +{ + if (striped) + readmax = inp.read32(); + lowbits = 32; + preload(); +} + +GP<MMRDecoder::VLSource> +MMRDecoder::VLSource::create(GP<ByteStream> &inp, const bool striped) +{ + VLSource *src=new VLSource(inp); + GP<VLSource> retval=src; + src->init(striped); + return retval; +} + +void +MMRDecoder::VLSource::shift(const int n) +{ + codeword<<=n; + lowbits+=n; + if (lowbits>=16) + preload(); +} + +inline unsigned int +MMRDecoder::VLSource::peek(void) +{ + return codeword; +} + + +void +MMRDecoder::VLSource::nextstripe(void) +{ + while (readmax>0) + { + int size = sizeof(buffer); + if (readmax < size) + size = readmax; + inp.readall(buffer, size); + readmax -= size; + } + bufpos = bufmax = 0; + memset(buffer,0,sizeof(buffer)); + readmax = inp.read32(); + codeword = 0; + lowbits = 32; + preload(); +} + +void +MMRDecoder::VLSource::preload(void) +{ + while (lowbits>=8) + { + if (bufpos >= bufmax) + { + // Refill buffer + bufpos = bufmax = 0; + int size = sizeof(buffer); + if (readmax>=0 && readmax<size) + size = readmax; + if (size>0) + bufmax = inp.read((void*)buffer, size); + readmax -= bufmax; + if (bufmax <= 0) + return; + } + lowbits -= 8; + codeword |= buffer[bufpos++] << lowbits; + } +} + + + +// ---------------------------------------- +// VARIABLE LENGTH CODES + + + +class MMRDecoder::VLTable : public GPEnabled +{ +protected: + VLTable(const VLCode *codes); + void init(const int nbits); +public: + // Construct a VLTable given a codebook with #nbits# long codes. + static GP<VLTable> create(VLCode const * const codes, const int nbits); + + // Reads one symbol from a VLSource + int decode(MMRDecoder::VLSource *src); + + const VLCode *code; + int codewordshift; + unsigned char *index; + GPBuffer<unsigned char> gindex; +}; + +GP<MMRDecoder::VLTable> +MMRDecoder::VLTable::create(VLCode const * const codes, const int nbits) +{ + VLTable *table=new VLTable(codes); + GP<VLTable> retval=table; + table->init(nbits); + return retval; +} + +inline int +MMRDecoder::VLTable::decode(MMRDecoder::VLSource *src) +{ + const VLCode &c = code[ index[ src->peek() >> codewordshift ] ]; + src->shift(c.codelen); + return c.value; +} + +MMRDecoder::VLTable::VLTable(const VLCode *codes) +: code(codes), codewordshift(0), gindex(index,0) +{} + +void +MMRDecoder::VLTable::init(const int nbits) +{ + // count entries + int ncodes = 0; + while (code[ncodes].codelen) + ncodes++; + // check arguments + if (nbits<=1 || nbits>16) + G_THROW(invalid_mmr_data); + if (ncodes>=256) + G_THROW(invalid_mmr_data); + codewordshift = 32 - nbits; + // allocate table + int size = (1<<nbits); + gindex.resize(size); + gindex.set(ncodes); + // process codes + for (int i=0; i<ncodes; i++) { + const int c = code[i].code; + const int b = code[i].codelen; + if(b<=0 || b>nbits) + { + G_THROW(invalid_mmr_data); + } + // fill table entries whose index high bits are code. + int n = c + (1<<(nbits-b)); + while ( --n >= c ) { + if(index[n] != ncodes) + G_THROW( ERR_MSG("MMRDecoder.bad_codebook") ); + index[n] = i; + } + } +} + +// ---------------------------------------- +// MMR DECODER + + + +MMRDecoder::~MMRDecoder() {} + +MMRDecoder::MMRDecoder( const int xwidth, const int xheight ) +: width(xwidth), height(xheight), lineno(0), + striplineno(0), rowsperstrip(0), gline(line,width+8), + glineruns(lineruns,width+4), gprevruns(prevruns,width+4) +{ + gline.clear(); + glineruns.clear(); + gprevruns.clear(); + lineruns[0] = width; + prevruns[0] = width; +} + +void +MMRDecoder::init(GP<ByteStream> gbs, const bool striped) +{ + rowsperstrip = (striped ? gbs->read16() : height); + src = VLSource::create(gbs, striped); + mrtable = VLTable::create(mrcodes, 7); + btable = VLTable::create(bcodes, 13); + wtable = VLTable::create(wcodes, 13); +} + +GP<MMRDecoder> +MMRDecoder::create( GP<ByteStream> gbs, const int width, + const int height, const bool striped ) +{ + MMRDecoder *mmr=new MMRDecoder(width,height); + GP<MMRDecoder> retval=mmr; + mmr->init(gbs,striped); + return retval; +} + +const unsigned short * +MMRDecoder::scanruns(const unsigned short **endptr) +{ + // Check if all lines have been returned + if (lineno >= height) + return 0; + // Check end of stripe + if ( striplineno == rowsperstrip ) + { + striplineno=0; + lineruns[0] = prevruns[0] = width; + src->nextstripe(); + } + // Swap run buffers + unsigned short *pr = lineruns; + unsigned short *xr = prevruns; + prevruns = pr; + lineruns = xr; + // Loop until scanline is complete + bool a0color = false; + int a0,rle,b1; + for(a0=0,rle=0,b1=*pr++;a0 < width;) + { + // Process MMR codes + const int c=mrtable->decode(src); + switch ( c ) + { + /* Pass Mode */ + case P: + { + b1 += *pr++; + rle += b1 - a0; + a0 = b1; + b1 += *pr++; + break; + } + /* Horizontal Mode */ + case H: + { + // First run + VLTable &table1 = *(a0color ? btable : wtable); + int inc; + do { inc=table1.decode(src); a0+=inc; rle+=inc; } while (inc>=64); + *xr = rle; xr++; rle = 0; + // Second run + VLTable &table2 = *(!a0color ? btable : wtable); + do { inc=table2.decode(src); a0+=inc; rle+=inc; } while (inc>=64); + *xr = rle; xr++; rle = 0; + break; + } + /* Vertical Modes */ + case V0: + case VR3: + case VR2: + case VR1: + case VL3: + case VL2: + case VL1: + { + int inc=b1; + switch ( c ) + { + case V0: + inc = b1; + b1 += *pr++; + break; + case VR3: + inc = b1+3; + b1 += *pr++; + break; + case VR2: + inc = b1+2; + b1 += *pr++; + break; + case VR1: + inc = b1+1; + b1 += *pr++; + break; + case VL3: + inc = b1-3; + b1 -= *--pr; + break; + case VL2: + inc = b1-2; + b1 -= *--pr; + break; + case VL1: + inc = b1-1; + b1 -= *--pr; + break; + } + *xr = inc+rle-a0; + xr++; + a0 = inc; + rle = 0; + a0color = !a0color; + break; + } + /* Uncommon modes */ + default: + { + src->preload(); + unsigned int m = src->peek(); + // -- Could be EOFB ``000000000001000000000001'' + // TIFF6 says that all remaining lines are white + if ((m & 0xffffff00) == 0x00100100) + { + lineno = height; + return 0; + } + // -- Could be UNCOMPRESSED ``0000001111'' + // TIFF6 says people should not do this. + // RFC1314 says people should do this. + else if ((m & 0xffc00000) == 0x03c00000) + { +#ifdef MMRDECODER_REFUSES_UNCOMPRESSED + G_THROW( ERR_MSG("MMRDecoder.cant_process") ); +#else + // ---THE-FOLLOWING-CODE-IS-POORLY-TESTED--- + src->shift(10); + while ((m = (src->peek() & 0xfc000000))) + { + if (m == 0x04000000) // 000001 + { + src->shift(6); + if (a0color) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + rle += 5; + a0 += 5; + } + else // 000010 to 111111 + { + src->shift(1); + if (a0color == !(m & 0x80000000)) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + rle++; + a0++; + } + if (a0 > width) + G_THROW(invalid_mmr_data); + } + // Analyze uncompressed termination code. + m = src->peek() & 0xff000000; + src->shift(8); + if ( (m & 0xfe000000) != 0x02000000 ) + G_THROW(invalid_mmr_data); + if (rle) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + if (a0color == !(m & 0x01000000)) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + // Cross fingers and proceed ... + break; +#endif + } + // -- Unknown MMR code. + G_THROW(invalid_mmr_data); + } + } + // Next reference run + for(;b1<=a0 && b1<width;pr+=2) + { + b1 += pr[0]+pr[1]; + } + } + // Final P must be followed by V0 (they say!) + if (rle > 0) + { + if (mrtable->decode(src) != V0) + { + G_THROW(invalid_mmr_data); + } + } + if (rle > 0) + { + *xr = rle; + xr++; + } + // At this point we should have A0 equal to WIDTH + // But there are buggy files around (Kofax!) + // and we are not the CCITT police. + if (a0 > width) + { + while (a0 > width && xr > lineruns) + a0 -= *--xr; + if (a0 < width) + { + *xr = width-a0; + xr++; + } + } + /* Increment and return */ + if (endptr) + *endptr = xr; + xr[0] = 0; + xr[1] = 0; + lineno ++; + striplineno ++; + return lineruns; +} + + + +const unsigned char * +MMRDecoder::scanrle(const bool invert, const unsigned char **endptr) +{ + // Obtain run lengths + const unsigned short *xr = scanruns(); + if (!xr) return 0; + unsigned char *p=line; + // Process inversion + if (invert) + { + if (! *xr) + { + xr++; + }else + { + *p = 0; p++; + } + } + // Encode lenghts using the RLE format + for(int a0=0;a0 < width;) + { + int count = *xr++; + a0 += count; + GBitmap::append_run(p, count); + } + if (endptr) + *endptr = p; + p[0] = 0; + p[1] = 0; + return line; +} + + +#if 0 +const unsigned char * +MMRDecoder::scanline(void) +{ + // Obtain run lengths + const unsigned short *xr = scanruns(); + if (!xr) return 0; + // Allocate data buffer if needed + unsigned char *p = line; + // Decode run lengths + int a0 = 0; + int a0color = 0; + while (a0 < width) + { + int a1 = a0 + *xr++; + while (a0<a1 && a0<width) + line[a0++] = a0color; + a0color = !a0color; + } + return line; +} +#endif + + + + +// ---------------------------------------- +// MAIN DECODING ROUTINE + +bool +MMRDecoder::decode_header( + ByteStream &inp, int &width, int &height, int &invert) +{ + unsigned long int magic = inp.read32(); + if((magic&0xfffffffc) != 0x4d4d5200) + G_THROW( ERR_MSG("MMRDecoder.unrecog_header") ); + invert = ((magic & 0x1) ? 1 : 0); + const bool strip = ((magic & 0x2) ? 1 : 0); + width = inp.read16(); + height = inp.read16(); + if (width<=0 || height<=0) + G_THROW( ERR_MSG("MMRDecoder.bad_header") ); + return strip; +} + +static inline int MAX(int a, int b) { return a>b ? a : b; } +static inline int MIN(int a, int b) { return a<b ? a : b; } + +GP<JB2Image> +MMRDecoder::decode(GP<ByteStream> gbs) +{ + ByteStream &inp=*gbs; + // Read header + int width, height, invert; + const bool striped=decode_header(inp, width, height, invert); + // Prepare image + GP<JB2Image> jimg = JB2Image::create(); + jimg->set_dimension(width, height); + // Choose pertinent blocksize + int blocksize = MIN(500,MAX(64,MAX(width/17,height/22))); + int blocksperline = (width+blocksize-1)/blocksize; + // Prepare decoder + GP<MMRDecoder> gdcd=MMRDecoder::create(gbs, width, height, striped); + MMRDecoder &dcd=*gdcd; + // Loop on JB2 bands + int line = height-1; + while (line >= 0) + { + int bandline = MIN(blocksize-1,line); + GPArray<GBitmap> blocks(0,blocksperline-1); + // Loop on scanlines + for(; bandline >= 0; bandline--,line--) + { + // Decode one scanline + const unsigned short *s = dcd.scanruns(); + if (s) + { + // Loop on blocks + int x = 0; + int b = 0; + int firstx = 0; + bool c = !!invert; + while (x < width) + { + int xend = x + *s++; + while (b<blocksperline) + { + int lastx = MIN(firstx+blocksize,width); + if (c) + { + if (!blocks[b]) + blocks[b] = GBitmap::create(bandline+1, lastx-firstx); + unsigned char *bptr = (*blocks[b])[bandline] - firstx; + int x1 = MAX(x,firstx); + int x2 = MIN(xend,lastx); + while (x1 < x2) + bptr[x1++] = 1; + } + if (xend < lastx) + break; + firstx = lastx; + b ++; + } + x = xend; + c = !c; + } + } + } + // Insert blocks into JB2Image + for (int b=0; b<blocksperline; b++) + { + JB2Shape shape; + shape.bits = blocks[b]; + if (shape.bits) + { + shape.parent = -1; + shape.bits->compress(); + JB2Blit blit; + blit.left = b*blocksize; + blit.bottom = line+1; + blit.shapeno = jimg->add_shape(shape); + jimg->add_blit(blit); + } + } + } + // Return + return jimg; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/MMRDecoder.h b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.h new file mode 100644 index 00000000..6516b4cd --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.h @@ -0,0 +1,238 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMRDecoder.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _MMRDECODER_H_ +#define _MMRDECODER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class JB2Image; + +/** @name MMRDecoder.h + Files #"MMRDecoder.h"# and #"MMRDecoder.cpp"# implement a + CCITT-G4/MMR decoder suitable for use in DjVu. The main + entry point is function \Ref{MMRDecoder::decode}. + + The foreground mask layer of a DjVu file is usually encoded with a + #"Sjbz"# chunk containing JB2 encoded data (cf. \Ref{JB2Image.h}). + Alternatively, the qmask layer may be encoded with a #"Smmr"# + chunk containing a small header followed by MMR encoded data. + This encoding scheme produces significantly larger files. On the + other hand, many scanners a printers talk MMR using very efficient + hardware components. This is the reason behind the introduction + of #"Smmr"# chunks. + + The #Smmr# chunk starts by a header containing the following data: + \begin{verbatim} + BYTE*3 : 'M' 'M' 'R' + BYTE : 0xb000000<s><i> + INT16 : <width> (MSB first) + INT16 : <height> (MSB first) + \end{verbatim} + + The header is followed by the encoded data. Bit 0 of the fourth header + byte (#<i>#) is similar to TIFF's ``min-is-black'' tag. This bit is set + for a reverse video image. The encoded data can be in either ``regular'' + MMR form or ``striped'' MMR form. This is indicated by bit 1 of the + fourth header byte (#<s>#). This bit is set to indicate ``striped'' + data. The ``regular'' data format consists of ordinary MMR encoded data. + The ``striped'' data format consists of one sixteen bit integer (msb + first) containing the number of rows per stripe, followed by data for each + stripe as follows. + \begin{verbatim} + INT16 : <rowsperstripe> (MSB first) + INT32 : <nbytes1> + BYTE*<nbytes1> : <mmrdata1> + INT32 : <nbytes2> + BYTE*<nbytes2> : <mmrdata2> + ... + \end{verbatim} + Static function \Ref{MMRDecoder::decode_header} decodes the header. You + can then create a \Ref{MMRDecoder} object with the flags #inverted# and + #striped# as obtained when decoding the header. One can also decode raw + MMR data by simply initialising a \Ref{MMRDecoder} object with flag + #striped# unset. Each call to \Ref{MMRDecoder::scanruns}, + \Ref{MMRDecoder::scanrle} or \Ref{MMRDecoder::scanline} will then decode a + row of the MMR encoded image. + + Function \Ref{MMRDecoder::decode} is a convenience function for decoding + the contents of a #"Smmr"# chunk. It returns a \Ref{JB2Image} divided + into manageable blocks in order to provide the zooming and panning + features implemented by class \Ref{JB2Image}. + + @memo + CCITT-G4/MMR decoder. + @version + #$Id: MMRDecoder.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# + @author + Parag Deshmukh <parag@sanskrit.lz.att.com> \\ + Leon Bottou <leonb@research.att.com> */ +//@{ + + + +#define MMRDECODER_HAS_SCANRUNS 1 +#define MMRDECODER_HAS_SCANRLE 1 + + + +/** Class for G4/MMR decoding. The simplest way to use this class is + the static member function \Ref{MMRDecoder::decode}. This + function internally creates an instance of #MMRDecoder# which + processes the MMR data scanline by scanline. */ +class MMRDecoder : public GPEnabled +{ +protected: + MMRDecoder(const int width, const int height); + void init(GP<ByteStream> gbs, const bool striped=false); +public: + /** Main decoding routine that (a) decodes the header using + #decode_header#, (b) decodes the MMR data using an instance of + #MMRDecoder#, and returns a new \Ref{JB2Image} composed of tiles + whose maximal width and height is derived from the size of the + image. */ + static GP<JB2Image> decode(GP<ByteStream> gbs); + + /// Only decode the header. + static bool decode_header(ByteStream &inp, + int &width, int &height, int &invert); + +public: + /// Non-virtual destructor. + ~MMRDecoder(); + /** Create a MMRDecoder object for decoding an image + of size #width# by #height#. Flag $striped# must be set + if the image is composed of multiple stripes. */ + static GP<MMRDecoder> create(GP<ByteStream> gbs, + const int width, const int height, + const bool striped=false ); + + /** Decodes a scanline and returns a pointer to an array of run lengths. + The returned buffer contains the length of alternative white and black + runs. These run lengths sum to the image width. They are followed by + two zeroes. The position of these two zeroes is stored in the pointer + specified by the optional argument #endptr#. The buffer data should be + processed before calling this function again. */ + const unsigned short *scanruns(const unsigned short **endptr=0); + /** Decodes a scanline and returns a pointer to RLE encoded data. The + buffer contains the length of the runs for the current line encoded as + described in \Ref{PNM and RLE file formats}.) The flag #invert# can be + used to indicate that the MMR data is encoded in reverse video. The RLE + data is followed by two zero bytes. The position of these two zeroes is + stored in the pointer specified by the optional argument #endptr#. The + buffer data should be processed before calling this function again. This + is implemented by calling \Ref{MMRDecoder::scanruns}. */ + const unsigned char *scanrle(const bool invert, + const unsigned char **endptr=0); +#if 0 + /** Decodes a scanline and returns a pointer to an array of #0# or #1# bytes. + Returns a pointer to the scanline buffer containing one byte per pixel. + The buffer data should be processed before calling this function again. + This is implemented by calling \Ref{MMRDecoder::scanruns}. */ + const unsigned char *scanline(); +#endif + private: + int width; + int height; + int lineno; + int striplineno; + int rowsperstrip; + unsigned char *line; + GPBuffer<unsigned char> gline; + unsigned short *lineruns; + GPBuffer<unsigned short> glineruns; + unsigned short *prevruns; + GPBuffer<unsigned short> gprevruns; +public: + class VLSource; + class VLTable; +private: + GP<VLSource> src; + GP<VLTable> mrtable; + GP<VLTable> wtable; + GP<VLTable> btable; + friend class VLSource; + friend class VLTable; +}; + + +//@} + + +// ----------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/MMX.cpp b/kviewshell/plugins/djvu/libdjvu/MMX.cpp new file mode 100644 index 00000000..58b74177 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMX.cpp @@ -0,0 +1,209 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMX.cpp,v 1.10 2004/05/13 16:50:10 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "MMX.h" +#include <stdio.h> +#include <stdlib.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ---------------------------------------- +// PRINTING MMX REGISTERS (Debug) + + +#if defined(MMX) && defined(DEBUG) +extern "C" void +mmx_show() +{ + /* This function can be called from a debugger + in order to visualize the contents of the MMX registers. */ + int mmregs[16]; + MMXra( movq, mm0, &mmregs[0]); + MMXra( movq, mm1, &mmregs[2]); + MMXra( movq, mm2, &mmregs[4]); + MMXra( movq, mm3, &mmregs[6]); + MMXra( movq, mm4, &mmregs[8]); + MMXra( movq, mm5, &mmregs[10]); + MMXra( movq, mm6, &mmregs[12]); + MMXra( movq, mm7, &mmregs[14]); + MMXemms; + for (int i=0; i<8; i++) + DjVuPrintMessageUTF8("mm%d: %08x%08x\n", i, + mmregs[i+i+1], mmregs[i+i]); + MMXar( movq, &mmregs[0], mm0); + MMXar( movq, &mmregs[2], mm1); + MMXar( movq, &mmregs[4], mm2); + MMXar( movq, &mmregs[6], mm3); + MMXar( movq, &mmregs[8], mm4); + MMXar( movq, &mmregs[10], mm5); + MMXar( movq, &mmregs[12], mm6); + MMXar( movq, &mmregs[14], mm7); +} +#endif + + + +// ---------------------------------------- +// MMX ENABLE/DISABLE + +// Default settings autodetect MMX. +// Use macro DISABLE_MMX to disable MMX by default. + +#if defined(MMX) && !defined(DISABLE_MMX) +int MMXControl::mmxflag = -1; +#else +int MMXControl::mmxflag = 0; +#endif + +int +MMXControl::disable_mmx() +{ + mmxflag = 0; + return mmxflag; +} + +int +MMXControl::enable_mmx() +{ + int cpuflags = 0; +#if defined(MMX) && defined(__GNUC__) && defined(__i386__) + // Detection of MMX for GCC + __asm__ volatile ("pushl %%ebx\n\t" + "pushfl\n\t" + "popl %%ecx\n\t" + "xorl %%edx,%%edx\n\t" + // Check that CPUID exists + "movl %%ecx,%%eax\n\t" + "xorl $0x200000,%%eax\n\t" + "pushl %%eax\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %%eax\n\t" + "xorl %%ecx,%%eax\n\t" + "jz 1f\n\t" + "pushl %%ecx\n\t" + "popfl\n\t" + // Check that CR0:EM is clear + "smsw %%ax\n\t" + "andl $4,%%eax\n\t" + "jnz 1f\n\t" + // Execute CPUID + "movl $1,%%eax\n\t" + "cpuid\n" + // EBX contains magic when -fPIC is on. + "1:\tpopl %%ebx\n\t" + "movl %%edx, %0" + : "=m" (cpuflags) : + : "eax","ecx","edx"); +#endif +#if defined(MMX) && defined(_MSC_VER) && defined(_M_IX86) + // Detection of MMX for MSVC + __asm { pushfd + pop ecx + xor edx,edx + ;// Check that CPUID exists + mov eax,ecx + xor eax,0x200000 + push eax + popfd + pushfd + pop eax + xor eax,ecx + jz fini + push ecx + popfd + ;// Check that CR0:EM is zero + smsw ax + and eax,4 + jnz fini + ;// Execute CPUID + mov eax,1 + _emit 0xf + _emit 0xa2 + fini: + mov cpuflags,edx + ;// MSVC determines clobbered registers by scanning the assembly code. + ;// Since it does not know CPUID, it would not know that EBX is clobbered + ;// without the dummy instruction below... + xor ebx,ebx + } +#endif + mmxflag = !!(cpuflags & 0x800000); + return mmxflag; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/MMX.h b/kviewshell/plugins/djvu/libdjvu/MMX.h new file mode 100644 index 00000000..41ec002f --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMX.h @@ -0,0 +1,194 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMX.h,v 1.9 2003/12/01 22:57:40 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _MMX_H_ +#define _MMX_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name MMX.h + Files #"MMX.h"# and #"MMX.cpp"# implement basic routines for + supporting the MMX instructions on x86. Future instruction sets + for other processors may be supported in this file as well. + + Macro #MMX# is defined if the compiler supports the X86-MMX instructions. + It does not mean however that the processor supports the instruction set. + Variable #MMXControl::mmxflag# must be used to decide whether MMX. + instructions can be executed. MMX instructions are entered in the middle + of C++ code using the following macros. Examples can be found in + #"IWTransform.cpp"#. + + \begin{description} + \item[MMXrr( insn, srcreg, dstreg)] + Encode a register to register MMX instruction + (e.g. #paddw# or #punpcklwd#). + \item[MMXar( insn, addr, dstreg )] + Encode a memory to register MMX instruction + (e.g. #moveq# from memory). + \item[MMXra( insn, srcreg, addr )] + Encode a register to memory MMX instruction + (e.g. #moveq# to memory). + \item[MMXir( insn, imm, dstreg )] + Encode a immediate to register MMX instruction + (e.g #psraw#). + \item[MMXemms] + Execute the #EMMS# instruction to reset the FPU state. + \end{description} + + @memo + Essential support for MMX. + @version + #$Id: MMX.h,v 1.9 2003/12/01 22:57:40 leonb Exp $# + @author: + L\'eon Bottou <leonb@research.att.com> -- initial implementation */ +//@{ + + +/** MMX Control. + Class #MMXControl# encapsulates a few static functions for + globally enabling or disabling MMX support. */ + +class MMXControl +{ + public: + // MMX DETECTION + /** Detects and enable MMX or similar technologies. This function checks + whether the CPU supports a vectorial instruction set (such as Intel's + MMX) and enables them. Returns a boolean indicating whether such an + instruction set is available. Speedups factors may vary. */ + static int enable_mmx(); + /** Disables MMX or similar technologies. The transforms will then be + performed using the baseline code. */ + static int disable_mmx(); + /** Contains a value greater than zero if the CPU supports vectorial + instructions. A negative value means that you must call \Ref{enable_mmx} + and test the value again. Direct access to this member should only be + used to transfer the instruction flow to the vectorial branch of the + code. Never modify the value of this variable. Use #enable_mmx# or + #disable_mmx# instead. */ + static int mmxflag; // readonly +}; + +//@} + + + + +// ---------------------------------------- +// GCC MMX MACROS + +#ifndef NO_MMX + +#if defined(__GNUC__) && defined(__i386__) +#define MMXemms \ + __asm__ volatile("emms" : : : "memory" ) +#define MMXrr(op,src,dst) \ + __asm__ volatile( #op " %%" #src ",%%" #dst : : : "memory") +#define MMXir(op,imm,dst) \ + __asm__ volatile( #op " %0,%%" #dst : : "i" (imm) : "memory") +#define MMXar(op,addr,dst) \ + __asm__ volatile( #op " %0,%%" #dst : : "m" (*(int*)(addr)) : "memory") +#define MMXra(op,src,addr) \ + __asm__ volatile( #op " %%" #src ",%0" : : "m" (*(int*)(addr)) : "memory") +#define MMX 1 +#endif + + +// ---------------------------------------- +// MSVC MMX MACROS + +#if defined(_MSC_VER) && defined(_M_IX86) +// Compiler option /GM is required +#pragma warning( disable : 4799 ) +#define MMXemms \ + __asm { emms } +#define MMXrr(op,src,dst) \ + __asm { op dst,src } +#define MMXir(op,imm,dst) \ + __asm { op dst,imm } +#define MMXar(op,addr,dst) \ + { register __int64 var=*(__int64*)(addr); __asm { op dst,var } } +#define MMXra(op,src,addr) \ + { register __int64 var; __asm { op [var],src }; *(__int64*)addr = var; } +// Probably not as efficient as GCC macros +#define MMX 1 +#endif + +#endif + +// ----------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/Makefile.am b/kviewshell/plugins/djvu/libdjvu/Makefile.am new file mode 100644 index 00000000..4795da0d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir) $(all_includes) + +kde_module_LTLIBRARIES = libdjvu.la +libdjvu_la_LDFLAGS = -avoid-version $(all_libraries) +libdjvu_la_LIBADD = $(LIBJPEG) +libdjvu_la_SOURCES = Arrays.cpp DjVuDocEditor.cpp DjVuMessageLite.cpp GOS.cpp IW44Image.cpp \ + BSByteStream.cpp DjVuDocument.cpp DjVuNavDir.cpp GPixmap.cpp JB2EncodeCodec.cpp \ + BSEncodeByteStream.cpp DjVuDumpHelper.cpp DjVuPalette.cpp GRect.cpp JB2Image.cpp \ + ByteStream.cpp DjVuErrorList.cpp DjVuPort.cpp GScaler.cpp JPEGDecoder.cpp \ + DataPool.cpp DjVuFileCache.cpp DjVuText.cpp GSmartPointer.cpp MMRDecoder.cpp \ + DjVuFile.cpp DjVuToPS.cpp GString.cpp MMX.cpp DjVmNav.cpp \ + debug.cpp DjVuGlobal.cpp GBitmap.cpp GThreads.cpp UnicodeByteStream.cpp \ + DjVmDir0.cpp DjVuGlobalMemory.cpp GContainer.cpp GUnicode.cpp XMLParser.cpp \ + DjVmDir.cpp DjVuImage.cpp GException.cpp GURL.cpp XMLTags.cpp \ + DjVmDoc.cpp DjVuInfo.cpp GIFFManager.cpp IFFByteStream.cpp ZPCodec.cpp \ + DjVuAnno.cpp DjVuMessage.cpp GMapAreas.cpp IW44EncodeCodec.cpp + +KDE_OPTIONS = nofinal diff --git a/kviewshell/plugins/djvu/libdjvu/README.djvulibre b/kviewshell/plugins/djvu/libdjvu/README.djvulibre new file mode 100644 index 00000000..fb37c8d7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/README.djvulibre @@ -0,0 +1,85 @@ + + +1- WHAT IS DJVU. +================ + +DjVu (pronounced "déjà vu") a set of compression technologies, a file format, +and a software platform for the delivery over the Web of digital documents, +scanned documents, and high resolution images. + +DjVu documents download and display extremely quickly, and look exactly the +same on all platforms. DjVu can be seen as superior alternative to PDF and +Postscript for digital documents, to TIFF (and PDF) for scanned documents, to +JPEG for photographs and pictures, and to GIF for large palettized +images. DjVu is the only Web format that is practical for distributing +high-resolution scanned documents in color. No other format comes close. + +Typical DjVu file sizes are as follows: + +- Bitonal scanned documents: + 5 to 30KB per page at 300dpi, + 3 to 10 times smaller than PDF or TIFF. + +- Color scanned documents: + 30 to 100KB per page at 300dpi, + 5 to 10 times smaller than JPEG. + +- Photos: + 2 times smaller than JPEG, + about the same as JPEG-2000. + +- Palettized images: + 2 times smaller than GIF, + up to 10 times if there is text. + +DjVu is used by hundreds of commercial, governmental, and non-commercial web +sites around the world to distribute scanned documents, digital documents, and +high-resolution photos. + +Demos, and general information about DjVu can be found at +http://www.djvuzone.org, or at http://www.lizardtech.com. + +DjVu was originally developed at AT&T Labs-Research. AT&T sold DjVu to +LizardTech Inc. in March 2000. + + + +2- WHAT IS DJVULIBRE? +===================== + +In an effort to promote DjVu as a Web standard, LizardTech's management was +enlightened enough to release the reference implementation of DjVu under the +GNU GPL in October 2000. DjVuLibre (pronounced like the French "déjà vu +libre"), is an enhanced version of that code maintained by the original +inventors of DjVu. It is compatible with LizardTech's DjVu software v3.5. + +DjVuLibre includes: + +- A standalone DjVu viewer for Unix under X11 (based on the Qt library). + +- A browser plugin that works with most Unix browsers, including: + Netscape-4.x, Netscape-6.x, Mozilla, Galeon, Konqueror, and Opera. + +- A full-fledged wavelet-based compressor for pictures. + +- A simple compressor for bitonal (black and white) scanned pages. + +- A compressor for palettized images (a la GIF). + +- A full set of utilities to manipulate and assemble DjVu images and documents. + +- A set of decoders to convert DjVu to a number of other formats. + +- An up-to-date version of the C++ DjVu Reference Library + +Windows and Mac versions of the viewer/plug-in, as well as commercial versions +of the compressors and OCR engines are available from LizardTech Inc.. The +compressors provided here are slower, produce larger files, and sometimes +lower quality images than the commercial compressors, but they do the job. + +A variety of free web-based conversion services are also available, including +any2djvu.djvuzone.org, bib2web.djvuzone.org, and openlib.djvuzone.org. + + + + diff --git a/kviewshell/plugins/djvu/libdjvu/Template.h b/kviewshell/plugins/djvu/libdjvu/Template.h new file mode 100644 index 00000000..c37935ce --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Template.h @@ -0,0 +1,258 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: Template.h,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +//T// This is a template for the header files in the +//T// DjVu reference library. It describes the general +//T// conventions as well as the documentation. +//T// Comments prefixed with '//T//' explain the template +//T// features and should be removed. + +#ifndef _TEMPLATE_H_ +#define _TEMPLATE_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +//T// Always include "DjVuGlobal.h" +#include "DjVuGlobal.h" + +//T// Other include files +#include <string.h> +#include "GException.h" + +//T// Begin name space + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name Template.h + + Files #"Template.h"# and #"Template.cpp"# are not used for anything but + the current programming and documentation standards in the DjVu reference + library. This doc++ comment briefly describes the abstractions defined in + this files. It must mention all the files involved in implementing this + features, as well as references to the main classes \Ref{classname}. + + This comment may contain additional sections as follows: + + {\bf Algorithmic Remarks} --- Comments about the algorithms, their + performance and their limitations. + + {\bf Historical Remarks} --- Comments about the successive revisions of + this file and other anecdotical details. This is where we can amuse the + reader with funny details. + + {\bf ToDo} --- Things that we have been thinking to do but did not + fully implement yet. It should explain how we plan to modify the current + code could be modified to implement these things. People who change this + code thus should avoid jeopardizing these plans. + + {\bf Example} --- It would be cool to demonstrate how these functions + can be used by providing a small segment of C/C++ code. + \begin{verbatim} + ExampleClass toto(3,4); + toto.draw(mywin); + \end{verbatim} + + This main doc++ comment is followed by a few doc++ entries. + \begin{itemize} + \item the "memo" field contains a single line description of the file. + \item the "version" field contains a cvs magic expression. + \item the author fields contains a list of authors and email addresses. + (the #\\# termination breaks the line.) + \end{itemize} + + @memo + Template header file + @version + #$Id: Template.h,v 1.8 2003/11/07 22:08:22 leonb Exp $# + @author: + L\'eon Bottou <leonb@research.att.com> -- initial implementation \\ + Andrew Erofeev <eaf@geocities.com> -- implemented EXTERNAL_TEMPLATES */ +//@{ +//T// The magic doc++ comment above opens a doc++ context. + + + +//T// Now comes the 'interface part' of the file. +//T// The c++ classes and public functions are defined there. +//T// Doc++ comments must be inserted for all functions +//T// intended to be used by other people. +//T// +//T// Quite often c++ sucks and it is necessary to have public or external symbols +//T// that actually are only there for implementation purposes. +//T// It is good to give a comment but this should not be a doc++ comment +//T// (see class GPool in GContainer.h). All other 'public' and 'protected' +//T// members should have a doc++ comment. There is no need to comment 'private' +//T// members, although a regular comment can be useful (not a doc++ comment). + + + +/** One-line class description. + Long description. There is no need to repeat the class name in the + one-line description. The long description should describe the abstraction + and point the user to the main member functions. An example could be + inserted when this is informative and not redundant with the file + description. Templates should document which member functions are + required for their type argument. The availability of non availabilty of a + copy constructor/copy operator can be specified when appropriate. + See the doc++ documentation for available LaTeX constructs. +*/ + +class ExampleClass +{ +public: + /** Virtual Destructor. */ + ~ExampleClass(); + /** Null Constructor. */ + ExampleClass(); + /** Copy Constructor. */ + ExampleClass(ExampleClass &ref); + /** Copy operator. */ + ExampleClass& operator=(ExampleClass &ref); + /** Example of member function. The first sentence of the member + function description must be a short single line description. + The rest can be more verbose. Excerpts of C or C++ text should + be surrounded by dieze characters (as in #win#). The doc++ #@param# + construct should be used when there is a need for additional details + about the arguments. In that case all the arguments must be documented + with a #@param# directive. + @param win drawing window. + This window must be created with #CreateWindow# and must be visible when + function #draw# is called. + */ + void draw(Window win); +protected: + /** Minimal x-coordinate. */ + int xmin; + /** Maximal x-coordinate. */ + int xmax; +private: + int whatever; + float encode; +}; + + +/** One-line function description. + Long description. Public external functions should be documented + as classes. Note that a family of public external function can be + introduced by a generic entry (see below) */ + +ExampleClass combine_example_classes(const ExampleClass&, + const ExampleClass &b); + + +/** @name Generic entry. + Long description. there is sometimes a need to add documentation + entries for grouping things which are not connected by the C++ + syntax (a family of functions, a family of defines, etc...). + The description starts with a very short name (introduced with #@name#) + followed by a long description. Because of doc++ limitations, + the one-line description must appear after the long description + in a #@memo# entry. + @memo One-line description +*/ + +//T// The following comments should be used when +//T// the preceding generic entry contains sub-entries +//@{ +//T// Sub-entries (both DOC++ and C++) should be declared there. +//@} + + + + + + +//@} +//T// The magic doc++ comment above closes the doc++ file context. +//T// The rest of the file only contains implementation stuff. + +// ------------ CLASSEXAMPLE INLINES +//T// This is where all the inline/template functions should be written +//T// This part of the file is segmented with comments. + +inline void +ClassExample::width() +{ + return xmax-xmin; +} + + + +// ------------ THE END +//T// End name space + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif +//T// Terminates the multiple inclusion #ifndef + + + + + diff --git a/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.cpp new file mode 100644 index 00000000..8d4f0188 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.cpp @@ -0,0 +1,368 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: UnicodeByteStream.cpp,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "UnicodeByteStream.h" +#include "ByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +UnicodeByteStream::UnicodeByteStream(const UnicodeByteStream &uni) +: bs(uni.bs), buffer(uni.buffer), bufferpos(uni.bufferpos), linesread(0) +{ + startpos=bs->tell(); +} + +UnicodeByteStream::UnicodeByteStream( + GP<ByteStream> ibs,const GStringRep::EncodeType et) +: bs(ibs), bufferpos(0), linesread(0) +{ + buffer=GUTF8String::create(0,0,et); + startpos=bs->tell(); +} + +UnicodeByteStream::~UnicodeByteStream() +{} + +static int +CountLines(const GUTF8String &str) +{ + int retval=0; + static const unsigned long lf='\n'; + for(int pos=0;(pos=str.search(lf,pos)+1)>0;++retval) + EMPTY_LOOP; + return retval; +} + +void +UnicodeByteStream::set_encodetype(const GStringRep::EncodeType et) +{ + seek(startpos,SEEK_SET); + bufferpos=0; + buffer=GUTF8String::create(0,0,et); +} + +void +UnicodeByteStream::set_encoding(const GUTF8String &xencoding) +{ + seek(startpos,SEEK_SET); + bufferpos=0; + buffer=GUTF8String::create(0,0,xencoding); +} + +size_t +UnicodeByteStream::read(void *buf, size_t size) +{ + bufferpos=0; + const int retval=bs->read(buf,size); + if(retval) + { + buffer=GUTF8String::create( + (unsigned char const *)buf,retval,buffer.get_remainder()); + }else + { + buffer=GUTF8String::create(0,0,buffer.get_remainder()); + } + return retval; +} + +size_t +UnicodeByteStream::write(const void *buf, size_t size) +{ + bufferpos=0; + buffer=GUTF8String::create(0,0,buffer.get_remainder()); + return bs->write(buf,size); +} + +long +UnicodeByteStream::tell(void) const +{ + return bs->tell(); +} + +UnicodeByteStream & +UnicodeByteStream::operator=(UnicodeByteStream &uni) +{ + bs=uni.bs; + bufferpos=uni.bufferpos; + buffer=uni.buffer; + return *this; +} + +int +UnicodeByteStream::seek +(long offset, int whence, bool nothrow) +{ + int retval=bs->seek(offset,whence,nothrow); + bufferpos=0; + buffer=GUTF8String::create(0,0,buffer.get_remainder()); + return retval; +} + +void +UnicodeByteStream::flush(void) +{ + bs->flush(); + bufferpos=0; + buffer=GUTF8String::create(0,0,buffer.get_remainder()); +} + + + +GUTF8String +UnicodeByteStream::gets( + size_t const t,unsigned long const stopat,bool const inclusive) +{ + GUTF8String retval; + unsigned int len=buffer.length()-bufferpos; + if(!len) + { + int i; + char *buf; + static const size_t bufsize=327680; + GPBuffer<char> gbuf(buf,bufsize); + while((i=read(buf,bufsize)>0)) + { + if((len=buffer.length()-bufferpos)) + break; + } + } + if(len) + { + int i=buffer.search((char)stopat,bufferpos); + if(i>=0) + { + if(inclusive) + { + ++i; + } + if(t&&(i>(int)t+bufferpos)) + { + i=t+bufferpos; + } + if(i>bufferpos) + { + retval=buffer.substr(bufferpos,i-bufferpos); + } + bufferpos=i; + linesread+=CountLines(retval); + }else + { + retval=buffer.substr(bufferpos,len); + bufferpos=buffer.length(); + linesread+=CountLines(retval); + retval+=gets(t?(t-(i-bufferpos)):0,stopat,inclusive); + } + } + return retval; +} + +XMLByteStream::XMLByteStream(UnicodeByteStream &uni) +: UnicodeByteStream(uni) {} + +XMLByteStream::XMLByteStream(GP<ByteStream> &ibs) +: UnicodeByteStream(ibs,GStringRep::XOTHER) +{} + +GP<XMLByteStream> +XMLByteStream::create(GP<ByteStream> ibs) +{ + XMLByteStream *xml=new XMLByteStream(ibs); + GP<XMLByteStream> retval=xml; + xml->init(); + return retval; +} + +void +XMLByteStream::init(void) +{ + unsigned char buf[4]; + GP<ByteStream> ibs=bs; + bufferpos=0; + bs->readall(buf,sizeof(buf)); + const unsigned int i=(buf[0]<<8)+buf[1]; + switch(i) + { + case 0x0000: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + switch(j) + { + case 0x003C: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4BE); + break; + } + case 0x3C00: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4_2143); + break; + } + case 0xFEFF: + { + buffer=GUTF8String::create(0,0,GStringRep::XUCS4BE); + startpos+=sizeof(buf); + break; + } + case 0xFFFE: + { + buffer=GUTF8String::create(0,0,GStringRep::XUCS4_2143); + startpos+=sizeof(buf); + break; + } + default: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + break; + } + } + } + case 0x003C: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + switch(j) + { + case 0x0000: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4_3412); + break; + case 0x003F: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF16BE); + break; + default: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + break; + } + break; + } + case 0x3C00: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + switch(j) + { + case 0x0000: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4LE); + break; + case 0x3F00: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF16LE); + break; + default: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + break; + } + break; + } + case 0x4C6F: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + buffer=GUTF8String::create(buf,sizeof(buf), + (j == 0xA794)?(GStringRep::XEBCDIC):(GStringRep::XUTF8)); + break; + } + case 0xFFFE: + { + buffer=GUTF8String::create(buf+2,sizeof(buf)-2,GStringRep::XUTF16LE); + startpos+=2; + break; + } + case 0xFEFF: + { + buffer=GUTF8String::create(buf+2,sizeof(buf)-2,GStringRep::XUTF16BE); + startpos+=2; + break; + } + case 0xEFBB: + { + if(buf[2] == 0xBF) + { + buffer=GUTF8String::create(buf+3,sizeof(buf)-3,GStringRep::XUTF8); + startpos+=3; + }else + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + } + break; + } + case 0x3C3F: + default: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + } + } + bs=ibs; +} + +XMLByteStream::~XMLByteStream() +{} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.h b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.h new file mode 100644 index 00000000..df678ffe --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.h @@ -0,0 +1,199 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: UnicodeByteStream.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _UNICODEBYTESTREAM_H_ +#define _UNICODEBYTESTREAM_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name UnicodeByteStream.h + + Files #"UnicodeByteStream.h"# and #"UnicodeByteStream.cpp"# implement a parser for + files structured W3C Extensible Markup Language (XML) 1.0 (Second Edition). + + Class \Ref{UnicodeByteStream} provides a way to read or write XML files. + files. Member functions provide an easy mean to position the underlying + \Ref{ByteStream}. + + {\bf References} --- W3C Extensible Markup Language (XML) 1.0 + (Second Edition) + \URL{http://www.w3.org/TR/2000/REC-xml-20001006.html} + + @memo + XML file parser. + @author + Bill C Riemers <docbill@sourceforge.net> + @version + #$Id: UnicodeByteStream.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# */ +//@{ + +#include "DjVuGlobal.h" +#include "GString.h" +#include "ByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** ByteStream interface for an Unicode file. + + Class #UnicodeByteStream# augments the #ByteStream# interface with + functions for navigating Unicode documents. It works in relation + with a ByteStream specified at construction time. + + {\bf Reading an Unicode file} --- You can read an Unicode file by + constructing an #UnicodeByteStream# object attached to the ByteStream + containing the Unicode file. + + {\bf Writing an Unicode file} --- You can write an Unicode file by + constructing an #UnicodeByteStream# object attached to the seekable + ByteStream object that will contain the XML file. + + Writing an XML file requires a seekable ByteStream (see + \Ref{ByteStream::is_seekable}). This is not much of a problem because you + can always create the XML file into a \Ref{MemoryByteStream} and then use + \Ref{ByteStream::copy} to transfer the XML file into a non seekable + ByteStream. */ + +class UnicodeByteStream : public ByteStream +{ +protected: + UnicodeByteStream(const UnicodeByteStream &bs); + UnicodeByteStream(GP<ByteStream> bs, + const GStringRep::EncodeType encodetype=GStringRep::XUTF8); +public: + /** Constructs an UnicodeByteStream object attached to ByteStream #bs#. + Any ByteStream can be used when reading an XML file. Writing + an XML file however requires a seekable ByteStream. */ + static GP<UnicodeByteStream> create(GP<ByteStream> bs, + const GStringRep::EncodeType encodetype=GStringRep::XUTF8) + { return new UnicodeByteStream(bs,encodetype); } + + // --- BYTESTREAM INTERFACE + ~UnicodeByteStream(); + /// Sets the encoding type and seek's to position 0. + void set_encodetype(const GStringRep::EncodeType et=GStringRep::XUTF8); + void set_encoding(const GUTF8String &encoding); + /// Simmular to fgets(), except read aheads effect the tell() position. + virtual GUTF8String gets(size_t const t=0,unsigned long const stopat='\n',bool const inclusive=true); + /// Resets the gets buffering as well as physically seeking. + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + /** Physically reads the specified bytes, and truncate the read ahead buffer. + */ + virtual size_t read(void *buffer, size_t size); + /// Not correctly implimented... + virtual size_t write(const void *buffer, size_t size); + /// tell will tell you the read position, including read ahead for gets()... + virtual long tell(void) const; + /// Does a flush, and clears the read ahead buffer. + virtual void flush(void); + + /// Find out how many lines have been read with gets. + int get_lines_read(void) const { return linesread; } +protected: + /// The real byte stream. + GP<ByteStream> bs; + GUTF8String buffer; + int bufferpos; + int linesread; + long startpos; +private: + // Cancel C++ default stuff + UnicodeByteStream & operator=(UnicodeByteStream &); +}; + + +class XMLByteStream : public UnicodeByteStream +{ +protected: + XMLByteStream(GP<ByteStream> &bs); + XMLByteStream(UnicodeByteStream &bs); + void init(void); +public: + static GP<XMLByteStream> create(GP<ByteStream> bs); + static GP<XMLByteStream> create(UnicodeByteStream &bs); + // --- BYTESTREAM INTERFACE + ~XMLByteStream(); +}; + +inline GP<XMLByteStream> +XMLByteStream::create(UnicodeByteStream &bs) +{ + return new XMLByteStream(bs); +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/XMLParser.cpp b/kviewshell/plugins/djvu/libdjvu/XMLParser.cpp new file mode 100644 index 00000000..b1d9f469 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLParser.cpp @@ -0,0 +1,1128 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLParser.cpp,v 1.10 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "XMLParser.h" +#include "XMLTags.h" +#include "ByteStream.h" +#include "GOS.h" +#include "DjVuDocument.h" +#include "DjVuText.h" +#include "DjVuAnno.h" +#include "DjVuFile.h" +#include "DjVuImage.h" +#include "debug.h" +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +static const char mimetype[]="image/x.djvu"; +static const char bodytag[]="BODY"; +static const char areatag[]="AREA"; +static const char maptag[]="MAP"; +static const char objecttag[]="OBJECT"; +static const char paramtag[]="PARAM"; +static const char wordtag[]="WORD"; +static const char linetag[]="LINE"; +static const char paragraphtag[]="PARAGRAPH"; +static const char regiontag[]="REGION"; +static const char pagecolumntag[]="PAGECOLUMN"; +static const char hiddentexttag[]="HIDDENTEXT"; +static const char metadatatag[]="METADATA"; + +class lt_XMLParser::Impl : public lt_XMLParser +{ +public: + Impl(void); + virtual ~Impl(); + /// Parse the specified bytestream. + virtual void parse(const GP<ByteStream> &bs); + /// Parse the specified tags - this one does all the work + virtual void parse(const lt_XMLTags &tags); + /// write to disk. + virtual void save(void); + /// erase. + virtual void empty(void); +protected: + GP<DjVuFile> get_file(const GURL &url,GUTF8String page); + + void parse_anno(const int width, const int height, + const lt_XMLTags &GObject, + GMap<GUTF8String,GP<lt_XMLTags> > &Maps, DjVuFile &dfile); + + void parse_text(const int width, const int height, + const lt_XMLTags &GObject, DjVuFile &dfile); + + void parse_meta(const lt_XMLTags &GObject, DjVuFile &dfile); + + void ChangeAnno( const int width, const int height, + DjVuFile &dfile, const lt_XMLTags &map); + + void ChangeInfo(DjVuFile &dfile,const int dpi,const double gamma); + + void ChangeText( const int width, const int height, + DjVuFile &dfile, const lt_XMLTags &map); + + void ChangeMeta( DjVuFile &dfile, const lt_XMLTags &map); + + void ChangeTextOCR( const GUTF8String &value, + const int width, const int height, + const GP<DjVuFile> &dfile); + + // we may want to make these list of modified file static so + // they only needed to be loaded and saved once. + + GMap<GUTF8String,GP<DjVuFile> > m_files; + GMap<GUTF8String,GP<DjVuDocument> > m_docs; + + GURL m_codebase; + GCriticalSection xmlparser_lock; +}; + +static GP<ByteStream> +OCRcallback( + void * const xarg, + lt_XMLParser::mapOCRcallback * const xcallback, + const GUTF8String &value=GUTF8String(), + const GP<DjVuImage> &image=0 ); + +static inline GP<ByteStream> +OCRcallback(const GUTF8String &value, const GP<DjVuImage> &image) +{ + return OCRcallback(0,0,value,image); +} + +lt_XMLParser::lt_XMLParser() {} +lt_XMLParser::~lt_XMLParser() {} +lt_XMLParser::Impl::Impl() {} +lt_XMLParser::Impl::~Impl() {} + +GP<lt_XMLParser> +lt_XMLParser::create(void) +{ + return new lt_XMLParser::Impl; +} + +// helper function for args +static void +intList(GUTF8String coords, GList<int> &retval) +{ + int pos=0; + while(coords.length()) + { + int epos; + unsigned long i=coords.toLong(pos,epos,10); + if(epos>=0) + { + retval.append(i); + const int n=coords.nextNonSpace(epos); + if(coords[n] != ',') + break; + pos=n+1; + } + } +} + +void +lt_XMLParser::Impl::empty(void) +{ + GCriticalSectionLock lock(&xmlparser_lock); + m_files.empty(); + m_docs.empty(); +} + +void +lt_XMLParser::Impl::save(void) +{ + GCriticalSectionLock lock(&xmlparser_lock); + for(GPosition pos=m_docs;pos;++pos) + { + const GP<DjVuDocument> doc(m_docs[pos]); + const GURL url=doc->get_init_url(); + + DEBUG_MSG("Saving "<<(const char *)url<<" with new text and annotations\n"); + const bool bundle=doc->is_bundled()||(doc->get_doc_type()==DjVuDocument::SINGLE_PAGE); + doc->save_as(url,bundle); + } + empty(); +} + +void +lt_XMLParser::Impl::parse(const GP<ByteStream> &bs) +{ + const GP<lt_XMLTags> tags(lt_XMLTags::create(bs)); + parse(*tags); +} + +static const GMap<GUTF8String,GMapArea::BorderType> & +BorderTypeMap(void) +{ + static GMap<GUTF8String,GMapArea::BorderType> typeMap; + if (! typeMap.size()) + { + typeMap["none"]=GMapArea::NO_BORDER; + typeMap["xor"]=GMapArea::XOR_BORDER; + typeMap["solid"]=GMapArea::SOLID_BORDER; + typeMap["default"]=GMapArea::SOLID_BORDER; + typeMap["shadowout"]=GMapArea::SHADOW_OUT_BORDER; + typeMap["shadowin"]=GMapArea::SHADOW_IN_BORDER; + typeMap["etchedin"]=GMapArea::SHADOW_EIN_BORDER; + typeMap["etchedout"]=GMapArea::SHADOW_EOUT_BORDER; + } + return typeMap; +} + +static unsigned long +convertToColor(const GUTF8String &s) +{ + unsigned long retval=0; + if(s.length()) + { + int endpos; + if(s[0] == '#') + { + retval=s.substr(1,-1).toULong(0,endpos,16); + } + if(endpos < 0) + { + G_THROW( (ERR_MSG("XMLAnno.bad_color") "\t")+s ); + } + } + return retval; +} + +void +lt_XMLParser::Impl::ChangeInfo(DjVuFile &dfile,const int dpi,const double gamma) +{ + GP<DjVuInfo> info; + if(dpi >= 5 && dpi <= 4800) + { + dfile.resume_decode(true); + if(dfile.info && (dpi != dfile.info->dpi) ) + { + info=new DjVuInfo(*dfile.info); + info->dpi=dpi; + } + } + if(gamma >= 0.1 && gamma <= 5.0) + { + dfile.resume_decode(true); + if(dfile.info && (gamma != dfile.info->gamma) ) + { + if(!info) + info=new DjVuInfo(*dfile.info); + info->gamma=gamma; + } + } + if(info) + { + dfile.change_info(info); + } +} + +void +lt_XMLParser::Impl::ChangeAnno( + const int width, const int height, + DjVuFile &dfile, + const lt_XMLTags &map ) +{ + dfile.resume_decode(true); + const GP<DjVuInfo> info(dfile.info); + const GP<DjVuAnno> ganno(DjVuAnno::create()); + DjVuAnno &anno=*ganno; + GPosition map_pos; + map_pos=map.contains(areatag); + if(dfile.contains_anno()) + { + GP<ByteStream> annobs=dfile.get_merged_anno(); + if(annobs) + { + anno.decode(annobs); + if(anno.ant && info) + { + anno.ant->map_areas.empty(); + } + } +// dfile.remove_anno(); + } + if(info && map_pos) + { + const int h=info->height; + const int w=info->width; + double ws=1.0; + double hs=1.0; + if(width && width != w) + { + ws=((double)w)/((double)width); + } + if(height && height != h) + { + hs=((double)h)/((double)height); + } + if(!anno.ant) + { + anno.ant=DjVuANT::create(); + } + GPList<GMapArea> &map_areas=anno.ant->map_areas; + map_areas.empty(); + GPList<lt_XMLTags> gareas=map[map_pos]; + for(GPosition pos=gareas;pos;++pos) + { + if(gareas[pos]) + { + lt_XMLTags &areas=*(gareas[pos]); + GMap<GUTF8String,GUTF8String> args(areas.get_args()); + GList<int> coords; + // ****************************************************** + // Parse the coords attribute: first read the raw data into + // a list, then scale the x, y data into another list. For + // circles, you also get a radius element with (looks like an x + // with no matching y). + // ****************************************************** + { + GPosition coords_pos=args.contains("coords"); + if(coords_pos) + { + GList<int> raw_coords; + intList(args[coords_pos],raw_coords); + for(GPosition raw_pos=raw_coords;raw_pos;++raw_pos) + { + const int r=raw_coords[raw_pos]; + const int x=(int)(ws*(double)r+0.5); + coords.append(x); + int y=h-1; + if(! ++raw_pos) + { + y-=(int)(hs*(double)r+0.5); + }else + { + y-=(int)(hs*(double)raw_coords[raw_pos]+0.5); + } + coords.append(y); +// DjVuPrintMessage("Coords (%d,%d)\n",x,y); + } + } + } + GUTF8String shape; + { + GPosition shape_pos=args.contains("shape"); + if(shape_pos) + { + shape=args[shape_pos]; + } + } + GP<GMapArea> a; + if(shape == "default") + { + GRect rect(0,0,w,h); + a=GMapRect::create(rect); + }else if(!shape.length() || shape == "rect") + { + int xx[4]; + int i=0; + for(GPosition rect_pos=coords;(rect_pos)&&(i<4);++rect_pos,++i) + { + xx[i]=coords[rect_pos]; + } + if(i!=4) + { + G_THROW( ERR_MSG("XMLAnno.bad_rect") ); + } + int xmin,xmax; + if(xx[0]>xx[2]) + { + xmax=xx[0]; + xmin=xx[2]; + }else + { + xmin=xx[0]; + xmax=xx[2]; + } + int ymin,ymax; + if(xx[1]>xx[3]) + { + ymax=xx[1]; + ymin=xx[3]; + }else + { + ymin=xx[1]; + ymax=xx[3]; + } + GRect rect(xmin,ymin,xmax-xmin,ymax-ymin); + a=GMapRect::create(rect); + }else if(shape == "circle") + { + int xx[4]; + int i=0; + GPosition rect_pos=coords.lastpos(); + if(rect_pos) + { + coords.append(coords[rect_pos]); + for(rect_pos=coords;(rect_pos)&&(i<4);++rect_pos) + { + xx[i++]=coords[rect_pos]; + } + } + if(i!=4) + { + G_THROW( ERR_MSG("XMLAnno.bad_circle") ); + } + int x=xx[0],y=xx[1],rx=xx[2],ry=(h-xx[3])-1; + GRect rect(x-rx,y-ry,2*rx,2*ry); + a=GMapOval::create(rect); + }else if(shape == "oval") + { + int xx[4]; + int i=0; + for(GPosition rect_pos=coords;(rect_pos)&&(i<4);++rect_pos,++i) + { + xx[i]=coords[rect_pos]; + } + if(i!=4) + { + G_THROW( ERR_MSG("XMLAnno.bad_oval") ); + } + int xmin,xmax; + if(xx[0]>xx[2]) + { + xmax=xx[0]; + xmin=xx[2]; + }else + { + xmin=xx[0]; + xmax=xx[2]; + } + int ymin,ymax; + if(xx[1]>xx[3]) + { + ymax=xx[1]; + ymin=xx[3]; + }else + { + ymin=xx[1]; + ymax=xx[3]; + } + GRect rect(xmin,ymin,xmax-xmin,ymax-ymin); + a=GMapOval::create(rect); + }else if(shape == "poly") + { + GP<GMapPoly> p=GMapPoly::create(); + for(GPosition poly_pos=coords;poly_pos;++poly_pos) + { + int x=coords[poly_pos]; + if(! ++poly_pos) + break; + int y=coords[poly_pos]; + p->add_vertex(x,y); + } + p->close_poly(); + a=p; + }else + { + G_THROW( ( ERR_MSG("XMLAnno.unknown_shape") "\t")+shape ); + } + if(a) + { + GPosition pos; + if((pos=args.contains("href"))) + { + a->url=args[pos]; + } + if((pos=args.contains("target"))) + { + a->target=args[pos]; + } + if((pos=args.contains("alt"))) + { + a->comment=args[pos]; + } + if((pos=args.contains("bordertype"))) + { + GUTF8String b=args[pos]; + static const GMap<GUTF8String,GMapArea::BorderType> typeMap=BorderTypeMap(); + if((pos=typeMap.contains(b))) + { + a->border_type=typeMap[pos]; + }else + { + G_THROW( (ERR_MSG("XMLAnno.unknown_border") "\t")+b ); + } + } + a->border_always_visible=!!args.contains("visible"); + if((pos=args.contains("bordercolor"))) + { + a->border_color=convertToColor(args[pos]); + } + if((pos=args.contains("highlight"))) + { + a->hilite_color=convertToColor(args[pos]); + } + if((pos=args.contains("border"))) + { + a->border_width=args[pos].toInt(); //atoi(args[pos]); + } + map_areas.append(a); + } + } + } + } + dfile.set_modified(true); + dfile.anno=ByteStream::create(); + anno.encode(dfile.anno); +} + +GP<DjVuFile> +lt_XMLParser::Impl::get_file(const GURL &url,GUTF8String id) +{ + GP<DjVuFile> dfile; + GP<DjVuDocument> doc; + GCriticalSectionLock lock(&xmlparser_lock); + { + GPosition pos=m_docs.contains(url.get_string()); + if(pos) + { + doc=m_docs[pos]; + }else + { + doc=DjVuDocument::create_wait(url); + if(! doc->wait_for_complete_init()) + { + G_THROW(( ERR_MSG("XMLAnno.fail_init") "\t")+url.get_string() ); + } + m_docs[url.get_string()]=doc; + } + if(id.is_int()) + { + const int xpage=id.toInt(); //atoi((char const *)page); + if(xpage>0) + id=doc->page_to_id(xpage-1); + }else if(!id.length()) + { + id=doc->page_to_id(0); + } + } + const GURL fileurl(doc->id_to_url(id)); + GPosition dpos(m_files.contains(fileurl.get_string())); + if(!dpos) + { + if(!doc->get_id_list().contains(id)) + { + G_THROW( ERR_MSG("XMLAnno.bad_page") ); + } + dfile=doc->get_djvu_file(id,false); + if(!dfile) + { + G_THROW( ERR_MSG("XMLAnno.bad_page") ); + } + m_files[fileurl.get_string()]=dfile; + }else + { + dfile=m_files[dpos]; + } + return dfile; +} + +void +lt_XMLParser::Impl::parse(const lt_XMLTags &tags) +{ + const GPList<lt_XMLTags> Body(tags.get_Tags(bodytag)); + GPosition pos=Body; + + if(!pos || (pos != Body.lastpos())) + { + G_THROW( ERR_MSG("XMLAnno.extra_body") ); + } + const GP<lt_XMLTags> GBody(Body[pos]); + if(!GBody) + { + G_THROW( ERR_MSG("XMLAnno.no_body") ); + } + + GMap<GUTF8String,GP<lt_XMLTags> > Maps; + lt_XMLTags::get_Maps(maptag,"name",Body,Maps); + + const GPList<lt_XMLTags> Objects(GBody->get_Tags(objecttag)); + lt_XMLTags::get_Maps(maptag,"name",Objects,Maps); + + for(GPosition Objpos=Objects;Objpos;++Objpos) + { + lt_XMLTags &GObject=*Objects[Objpos]; + // Map of attributes to value (e.g. "width" --> "500") + const GMap<GUTF8String,GUTF8String> &args=GObject.get_args(); + GURL codebase; + { + DEBUG_MSG("Setting up codebase... m_codebase = " << m_codebase << "\n"); + GPosition codebasePos=args.contains("codebase"); + // If user specified a codebase attribute, assume it is correct (absolute URL): + // the GURL constructor will throw an exception if it isn't + if(codebasePos) + { + codebase=GURL::UTF8(args[codebasePos]); + }else if (m_codebase.is_dir()) + { + codebase=m_codebase; + }else + { + codebase=GURL::Filename::UTF8(GOS::cwd()); + } + DEBUG_MSG("codebase = " << codebase << "\n"); + } + // the data attribute specifies the input file. This can be + // either an absolute URL (starts with file:/) or a relative + // URL (for now, just a path and file name). If it's absolute, + // our GURL will adequately wrap it. If it's relative, we need + // to use the codebase attribute to form an absolute URL first. + GPosition datapos=args.contains("data"); + if(datapos) + { + bool isDjVuType=false; + GPosition typePos(args.contains("type")); + if(typePos) + { + if(args[typePos] != mimetype) + { +// DjVuPrintErrorUTF8("Ignoring %s Object tag\n",mimetype); + continue; + } + isDjVuType=true; + } + const GURL url=GURL::UTF8(args[datapos],(args[datapos][0] == '/')?codebase.base():codebase); + int width; + { + GPosition widthPos=args.contains("width"); + width=(widthPos)?args[widthPos].toInt():0; + } + int height; + { + GPosition heightPos=args.contains("height"); + height=(heightPos)?args[heightPos].toInt():0; + } + GUTF8String gamma; + GUTF8String dpi; + GUTF8String page; + GUTF8String do_ocr; + { + GPosition paramPos(GObject.contains(paramtag)); + if(paramPos) + { + const GPList<lt_XMLTags> Params(GObject[paramPos]); + for(GPosition loc=Params;loc;++loc) + { + const GMap<GUTF8String,GUTF8String> &pargs=Params[loc]->get_args(); + GPosition namepos=pargs.contains("name"); + if(namepos) + { + GPosition valuepos=pargs.contains("value"); + if(valuepos) + { + const GUTF8String name=pargs[namepos].downcase(); + const GUTF8String &value=pargs[valuepos]; + if(name == "flags") + { + GMap<GUTF8String,GUTF8String> args; + lt_XMLTags::ParseValues(value,args,true); + if(args.contains("page")) + { + page=args["page"]; + } + if(args.contains("dpi")) + { + dpi=args["dpi"]; + } + if(args.contains("gamma")) + { + gamma=args["gamma"]; + } + if(args.contains("ocr")) + { + do_ocr=args["ocr"]; + } + }else if(name == "page") + { + page=value; + }else if(name == "dpi") + { + dpi=value; + }else if(name == "gamma") + { + gamma=value; + }else if(name == "ocr") + { + do_ocr=value; + } + } + } + } + } + } + const GP<DjVuFile> dfile(get_file(url,page)); + if(dpi.is_int() || gamma.is_float()) + { + int pos=0; + ChangeInfo(*dfile,dpi.toInt(),gamma.toDouble(pos,pos)); + } + parse_anno(width,height,GObject,Maps,*dfile); + parse_meta(GObject,*dfile); + parse_text(width,height,GObject,*dfile); + ChangeTextOCR(do_ocr,width,height,dfile); + } + } +} + +void +lt_XMLParser::Impl::parse_anno( + const int width, + const int height, + const lt_XMLTags &GObject, + GMap<GUTF8String,GP<lt_XMLTags> > &Maps, + DjVuFile &dfile ) +{ + GP<lt_XMLTags> map; + { + GPosition usemappos=GObject.get_args().contains("usemap"); + if(usemappos) + { + const GUTF8String mapname(GObject.get_args()[usemappos]); + GPosition mappos=Maps.contains(mapname); + if(!mappos) + { + G_THROW((ERR_MSG("XMLAnno.map_find") "\t")+mapname ); + }else + { + map=Maps[mappos]; + } + } + } + if(map) + { + ChangeAnno(width,height,dfile,*map); + } +} + +#ifdef max +#undef max +#endif +template<class TYPE> +static inline TYPE max(TYPE a,TYPE b) { return (a>b)?a:b; } +#ifdef min +#undef min +#endif +template<class TYPE> +static inline TYPE min(TYPE a,TYPE b) { return (a<b)?a:b; } + +// used to build the zone tree +// true is returned if the GRect is known for this object, +// and false, if the rectangle's size is just the parent size. +static bool +make_child_layer( + DjVuTXT::Zone &parent, + const lt_XMLTags &tag, ByteStream &bs, + const int height, const double ws, const double hs) +{ + bool retval=true; + // the plugin thinks there are only Pages, Lines and Words + // so we don't make Paragraphs, Regions and Columns zones + // if we did the plugin is not able to search the text but + // DjVuToText writes out all the text anyway + DjVuTXT::Zone *self_ptr; + char sepchar; + const GUTF8String name(tag.get_name()); + if(name == wordtag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::WORD; + sepchar=' '; + }else if(name == linetag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::LINE; + sepchar=DjVuTXT::end_of_line; + }else if(name == paragraphtag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::PARAGRAPH; + sepchar=DjVuTXT::end_of_paragraph; + }else if(name == regiontag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::REGION; + sepchar=DjVuTXT::end_of_region; + }else if(name == pagecolumntag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::COLUMN; + sepchar=DjVuTXT::end_of_column; + }else + { + self_ptr = &parent; + self_ptr->ztype = DjVuTXT::PAGE; + sepchar=0; + } + DjVuTXT::Zone &self = *self_ptr; + self.text_start = bs.tell(); + int &xmin=self.rect.xmin, &ymin=self.rect.ymin, + &xmax=self.rect.xmax, &ymax=self.rect.ymax; + GRect default_rect; + default_rect.xmin=max(parent.rect.xmax,parent.rect.xmin); + default_rect.xmax=min(parent.rect.xmax,parent.rect.xmin); + default_rect.ymin=max(parent.rect.ymax,parent.rect.ymin); + default_rect.ymax=min(parent.rect.ymax,parent.rect.ymin); + // Now if there are coordinates, use those. + GPosition pos(tag.get_args().contains("coords")); + if(pos) + { + GList<int> rectArgs; + intList(tag.get_args()[pos], rectArgs); + if((pos=rectArgs)) + { + xmin=(int)(ws*(double)rectArgs[pos]); + if(++pos) + { + ymin=(height-1)-(int)(hs*(double)rectArgs[pos]); + if(++pos) + { + xmax=(int)(ws*(double)rectArgs[pos]); + if(++pos) + { + ymax=(height-1)-(int)(hs*(double)rectArgs[pos]); + if(xmin>xmax) // Make sure xmin is really minimum + { + const int t=xmin; + xmin=xmax; + xmax=t; + } + if(ymin>ymax) // Make sure ymin is really minimum + { + const int t=ymin; + ymin=ymax; + ymax=t; + } + } + } + } + } + } + if(self.ztype == DjVuTXT::WORD) + { + if(! pos) + { + self.rect=default_rect; + retval=false; + } + const GUTF8String raw(tag.get_raw().fromEscaped()); + const int i=raw.nextNonSpace(0); + bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i)); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + }else if(pos) + { + pos=tag.get_content(); + if(pos) + { + for(pos=tag.get_content(); pos; ++pos) + { + const GP<lt_XMLTags> t(tag.get_content()[pos].tag); + make_child_layer(self, *t, bs, height,ws,hs); + } + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + }else + { + const GUTF8String raw(tag.get_raw().fromEscaped()); + const int i=raw.nextNonSpace(0); + bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i)); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + } + }else + { + self.rect=default_rect; + if((pos=tag.get_content())) + { + do + { + const GP<lt_XMLTags> t(tag.get_content()[pos].tag); + const GRect save_rect(self.rect); + self.rect=default_rect; + if(retval=make_child_layer(self, *t, bs, height,ws,hs)) + { + xmin=min(save_rect.xmin,xmin); + xmax=max(save_rect.xmax,xmax); + ymin=min(save_rect.ymin,ymin); + ymax=max(save_rect.ymax,ymax); + }else + { + // If the child doesn't have coordinates, we need to use a box + // at least as big as the parent's coordinates. + xmin=min(save_rect.xmin,default_rect.xmax); + xmax=max(save_rect.xmax,default_rect.xmin); + ymin=min(save_rect.ymin,default_rect.ymax); + ymax=max(save_rect.ymax,default_rect.ymin); + for(; pos; ++pos) + { + const GP<lt_XMLTags> t(tag.get_content()[pos].tag); + make_child_layer(self, *t, bs, height,ws,hs); + } + break; + } + } while(++pos); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + }else + { + const GUTF8String raw(tag.get_raw().fromEscaped()); + const int i=raw.nextNonSpace(0); + bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i)); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + } + } + parent.rect.xmin=min(xmin,parent.rect.xmin); + parent.rect.ymin=min(ymin,parent.rect.ymin); + parent.rect.xmax=max(xmax,parent.rect.xmax); + parent.rect.ymax=max(ymax,parent.rect.ymax); + if(xmin>xmax) + { + const int t=xmin; + xmin=xmax; + xmax=t; + } + if(ymin>ymax) + { + const int t=ymin; + ymin=ymax; + ymax=t; + } +// DjVuPrintMessage("(%d,%d)(%d,%d)<<<\\%o>>>\n", +// xmin,ymin,xmax,ymax, sepchar); + return retval; +} + +void +lt_XMLParser::Impl::ChangeTextOCR( + const GUTF8String &value, + const int width, + const int height, + const GP<DjVuFile> &dfile) +{ + if(value.length() && value.downcase() != "false") + { + const GP<ByteStream> bs=OCRcallback(value,DjVuImage::create(dfile)); + if( bs && bs->size() ) + { + const GP<lt_XMLTags> tags(lt_XMLTags::create(bs)); + ChangeText(width,height,*dfile,*tags); + } + } +} + +void +lt_XMLParser::Impl::ChangeMeta( + DjVuFile &dfile, const lt_XMLTags &tags ) +{ + dfile.resume_decode(true); + GP<ByteStream> gbs(ByteStream::create()); + tags.write(*gbs,false); + gbs->seek(0L); + GUTF8String raw(gbs->getAsUTF8()); + if(raw.length()) + { + //GUTF8String gs="<"+(metadatatag+(">"+raw))+"</"+metadatatag+">\n"); + dfile.change_meta(raw+"\n"); + }else + { + dfile.change_meta(GUTF8String()); + } +} + +void +lt_XMLParser::Impl::ChangeText( + const int width, const int height, + DjVuFile &dfile, const lt_XMLTags &tags ) +{ + dfile.resume_decode(true); + + GP<DjVuText> text = DjVuText::create(); + GP<DjVuTXT> txt = text->txt = DjVuTXT::create(); + + // to store the new text + GP<ByteStream> textbs = ByteStream::create(); + + GP<DjVuInfo> info=(dfile.info); + if(info) + { + const int h=info->height; + const int w=info->width; + txt->page_zone.text_start = 0; + DjVuTXT::Zone &parent=txt->page_zone; + parent.rect.xmin=0; + parent.rect.ymin=0; + parent.rect.ymax=h; + parent.rect.xmax=w; + double ws=1.0; + if(width && width != w) + { + ws=((double)w)/((double)width); + } + double hs=1.0; + if(height && height != h) + { + hs=((double)h)/((double)height); + } + make_child_layer(parent, tags, *textbs, h, ws,hs); + textbs->write8(0); + long len = textbs->tell(); + txt->page_zone.text_length = len; + textbs->seek(0,SEEK_SET); + textbs->read(txt->textUTF8.getbuf(len), len); + + dfile.change_text(txt,false); + } +} + +void +lt_XMLParser::Impl::parse_text( + const int width, + const int height, + const lt_XMLTags &GObject, + DjVuFile &dfile ) +{ + GPosition textPos = GObject.contains(hiddentexttag); + if(textPos) + { + // loop through the hidden text - there should only be one + // if there are more ??only the last one will be saved?? + GPList<lt_XMLTags> textTags = GObject[textPos]; + GPosition pos = textTags; + ChangeText(width,height,dfile,*textTags[pos]); + } +} + +void +lt_XMLParser::Impl::parse_meta( + const lt_XMLTags &GObject, + DjVuFile &dfile ) +{ + GPosition metaPos = GObject.contains(metadatatag); + if(metaPos) + { + // loop through the hidden text - there should only be one + // if there are more ??only the last one will be saved?? + GPList<lt_XMLTags> metaTags = GObject[metaPos]; + GPosition pos = metaTags; + ChangeMeta(dfile,*metaTags[pos]); + } +} + +static GP<ByteStream> +OCRcallback( + void * const xarg, + lt_XMLParser::mapOCRcallback * const xcallback, + const GUTF8String &value, + const GP<DjVuImage> &image ) +{ + GP<ByteStream> retval; + static void *arg=0; + static lt_XMLParser::mapOCRcallback *callback=0; + if(image) + { + if(callback) + retval=callback(arg,value,image); + }else + { + arg=xarg; + callback=xcallback; + } + return retval; +} + +void +lt_XMLParser::setOCRcallback( + void * const arg, + mapOCRcallback * const callback) +{ + ::OCRcallback(arg,callback); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/XMLParser.h b/kviewshell/plugins/djvu/libdjvu/XMLParser.h new file mode 100644 index 00000000..08b6d508 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLParser.h @@ -0,0 +1,123 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLParser.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _LT_XMLPARSER__ +#define _LT_XMLPARSER__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "GContainer.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class lt_XMLTags; +class lt_XMLContents; +class DjVuFile; +class DjVuDocument; +class DjVuImage; +class GBitmap; + +// this is the base class for using XML to change DjVu Docs. + +class lt_XMLParser : public GPEnabled +{ +public: + class Impl; + typedef GP<ByteStream> mapOCRcallback( + void *,const GUTF8String &value,const GP<DjVuImage> &); +protected: + lt_XMLParser(void); + virtual ~lt_XMLParser(); +public: + static GP<lt_XMLParser> create(void); + /// Parse the specified bytestream. + virtual void parse(const GP<ByteStream> &bs) = 0; + /// Parse the specified tags - this one does all the work + virtual void parse(const lt_XMLTags &tags) = 0; + /// write to disk. + virtual void save(void) = 0; + /// erase. + virtual void empty(void) = 0; + + // helper function for args + static void setOCRcallback( + void * const arg,mapOCRcallback * const ); +}; + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* _LT_XMLPARSER__ */ + + diff --git a/kviewshell/plugins/djvu/libdjvu/XMLTags.cpp b/kviewshell/plugins/djvu/libdjvu/XMLTags.cpp new file mode 100644 index 00000000..2511a585 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLTags.cpp @@ -0,0 +1,417 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLTags.cpp,v 1.12 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "XMLTags.h" +#include "UnicodeByteStream.h" +#include <ctype.h> +#if HAS_WCTYPE +#include <wctype.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +lt_XMLContents::lt_XMLContents(void) {} + +lt_XMLContents::lt_XMLContents(GP<lt_XMLTags> t) +{ + tag=t; +} + +static GUTF8String +getargn(char const tag[], char const *&t) +{ + char const *s; + for(s=tag;isspace(*s);s++); + for(t=s;(*t)&&((*t)!='/')&&((*t)!='>')&&((*t)!='=')&&!isspace(*t);++t); + return GUTF8String(s,t-s); +} + +static GUTF8String +getargv(char const tag[], char const *&t) +{ + GUTF8String retval; + if(tag && tag[0] == '=') + { + char const *s=t=tag+1; + if((*t == '"')||(*t == '\47')) + { + char const q=*(t++); + for(s++;(*t)&&((*t)!=q)&&((*t)!='>');++t); + retval=GUTF8String(s,t-s); + if (t[0] == q) + { + ++t; + } + }else + { + for(t=s;(*t)&&((*t)!='/')&&((*t)!='>')&&!isspace(*t);++t); + retval=GUTF8String(s,t-s); + } + }else + { + t=tag; + } + return retval; +} + +static GUTF8String +tagtoname(char const tag[],char const *&t) +{ + char const *s; + for(s=tag;isspace(*s);s++); + for(t=s;(*t)&&((*t)!='>')&&((*t)!='/')&&!isspace(*t);++t); + return GUTF8String(s,t-s); +} + +static inline GUTF8String +tagtoname(char const tag[]) +{ + char const *t; + return tagtoname(tag,t); +} + +static inline bool +isspaces(const GUTF8String &raw) +{ + return (raw.nextNonSpace() == (int)raw.length()); +} + +void +lt_XMLTags::ParseValues(char const *t, GMap<GUTF8String,GUTF8String> &args,bool downcase) +{ + GUTF8String argn; + char const *tt; + while((argn=getargn(t,tt)).length()) + { + if(downcase) + argn=argn.downcase(); + args[argn]=getargv(tt,t).fromEscaped(); + } +} + +lt_XMLTags::~lt_XMLTags() {} + +lt_XMLTags::lt_XMLTags(void) : startline(0) {} + +lt_XMLTags::lt_XMLTags(const char n[]) : startline(0) +{ + char const *t; + name=tagtoname(n,t); + ParseValues(t,args); +} + +void +lt_XMLTags::init(const GP<ByteStream> &bs) +{ + GP<XMLByteStream> gxmlbs=XMLByteStream::create(bs); + init(*gxmlbs); +} + +void +lt_XMLTags::init(const GURL &url) +{ + const GP<ByteStream> bs=ByteStream::create(url,"rb"); + init(bs); +} + +void +lt_XMLTags::init(XMLByteStream &xmlbs) +{ + if(!get_count()) + { + G_THROW( ERR_MSG("XMLTags.no_GP") ); + } + GPList<lt_XMLTags> level; + GUTF8String tag,raw(xmlbs.gets(0,'<',false)); + int linesread=xmlbs.get_lines_read(); + if(!isspaces(raw)) + { + G_THROW( (ERR_MSG("XMLTags.raw_string") "\t")+raw); + } + GUTF8String encoding; + for(int len;(len=(tag=xmlbs.gets(0,'>',true)).length());) + { + if(tag[len-1] != '>') + { + G_THROW((ERR_MSG("XMLTags.bad_tag") "\t")+tag); + } + switch(tag[1]) + { + case '?': + { + while(len < 4 || tag.substr(len-2,len) != "?>") + { + GUTF8String cont(xmlbs.gets(0,'>',true)); + if(!cont.length()) + { + G_THROW( (ERR_MSG("XMLTags.bad_PI") "\t")+tag); + } + len=((tag+=cont).length()); + } + char const *n; + GUTF8String xtag = tag.substr(2,-1); + GUTF8String xname = tagtoname(xtag,n); + if(xname.downcase() == "xml") + { + ParseValues(n,args); + for(GPosition pos=args;pos;++pos) + { + if(args.key(pos) == "encoding") + { + const GUTF8String e=args[pos].upcase(); + if(e != encoding) + { + xmlbs.set_encoding((encoding=e)); + } + } + } + } + break; + } + case '!': + { + if(tag[2] == '-' && tag[3] == '-') + { + while((len < 7) || + (tag.substr(len-3,-1) != "-->")) + { + GUTF8String cont(xmlbs.gets(0,'>',true)); + if(!cont.length()) + { + GUTF8String mesg; + mesg.format( ERR_MSG("XMLTags.bad_comment") "\t%s",(const char *)tag); + G_THROW(mesg); + } + len=((tag+=cont).length()); + } + } + break; + } + case '/': + { + GUTF8String xname=tagtoname(tag.substr(2,-1)); + GPosition last=level.lastpos(); + if(last) + { + if(level[last]->name != xname) + { + G_THROW( (ERR_MSG("XMLTags.unmatched_end") "\t") + +level[last]->name+("\t"+GUTF8String(level[last]->get_Line())) + +("\t"+xname)+("\t"+GUTF8String(linesread+1))); + } + level.del(last); + }else + { + G_THROW( ERR_MSG("XMLTags.bad_form") ); + } + break; + } + default: + { + GPosition last=level.lastpos(); + GP<lt_XMLTags> t; + if(last) + { + t=new lt_XMLTags(tag.substr(1,len-1)); + level[last]->addtag(t); + if(tag[len-2] != '/') + { + level.append(t); + } + }else if(tag[len-2] != '/') + { + char const *n; + GUTF8String xtag = tag.substr(1,-1); + name=tagtoname(xtag, n); + ParseValues(n,args); + t=this; + level.append(t); + }else + { + G_THROW( ERR_MSG("XMLTags.no_body") ); + } + t->set_Line(linesread+1); + break; + } + } + if((raw=xmlbs.gets(0,'<',false))[0]) + { + linesread=xmlbs.get_lines_read(); + GPosition last=level.lastpos(); + if(last) + { + level[last]->addraw(raw); + }else if(!isspaces(raw)) + { + G_THROW(( ERR_MSG("XMLTags.raw_string") "\t")+raw); + } + } + } +} + +GPList<lt_XMLTags> +lt_XMLTags::get_Tags(char const tagname[]) const +{ + GPosition pos=allTags.contains(tagname); + GPList<lt_XMLTags> retval; + return (pos?allTags[pos]:retval); +} + +void +lt_XMLTags::get_Maps(char const tagname[], + char const argn[], + GPList<lt_XMLTags> list, + GMap<GUTF8String, GP<lt_XMLTags> > &map) +{ + for(GPosition pos=list;pos;++pos) + { + GP<lt_XMLTags> &tag=list[pos]; + if(tag) + { + GPosition loc; + if((loc=tag->contains(tagname))) + { + GPList<lt_XMLTags> maps=(GPList<lt_XMLTags> &)((*tag)[loc]); + for(GPosition mloc=maps;mloc;++mloc) + { + GP<lt_XMLTags> gtag=maps[mloc]; + if(gtag) + { + GMap<GUTF8String,GUTF8String> &args=gtag->args; + GPosition gpos; + if((gpos=args.contains(argn))) + { + map[args[gpos]]=gtag; + } + } + } + } + } + } +} + +void +lt_XMLTags::write(ByteStream &bs,bool const top) const +{ + if(name.length()) + { + GUTF8String tag="<"+name; + for(GPosition pos=args;pos;++pos) + { + tag+=GUTF8String(' ')+args.key(pos)+GUTF8String("=\42")+args[pos].toEscaped()+GUTF8String("\42"); + } + GPosition tags=content; + if(tags||raw.length()) + { + tag+=">"; + bs.writall((const char *)tag,tag.length()); + tag="</"+name+">"; + if(raw.length()) + { + bs.writestring(raw); + } + for(;tags;++tags) + { + content[tags].write(bs); + } + }else if(!raw.length()) + { + tag+="/>"; + } + bs.writall((const char *)tag,tag.length()); + } + if(top) + { + bs.writall("\n",1); + } +} + +void +lt_XMLContents::write(ByteStream &bs) const +{ + if(tag) + { + tag->write(bs,false); + } + if(raw.length()) + { + bs.writestring(raw); + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/XMLTags.h b/kviewshell/plugins/djvu/libdjvu/XMLTags.h new file mode 100644 index 00000000..027e629b --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLTags.h @@ -0,0 +1,242 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLTags.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _LT_XMLTAGS__ +#define _LT_XMLTAGS__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "GContainer.h" +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class lt_XMLContents; +class DjVuFile; +class DjVuDocument; +class ByteStream; +class XMLByteStream; +class GURL; + +class lt_XMLTags : public GPEnabled +{ +protected: + lt_XMLTags(); + lt_XMLTags(const char n[]); + +public: + /// Empty creator. + static GP<lt_XMLTags> create(void) { return new lt_XMLTags; } + /// Default the specified tag. + static GP<lt_XMLTags> create(const char n[]) { return new lt_XMLTags(n); } + /// Initialize from the specified URL. + void init(const GURL & url); + /// Create from the specified URL. + static GP<lt_XMLTags> create(const GURL &url); + /// Initialize from the specified bytestream. + void init(const GP<ByteStream> &bs); + /// Create from the specified bytestream. + static GP<lt_XMLTags> create(const GP<ByteStream> &bs); + /// Initialize from an XMLByteStream. + void init(XMLByteStream &xmlbs); + /// Create from an XML bytestream. + static GP<lt_XMLTags> create(XMLByteStream &xmlbs); + /// Non-virtual destructor. + ~lt_XMLTags(); + + inline int get_Line(void) const; + inline const GUTF8String& get_raw(void) const; + inline const GUTF8String& get_name(void) const; + inline const GList<lt_XMLContents>& get_content(void) const; + inline const GMap<GUTF8String,GUTF8String>& get_args(void) const; + inline const GMap<GUTF8String,GPList<lt_XMLTags> >& get_allTags(void) const; + + GPList<lt_XMLTags> get_Tags(char const tagname[]) const; + inline void set_Line(const int xstartline) { startline=xstartline; } + + inline void addtag(GP<lt_XMLTags> x); + inline void addraw(GUTF8String raw); + inline GPosition contains(GUTF8String name) const; + inline const GPList<lt_XMLTags> & operator [] (const GUTF8String name) const; + inline const GPList<lt_XMLTags> & operator [] (const GPosition &pos) const; + static void ParseValues(char const *t, GMap<GUTF8String,GUTF8String> &args,bool downcase=true); + static void get_Maps(char const tagname[],char const argn[], + GPList<lt_XMLTags> list, GMap<GUTF8String, GP<lt_XMLTags> > &map); + void write(ByteStream &bs,bool const top=true) const; + +protected: + GUTF8String name; + GMap<GUTF8String,GUTF8String> args; + GList<lt_XMLContents> content; + GUTF8String raw; + GMap<GUTF8String,GPList<lt_XMLTags> > allTags; + int startline; +}; + +class lt_XMLContents +{ +public: + lt_XMLContents(void); + lt_XMLContents(GP<lt_XMLTags> tag); + GP<lt_XMLTags> tag; + GUTF8String raw; + void write(ByteStream &bs) const; +}; + +inline GP<lt_XMLTags> +lt_XMLTags::create(const GURL &url) +{ + const GP<lt_XMLTags> retval(new lt_XMLTags); + retval->init(url); + return retval; +} + +inline GP<lt_XMLTags> +lt_XMLTags::create(const GP<ByteStream> &bs) +{ + const GP<lt_XMLTags> retval(new lt_XMLTags); + retval->init(bs); + return retval; +} + +inline GP<lt_XMLTags> +lt_XMLTags::create(XMLByteStream &xmlbs) +{ + const GP<lt_XMLTags> retval(new lt_XMLTags); + retval->init(xmlbs); + return retval; +} + +/// Non-virtual destructor. +inline void +lt_XMLTags::addtag (GP<lt_XMLTags> x) +{ + content.append(lt_XMLContents(x)); + allTags[x->name].append(x); +} + +inline void +lt_XMLTags::addraw (GUTF8String r) +{ + GPosition pos=content; + if(pos) + { + content[pos].raw+=r; + }else + { + raw+=r; + } +} + +inline int +lt_XMLTags::get_Line(void) const +{ return startline; } + +inline const GUTF8String & +lt_XMLTags::get_name(void) const { return name; } + +inline const GUTF8String & +lt_XMLTags::get_raw(void) const { return raw; } + +inline const GList<lt_XMLContents> & +lt_XMLTags::get_content(void) const { return content; } + +inline const GMap<GUTF8String,GUTF8String> & +lt_XMLTags::get_args(void) const { return args; } + +inline const GMap<GUTF8String,GPList<lt_XMLTags> > & +lt_XMLTags::get_allTags(void) const { return allTags; } + +inline GPosition +lt_XMLTags::contains(GUTF8String name) const +{ + return allTags.contains(name); +} + +inline const GPList<lt_XMLTags> & +lt_XMLTags::operator [] (const GUTF8String name) const +{ + return allTags[name]; +} + +inline const GPList<lt_XMLTags> & +lt_XMLTags::operator [] (const GPosition &pos) const +{ + return allTags[pos]; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* _LT_XMLTAGS__ */ + + diff --git a/kviewshell/plugins/djvu/libdjvu/ZPCodec.cpp b/kviewshell/plugins/djvu/libdjvu/ZPCodec.cpp new file mode 100644 index 00000000..89461872 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ZPCodec.cpp @@ -0,0 +1,1292 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ZPCodec.cpp,v 1.10 2004/08/06 14:49:34 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "ZPCodec.h" +#include "ByteStream.h" +#include "GException.h" +#include <stdlib.h> +#include <assert.h> +#include <math.h> +#include <stdio.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +//////////////////////////////////////////////////////////////// +// CODER SPECIFICATION +//////////////////////////////////////////////////////////////// + + +#ifndef ZPCODER +#ifndef ZCODER +#define ZPCODER +#endif +#endif +#ifdef ZCODER + +// The ZCODER option is provided for documentation purposes only. The ZCODER +// might come dangerously close to U.S. patent 5059976 (Mitsubishi). This is +// why we always use the ZPCODER, although it usually produces 1% larger files. +#warning "The ZCODER may infringe non-LizardTech patent(s)." +#warning "You should use the ZPCODER instead." +#endif + + +//////////////////////////////////////////////////////////////// +// ZP CODER DEFAULT ADAPTATION TABLE +//////////////////////////////////////////////////////////////// + + +// See ZPCodec::ZPCodec to see how this +// default table is modified when not +// using the DjVu compatibility mode. + + +static ZPCodec::Table default_ztable[256] = +{ +#ifdef ZPCODER + /* This table has been designed for the ZPCoder + * by running the following command in file 'zptable.sn': + * (fast-crude (steady-mat 0.0035 0.0002) 260))) + */ + { 0x8000, 0x0000, 84, 145 }, /* 000: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 3, 4 }, /* 001: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 4, 3 }, /* 002: p=0.500000 ( 0, 0) */ + { 0x6bbd, 0x10a5, 5, 1 }, /* 003: p=0.465226 ( 0, 0) */ + { 0x6bbd, 0x10a5, 6, 2 }, /* 004: p=0.465226 ( 0, 0) */ + { 0x5d45, 0x1f28, 7, 3 }, /* 005: p=0.430708 ( 0, 0) */ + { 0x5d45, 0x1f28, 8, 4 }, /* 006: p=0.430708 ( 0, 0) */ + { 0x51b9, 0x2bd3, 9, 5 }, /* 007: p=0.396718 ( 0, 0) */ + { 0x51b9, 0x2bd3, 10, 6 }, /* 008: p=0.396718 ( 0, 0) */ + { 0x4813, 0x36e3, 11, 7 }, /* 009: p=0.363535 ( 0, 0) */ + { 0x4813, 0x36e3, 12, 8 }, /* 010: p=0.363535 ( 0, 0) */ + { 0x3fd5, 0x408c, 13, 9 }, /* 011: p=0.331418 ( 0, 0) */ + { 0x3fd5, 0x408c, 14, 10 }, /* 012: p=0.331418 ( 0, 0) */ + { 0x38b1, 0x48fd, 15, 11 }, /* 013: p=0.300585 ( 0, 0) */ + { 0x38b1, 0x48fd, 16, 12 }, /* 014: p=0.300585 ( 0, 0) */ + { 0x3275, 0x505d, 17, 13 }, /* 015: p=0.271213 ( 0, 0) */ + { 0x3275, 0x505d, 18, 14 }, /* 016: p=0.271213 ( 0, 0) */ + { 0x2cfd, 0x56d0, 19, 15 }, /* 017: p=0.243438 ( 0, 0) */ + { 0x2cfd, 0x56d0, 20, 16 }, /* 018: p=0.243438 ( 0, 0) */ + { 0x2825, 0x5c71, 21, 17 }, /* 019: p=0.217391 ( 0, 0) */ + { 0x2825, 0x5c71, 22, 18 }, /* 020: p=0.217391 ( 0, 0) */ + { 0x23ab, 0x615b, 23, 19 }, /* 021: p=0.193150 ( 0, 0) */ + { 0x23ab, 0x615b, 24, 20 }, /* 022: p=0.193150 ( 0, 0) */ + { 0x1f87, 0x65a5, 25, 21 }, /* 023: p=0.170728 ( 0, 0) */ + { 0x1f87, 0x65a5, 26, 22 }, /* 024: p=0.170728 ( 0, 0) */ + { 0x1bbb, 0x6962, 27, 23 }, /* 025: p=0.150158 ( 0, 0) */ + { 0x1bbb, 0x6962, 28, 24 }, /* 026: p=0.150158 ( 0, 0) */ + { 0x1845, 0x6ca2, 29, 25 }, /* 027: p=0.131418 ( 0, 0) */ + { 0x1845, 0x6ca2, 30, 26 }, /* 028: p=0.131418 ( 0, 0) */ + { 0x1523, 0x6f74, 31, 27 }, /* 029: p=0.114460 ( 0, 0) */ + { 0x1523, 0x6f74, 32, 28 }, /* 030: p=0.114460 ( 0, 0) */ + { 0x1253, 0x71e6, 33, 29 }, /* 031: p=0.099230 ( 0, 0) */ + { 0x1253, 0x71e6, 34, 30 }, /* 032: p=0.099230 ( 0, 0) */ + { 0x0fcf, 0x7404, 35, 31 }, /* 033: p=0.085611 ( 0, 0) */ + { 0x0fcf, 0x7404, 36, 32 }, /* 034: p=0.085611 ( 0, 0) */ + { 0x0d95, 0x75d6, 37, 33 }, /* 035: p=0.073550 ( 0, 0) */ + { 0x0d95, 0x75d6, 38, 34 }, /* 036: p=0.073550 ( 0, 0) */ + { 0x0b9d, 0x7768, 39, 35 }, /* 037: p=0.062888 ( 0, 0) */ + { 0x0b9d, 0x7768, 40, 36 }, /* 038: p=0.062888 ( 0, 0) */ + { 0x09e3, 0x78c2, 41, 37 }, /* 039: p=0.053539 ( 0, 0) */ + { 0x09e3, 0x78c2, 42, 38 }, /* 040: p=0.053539 ( 0, 0) */ + { 0x0861, 0x79ea, 43, 39 }, /* 041: p=0.045365 ( 0, 0) */ + { 0x0861, 0x79ea, 44, 40 }, /* 042: p=0.045365 ( 0, 0) */ + { 0x0711, 0x7ae7, 45, 41 }, /* 043: p=0.038272 ( 0, 0) */ + { 0x0711, 0x7ae7, 46, 42 }, /* 044: p=0.038272 ( 0, 0) */ + { 0x05f1, 0x7bbe, 47, 43 }, /* 045: p=0.032174 ( 0, 0) */ + { 0x05f1, 0x7bbe, 48, 44 }, /* 046: p=0.032174 ( 0, 0) */ + { 0x04f9, 0x7c75, 49, 45 }, /* 047: p=0.026928 ( 0, 0) */ + { 0x04f9, 0x7c75, 50, 46 }, /* 048: p=0.026928 ( 0, 0) */ + { 0x0425, 0x7d0f, 51, 47 }, /* 049: p=0.022444 ( 0, 0) */ + { 0x0425, 0x7d0f, 52, 48 }, /* 050: p=0.022444 ( 0, 0) */ + { 0x0371, 0x7d91, 53, 49 }, /* 051: p=0.018636 ( 0, 0) */ + { 0x0371, 0x7d91, 54, 50 }, /* 052: p=0.018636 ( 0, 0) */ + { 0x02d9, 0x7dfe, 55, 51 }, /* 053: p=0.015421 ( 0, 0) */ + { 0x02d9, 0x7dfe, 56, 52 }, /* 054: p=0.015421 ( 0, 0) */ + { 0x0259, 0x7e5a, 57, 53 }, /* 055: p=0.012713 ( 0, 0) */ + { 0x0259, 0x7e5a, 58, 54 }, /* 056: p=0.012713 ( 0, 0) */ + { 0x01ed, 0x7ea6, 59, 55 }, /* 057: p=0.010419 ( 0, 0) */ + { 0x01ed, 0x7ea6, 60, 56 }, /* 058: p=0.010419 ( 0, 0) */ + { 0x0193, 0x7ee6, 61, 57 }, /* 059: p=0.008525 ( 0, 0) */ + { 0x0193, 0x7ee6, 62, 58 }, /* 060: p=0.008525 ( 0, 0) */ + { 0x0149, 0x7f1a, 63, 59 }, /* 061: p=0.006959 ( 0, 0) */ + { 0x0149, 0x7f1a, 64, 60 }, /* 062: p=0.006959 ( 0, 0) */ + { 0x010b, 0x7f45, 65, 61 }, /* 063: p=0.005648 ( 0, 0) */ + { 0x010b, 0x7f45, 66, 62 }, /* 064: p=0.005648 ( 0, 0) */ + { 0x00d5, 0x7f6b, 67, 63 }, /* 065: p=0.004506 ( 0, 0) */ + { 0x00d5, 0x7f6b, 68, 64 }, /* 066: p=0.004506 ( 0, 0) */ + { 0x00a5, 0x7f8d, 69, 65 }, /* 067: p=0.003480 ( 0, 0) */ + { 0x00a5, 0x7f8d, 70, 66 }, /* 068: p=0.003480 ( 0, 0) */ + { 0x007b, 0x7faa, 71, 67 }, /* 069: p=0.002602 ( 0, 0) */ + { 0x007b, 0x7faa, 72, 68 }, /* 070: p=0.002602 ( 0, 0) */ + { 0x0057, 0x7fc3, 73, 69 }, /* 071: p=0.001843 ( 0, 0) */ + { 0x0057, 0x7fc3, 74, 70 }, /* 072: p=0.001843 ( 0, 0) */ + { 0x003b, 0x7fd7, 75, 71 }, /* 073: p=0.001248 ( 0, 0) */ + { 0x003b, 0x7fd7, 76, 72 }, /* 074: p=0.001248 ( 0, 0) */ + { 0x0023, 0x7fe7, 77, 73 }, /* 075: p=0.000749 ( 0, 0) */ + { 0x0023, 0x7fe7, 78, 74 }, /* 076: p=0.000749 ( 0, 0) */ + { 0x0013, 0x7ff2, 79, 75 }, /* 077: p=0.000402 ( 0, 0) */ + { 0x0013, 0x7ff2, 80, 76 }, /* 078: p=0.000402 ( 0, 0) */ + { 0x0007, 0x7ffa, 81, 77 }, /* 079: p=0.000153 ( 0, 0) */ + { 0x0007, 0x7ffa, 82, 78 }, /* 080: p=0.000153 ( 0, 0) */ + { 0x0001, 0x7fff, 81, 79 }, /* 081: p=0.000027 ( 0, 0) */ + { 0x0001, 0x7fff, 82, 80 }, /* 082: p=0.000027 ( 0, 0) */ + { 0x5695, 0x0000, 9, 85 }, /* 083: p=0.411764 ( 2, 3) */ + { 0x24ee, 0x0000, 86, 226 }, /* 084: p=0.199988 ( 1, 0) */ + { 0x8000, 0x0000, 5, 6 }, /* 085: p=0.500000 ( 3, 3) */ + { 0x0d30, 0x0000, 88, 176 }, /* 086: p=0.071422 ( 4, 0) */ + { 0x481a, 0x0000, 89, 143 }, /* 087: p=0.363634 ( 1, 2) */ + { 0x0481, 0x0000, 90, 138 }, /* 088: p=0.024388 ( 13, 0) */ + { 0x3579, 0x0000, 91, 141 }, /* 089: p=0.285711 ( 1, 3) */ + { 0x017a, 0x0000, 92, 112 }, /* 090: p=0.007999 ( 41, 0) */ + { 0x24ef, 0x0000, 93, 135 }, /* 091: p=0.199997 ( 1, 5) */ + { 0x007b, 0x0000, 94, 104 }, /* 092: p=0.002611 ( 127, 0) */ + { 0x1978, 0x0000, 95, 133 }, /* 093: p=0.137929 ( 1, 8) */ + { 0x0028, 0x0000, 96, 100 }, /* 094: p=0.000849 ( 392, 0) */ + { 0x10ca, 0x0000, 97, 129 }, /* 095: p=0.090907 ( 1, 13) */ + { 0x000d, 0x0000, 82, 98 }, /* 096: p=0.000276 ( 1208, 0) */ + { 0x0b5d, 0x0000, 99, 127 }, /* 097: p=0.061537 ( 1, 20) */ + { 0x0034, 0x0000, 76, 72 }, /* 098: p=0.001102 ( 1208, 1) */ + { 0x078a, 0x0000, 101, 125 }, /* 099: p=0.040815 ( 1, 31) */ + { 0x00a0, 0x0000, 70, 102 }, /* 100: p=0.003387 ( 392, 1) */ + { 0x050f, 0x0000, 103, 123 }, /* 101: p=0.027397 ( 1, 47) */ + { 0x0117, 0x0000, 66, 60 }, /* 102: p=0.005912 ( 392, 2) */ + { 0x0358, 0x0000, 105, 121 }, /* 103: p=0.018099 ( 1, 72) */ + { 0x01ea, 0x0000, 106, 110 }, /* 104: p=0.010362 ( 127, 1) */ + { 0x0234, 0x0000, 107, 119 }, /* 105: p=0.011940 ( 1, 110) */ + { 0x0144, 0x0000, 66, 108 }, /* 106: p=0.006849 ( 193, 1) */ + { 0x0173, 0x0000, 109, 117 }, /* 107: p=0.007858 ( 1, 168) */ + { 0x0234, 0x0000, 60, 54 }, /* 108: p=0.011925 ( 193, 2) */ + { 0x00f5, 0x0000, 111, 115 }, /* 109: p=0.005175 ( 1, 256) */ + { 0x0353, 0x0000, 56, 48 }, /* 110: p=0.017995 ( 127, 2) */ + { 0x00a1, 0x0000, 69, 113 }, /* 111: p=0.003413 ( 1, 389) */ + { 0x05c5, 0x0000, 114, 134 }, /* 112: p=0.031249 ( 41, 1) */ + { 0x011a, 0x0000, 65, 59 }, /* 113: p=0.005957 ( 2, 389) */ + { 0x03cf, 0x0000, 116, 132 }, /* 114: p=0.020618 ( 63, 1) */ + { 0x01aa, 0x0000, 61, 55 }, /* 115: p=0.009020 ( 2, 256) */ + { 0x0285, 0x0000, 118, 130 }, /* 116: p=0.013652 ( 96, 1) */ + { 0x0286, 0x0000, 57, 51 }, /* 117: p=0.013672 ( 2, 168) */ + { 0x01ab, 0x0000, 120, 128 }, /* 118: p=0.009029 ( 146, 1) */ + { 0x03d3, 0x0000, 53, 47 }, /* 119: p=0.020710 ( 2, 110) */ + { 0x011a, 0x0000, 122, 126 }, /* 120: p=0.005961 ( 222, 1) */ + { 0x05c5, 0x0000, 49, 41 }, /* 121: p=0.031250 ( 2, 72) */ + { 0x00ba, 0x0000, 124, 62 }, /* 122: p=0.003925 ( 338, 1) */ + { 0x08ad, 0x0000, 43, 37 }, /* 123: p=0.046979 ( 2, 47) */ + { 0x007a, 0x0000, 72, 66 }, /* 124: p=0.002586 ( 514, 1) */ + { 0x0ccc, 0x0000, 39, 31 }, /* 125: p=0.069306 ( 2, 31) */ + { 0x01eb, 0x0000, 60, 54 }, /* 126: p=0.010386 ( 222, 2) */ + { 0x1302, 0x0000, 33, 25 }, /* 127: p=0.102940 ( 2, 20) */ + { 0x02e6, 0x0000, 56, 50 }, /* 128: p=0.015695 ( 146, 2) */ + { 0x1b81, 0x0000, 29, 131 }, /* 129: p=0.148935 ( 2, 13) */ + { 0x045e, 0x0000, 52, 46 }, /* 130: p=0.023648 ( 96, 2) */ + { 0x24ef, 0x0000, 23, 17 }, /* 131: p=0.199999 ( 3, 13) */ + { 0x0690, 0x0000, 48, 40 }, /* 132: p=0.035533 ( 63, 2) */ + { 0x2865, 0x0000, 23, 15 }, /* 133: p=0.218748 ( 2, 8) */ + { 0x09de, 0x0000, 42, 136 }, /* 134: p=0.053434 ( 41, 2) */ + { 0x3987, 0x0000, 137, 7 }, /* 135: p=0.304346 ( 2, 5) */ + { 0x0dc8, 0x0000, 38, 32 }, /* 136: p=0.074626 ( 41, 3) */ + { 0x2c99, 0x0000, 21, 139 }, /* 137: p=0.241378 ( 2, 7) */ + { 0x10ca, 0x0000, 140, 172 }, /* 138: p=0.090907 ( 13, 1) */ + { 0x3b5f, 0x0000, 15, 9 }, /* 139: p=0.312499 ( 3, 7) */ + { 0x0b5d, 0x0000, 142, 170 }, /* 140: p=0.061537 ( 20, 1) */ + { 0x5695, 0x0000, 9, 85 }, /* 141: p=0.411764 ( 2, 3) */ + { 0x078a, 0x0000, 144, 168 }, /* 142: p=0.040815 ( 31, 1) */ + { 0x8000, 0x0000, 141, 248 }, /* 143: p=0.500000 ( 2, 2) */ + { 0x050f, 0x0000, 146, 166 }, /* 144: p=0.027397 ( 47, 1) */ + { 0x24ee, 0x0000, 147, 247 }, /* 145: p=0.199988 ( 0, 1) */ + { 0x0358, 0x0000, 148, 164 }, /* 146: p=0.018099 ( 72, 1) */ + { 0x0d30, 0x0000, 149, 197 }, /* 147: p=0.071422 ( 0, 4) */ + { 0x0234, 0x0000, 150, 162 }, /* 148: p=0.011940 ( 110, 1) */ + { 0x0481, 0x0000, 151, 95 }, /* 149: p=0.024388 ( 0, 13) */ + { 0x0173, 0x0000, 152, 160 }, /* 150: p=0.007858 ( 168, 1) */ + { 0x017a, 0x0000, 153, 173 }, /* 151: p=0.007999 ( 0, 41) */ + { 0x00f5, 0x0000, 154, 158 }, /* 152: p=0.005175 ( 256, 1) */ + { 0x007b, 0x0000, 155, 165 }, /* 153: p=0.002611 ( 0, 127) */ + { 0x00a1, 0x0000, 70, 156 }, /* 154: p=0.003413 ( 389, 1) */ + { 0x0028, 0x0000, 157, 161 }, /* 155: p=0.000849 ( 0, 392) */ + { 0x011a, 0x0000, 66, 60 }, /* 156: p=0.005957 ( 389, 2) */ + { 0x000d, 0x0000, 81, 159 }, /* 157: p=0.000276 ( 0, 1208) */ + { 0x01aa, 0x0000, 62, 56 }, /* 158: p=0.009020 ( 256, 2) */ + { 0x0034, 0x0000, 75, 71 }, /* 159: p=0.001102 ( 1, 1208) */ + { 0x0286, 0x0000, 58, 52 }, /* 160: p=0.013672 ( 168, 2) */ + { 0x00a0, 0x0000, 69, 163 }, /* 161: p=0.003387 ( 1, 392) */ + { 0x03d3, 0x0000, 54, 48 }, /* 162: p=0.020710 ( 110, 2) */ + { 0x0117, 0x0000, 65, 59 }, /* 163: p=0.005912 ( 2, 392) */ + { 0x05c5, 0x0000, 50, 42 }, /* 164: p=0.031250 ( 72, 2) */ + { 0x01ea, 0x0000, 167, 171 }, /* 165: p=0.010362 ( 1, 127) */ + { 0x08ad, 0x0000, 44, 38 }, /* 166: p=0.046979 ( 47, 2) */ + { 0x0144, 0x0000, 65, 169 }, /* 167: p=0.006849 ( 1, 193) */ + { 0x0ccc, 0x0000, 40, 32 }, /* 168: p=0.069306 ( 31, 2) */ + { 0x0234, 0x0000, 59, 53 }, /* 169: p=0.011925 ( 2, 193) */ + { 0x1302, 0x0000, 34, 26 }, /* 170: p=0.102940 ( 20, 2) */ + { 0x0353, 0x0000, 55, 47 }, /* 171: p=0.017995 ( 2, 127) */ + { 0x1b81, 0x0000, 30, 174 }, /* 172: p=0.148935 ( 13, 2) */ + { 0x05c5, 0x0000, 175, 193 }, /* 173: p=0.031249 ( 1, 41) */ + { 0x24ef, 0x0000, 24, 18 }, /* 174: p=0.199999 ( 13, 3) */ + { 0x03cf, 0x0000, 177, 191 }, /* 175: p=0.020618 ( 1, 63) */ + { 0x2b74, 0x0000, 178, 222 }, /* 176: p=0.235291 ( 4, 1) */ + { 0x0285, 0x0000, 179, 189 }, /* 177: p=0.013652 ( 1, 96) */ + { 0x201d, 0x0000, 180, 218 }, /* 178: p=0.173910 ( 6, 1) */ + { 0x01ab, 0x0000, 181, 187 }, /* 179: p=0.009029 ( 1, 146) */ + { 0x1715, 0x0000, 182, 216 }, /* 180: p=0.124998 ( 9, 1) */ + { 0x011a, 0x0000, 183, 185 }, /* 181: p=0.005961 ( 1, 222) */ + { 0x0fb7, 0x0000, 184, 214 }, /* 182: p=0.085105 ( 14, 1) */ + { 0x00ba, 0x0000, 69, 61 }, /* 183: p=0.003925 ( 1, 338) */ + { 0x0a67, 0x0000, 186, 212 }, /* 184: p=0.056337 ( 22, 1) */ + { 0x01eb, 0x0000, 59, 53 }, /* 185: p=0.010386 ( 2, 222) */ + { 0x06e7, 0x0000, 188, 210 }, /* 186: p=0.037382 ( 34, 1) */ + { 0x02e6, 0x0000, 55, 49 }, /* 187: p=0.015695 ( 2, 146) */ + { 0x0496, 0x0000, 190, 208 }, /* 188: p=0.024844 ( 52, 1) */ + { 0x045e, 0x0000, 51, 45 }, /* 189: p=0.023648 ( 2, 96) */ + { 0x030d, 0x0000, 192, 206 }, /* 190: p=0.016529 ( 79, 1) */ + { 0x0690, 0x0000, 47, 39 }, /* 191: p=0.035533 ( 2, 63) */ + { 0x0206, 0x0000, 194, 204 }, /* 192: p=0.010959 ( 120, 1) */ + { 0x09de, 0x0000, 41, 195 }, /* 193: p=0.053434 ( 2, 41) */ + { 0x0155, 0x0000, 196, 202 }, /* 194: p=0.007220 ( 183, 1) */ + { 0x0dc8, 0x0000, 37, 31 }, /* 195: p=0.074626 ( 3, 41) */ + { 0x00e1, 0x0000, 198, 200 }, /* 196: p=0.004750 ( 279, 1) */ + { 0x2b74, 0x0000, 199, 243 }, /* 197: p=0.235291 ( 1, 4) */ + { 0x0094, 0x0000, 72, 64 }, /* 198: p=0.003132 ( 424, 1) */ + { 0x201d, 0x0000, 201, 239 }, /* 199: p=0.173910 ( 1, 6) */ + { 0x0188, 0x0000, 62, 56 }, /* 200: p=0.008284 ( 279, 2) */ + { 0x1715, 0x0000, 203, 237 }, /* 201: p=0.124998 ( 1, 9) */ + { 0x0252, 0x0000, 58, 52 }, /* 202: p=0.012567 ( 183, 2) */ + { 0x0fb7, 0x0000, 205, 235 }, /* 203: p=0.085105 ( 1, 14) */ + { 0x0383, 0x0000, 54, 48 }, /* 204: p=0.019021 ( 120, 2) */ + { 0x0a67, 0x0000, 207, 233 }, /* 205: p=0.056337 ( 1, 22) */ + { 0x0547, 0x0000, 50, 44 }, /* 206: p=0.028571 ( 79, 2) */ + { 0x06e7, 0x0000, 209, 231 }, /* 207: p=0.037382 ( 1, 34) */ + { 0x07e2, 0x0000, 46, 38 }, /* 208: p=0.042682 ( 52, 2) */ + { 0x0496, 0x0000, 211, 229 }, /* 209: p=0.024844 ( 1, 52) */ + { 0x0bc0, 0x0000, 40, 34 }, /* 210: p=0.063636 ( 34, 2) */ + { 0x030d, 0x0000, 213, 227 }, /* 211: p=0.016529 ( 1, 79) */ + { 0x1178, 0x0000, 36, 28 }, /* 212: p=0.094593 ( 22, 2) */ + { 0x0206, 0x0000, 215, 225 }, /* 213: p=0.010959 ( 1, 120) */ + { 0x19da, 0x0000, 30, 22 }, /* 214: p=0.139999 ( 14, 2) */ + { 0x0155, 0x0000, 217, 223 }, /* 215: p=0.007220 ( 1, 183) */ + { 0x24ef, 0x0000, 26, 16 }, /* 216: p=0.199998 ( 9, 2) */ + { 0x00e1, 0x0000, 219, 221 }, /* 217: p=0.004750 ( 1, 279) */ + { 0x320e, 0x0000, 20, 220 }, /* 218: p=0.269229 ( 6, 2) */ + { 0x0094, 0x0000, 71, 63 }, /* 219: p=0.003132 ( 1, 424) */ + { 0x432a, 0x0000, 14, 8 }, /* 220: p=0.344827 ( 6, 3) */ + { 0x0188, 0x0000, 61, 55 }, /* 221: p=0.008284 ( 2, 279) */ + { 0x447d, 0x0000, 14, 224 }, /* 222: p=0.349998 ( 4, 2) */ + { 0x0252, 0x0000, 57, 51 }, /* 223: p=0.012567 ( 2, 183) */ + { 0x5ece, 0x0000, 8, 2 }, /* 224: p=0.434782 ( 4, 3) */ + { 0x0383, 0x0000, 53, 47 }, /* 225: p=0.019021 ( 2, 120) */ + { 0x8000, 0x0000, 228, 87 }, /* 226: p=0.500000 ( 1, 1) */ + { 0x0547, 0x0000, 49, 43 }, /* 227: p=0.028571 ( 2, 79) */ + { 0x481a, 0x0000, 230, 246 }, /* 228: p=0.363634 ( 2, 1) */ + { 0x07e2, 0x0000, 45, 37 }, /* 229: p=0.042682 ( 2, 52) */ + { 0x3579, 0x0000, 232, 244 }, /* 230: p=0.285711 ( 3, 1) */ + { 0x0bc0, 0x0000, 39, 33 }, /* 231: p=0.063636 ( 2, 34) */ + { 0x24ef, 0x0000, 234, 238 }, /* 232: p=0.199997 ( 5, 1) */ + { 0x1178, 0x0000, 35, 27 }, /* 233: p=0.094593 ( 2, 22) */ + { 0x1978, 0x0000, 138, 236 }, /* 234: p=0.137929 ( 8, 1) */ + { 0x19da, 0x0000, 29, 21 }, /* 235: p=0.139999 ( 2, 14) */ + { 0x2865, 0x0000, 24, 16 }, /* 236: p=0.218748 ( 8, 2) */ + { 0x24ef, 0x0000, 25, 15 }, /* 237: p=0.199998 ( 2, 9) */ + { 0x3987, 0x0000, 240, 8 }, /* 238: p=0.304346 ( 5, 2) */ + { 0x320e, 0x0000, 19, 241 }, /* 239: p=0.269229 ( 2, 6) */ + { 0x2c99, 0x0000, 22, 242 }, /* 240: p=0.241378 ( 7, 2) */ + { 0x432a, 0x0000, 13, 7 }, /* 241: p=0.344827 ( 3, 6) */ + { 0x3b5f, 0x0000, 16, 10 }, /* 242: p=0.312499 ( 7, 3) */ + { 0x447d, 0x0000, 13, 245 }, /* 243: p=0.349998 ( 2, 4) */ + { 0x5695, 0x0000, 10, 2 }, /* 244: p=0.411764 ( 3, 2) */ + { 0x5ece, 0x0000, 7, 1 }, /* 245: p=0.434782 ( 3, 4) */ + { 0x8000, 0x0000, 244, 83 }, /* 246: p=0.500000 ( 2, 2) */ + { 0x8000, 0x0000, 249, 250 }, /* 247: p=0.500000 ( 1, 1) */ + { 0x5695, 0x0000, 10, 2 }, /* 248: p=0.411764 ( 3, 2) */ + { 0x481a, 0x0000, 89, 143 }, /* 249: p=0.363634 ( 1, 2) */ + { 0x481a, 0x0000, 230, 246 }, /* 250: p=0.363634 ( 2, 1) */ +#endif +#ifdef ZCODER + /* This table has been designed for the ZCoder + * by running the following command in file 'ztable2.sn': + * (fast-crude (steady-mat 0.0035 0.0002) 260))) + */ + { 0x8000, 0x0000, 84, 139 }, /* 000: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 3, 4 }, /* 001: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 4, 3 }, /* 002: p=0.500000 ( 0, 0) */ + { 0x7399, 0x10a5, 5, 1 }, /* 003: p=0.465226 ( 0, 0) */ + { 0x7399, 0x10a5, 6, 2 }, /* 004: p=0.465226 ( 0, 0) */ + { 0x6813, 0x1f28, 7, 3 }, /* 005: p=0.430708 ( 0, 0) */ + { 0x6813, 0x1f28, 8, 4 }, /* 006: p=0.430708 ( 0, 0) */ + { 0x5d65, 0x2bd3, 9, 5 }, /* 007: p=0.396718 ( 0, 0) */ + { 0x5d65, 0x2bd3, 10, 6 }, /* 008: p=0.396718 ( 0, 0) */ + { 0x5387, 0x36e3, 11, 7 }, /* 009: p=0.363535 ( 0, 0) */ + { 0x5387, 0x36e3, 12, 8 }, /* 010: p=0.363535 ( 0, 0) */ + { 0x4a73, 0x408c, 13, 9 }, /* 011: p=0.331418 ( 0, 0) */ + { 0x4a73, 0x408c, 14, 10 }, /* 012: p=0.331418 ( 0, 0) */ + { 0x421f, 0x48fe, 15, 11 }, /* 013: p=0.300562 ( 0, 0) */ + { 0x421f, 0x48fe, 16, 12 }, /* 014: p=0.300562 ( 0, 0) */ + { 0x3a85, 0x5060, 17, 13 }, /* 015: p=0.271166 ( 0, 0) */ + { 0x3a85, 0x5060, 18, 14 }, /* 016: p=0.271166 ( 0, 0) */ + { 0x339b, 0x56d3, 19, 15 }, /* 017: p=0.243389 ( 0, 0) */ + { 0x339b, 0x56d3, 20, 16 }, /* 018: p=0.243389 ( 0, 0) */ + { 0x2d59, 0x5c73, 21, 17 }, /* 019: p=0.217351 ( 0, 0) */ + { 0x2d59, 0x5c73, 22, 18 }, /* 020: p=0.217351 ( 0, 0) */ + { 0x27b3, 0x615e, 23, 19 }, /* 021: p=0.193091 ( 0, 0) */ + { 0x27b3, 0x615e, 24, 20 }, /* 022: p=0.193091 ( 0, 0) */ + { 0x22a1, 0x65a7, 25, 21 }, /* 023: p=0.170683 ( 0, 0) */ + { 0x22a1, 0x65a7, 26, 22 }, /* 024: p=0.170683 ( 0, 0) */ + { 0x1e19, 0x6963, 27, 23 }, /* 025: p=0.150134 ( 0, 0) */ + { 0x1e19, 0x6963, 28, 24 }, /* 026: p=0.150134 ( 0, 0) */ + { 0x1a0f, 0x6ca3, 29, 25 }, /* 027: p=0.131397 ( 0, 0) */ + { 0x1a0f, 0x6ca3, 30, 26 }, /* 028: p=0.131397 ( 0, 0) */ + { 0x167b, 0x6f75, 31, 27 }, /* 029: p=0.114441 ( 0, 0) */ + { 0x167b, 0x6f75, 32, 28 }, /* 030: p=0.114441 ( 0, 0) */ + { 0x1353, 0x71e6, 33, 29 }, /* 031: p=0.099214 ( 0, 0) */ + { 0x1353, 0x71e6, 34, 30 }, /* 032: p=0.099214 ( 0, 0) */ + { 0x108d, 0x7403, 35, 31 }, /* 033: p=0.085616 ( 0, 0) */ + { 0x108d, 0x7403, 36, 32 }, /* 034: p=0.085616 ( 0, 0) */ + { 0x0e1f, 0x75d7, 37, 33 }, /* 035: p=0.073525 ( 0, 0) */ + { 0x0e1f, 0x75d7, 38, 34 }, /* 036: p=0.073525 ( 0, 0) */ + { 0x0c01, 0x7769, 39, 35 }, /* 037: p=0.062871 ( 0, 0) */ + { 0x0c01, 0x7769, 40, 36 }, /* 038: p=0.062871 ( 0, 0) */ + { 0x0a2b, 0x78c2, 41, 37 }, /* 039: p=0.053524 ( 0, 0) */ + { 0x0a2b, 0x78c2, 42, 38 }, /* 040: p=0.053524 ( 0, 0) */ + { 0x0895, 0x79ea, 43, 39 }, /* 041: p=0.045374 ( 0, 0) */ + { 0x0895, 0x79ea, 44, 40 }, /* 042: p=0.045374 ( 0, 0) */ + { 0x0737, 0x7ae7, 45, 41 }, /* 043: p=0.038280 ( 0, 0) */ + { 0x0737, 0x7ae7, 46, 42 }, /* 044: p=0.038280 ( 0, 0) */ + { 0x060b, 0x7bbe, 47, 43 }, /* 045: p=0.032175 ( 0, 0) */ + { 0x060b, 0x7bbe, 48, 44 }, /* 046: p=0.032175 ( 0, 0) */ + { 0x050b, 0x7c75, 49, 45 }, /* 047: p=0.026926 ( 0, 0) */ + { 0x050b, 0x7c75, 50, 46 }, /* 048: p=0.026926 ( 0, 0) */ + { 0x0431, 0x7d10, 51, 47 }, /* 049: p=0.022430 ( 0, 0) */ + { 0x0431, 0x7d10, 52, 48 }, /* 050: p=0.022430 ( 0, 0) */ + { 0x0379, 0x7d92, 53, 49 }, /* 051: p=0.018623 ( 0, 0) */ + { 0x0379, 0x7d92, 54, 50 }, /* 052: p=0.018623 ( 0, 0) */ + { 0x02dd, 0x7dff, 55, 51 }, /* 053: p=0.015386 ( 0, 0) */ + { 0x02dd, 0x7dff, 56, 52 }, /* 054: p=0.015386 ( 0, 0) */ + { 0x025b, 0x7e5b, 57, 53 }, /* 055: p=0.012671 ( 0, 0) */ + { 0x025b, 0x7e5b, 58, 54 }, /* 056: p=0.012671 ( 0, 0) */ + { 0x01ef, 0x7ea7, 59, 55 }, /* 057: p=0.010414 ( 0, 0) */ + { 0x01ef, 0x7ea7, 60, 56 }, /* 058: p=0.010414 ( 0, 0) */ + { 0x0195, 0x7ee6, 61, 57 }, /* 059: p=0.008529 ( 0, 0) */ + { 0x0195, 0x7ee6, 62, 58 }, /* 060: p=0.008529 ( 0, 0) */ + { 0x0149, 0x7f1b, 63, 59 }, /* 061: p=0.006935 ( 0, 0) */ + { 0x0149, 0x7f1b, 64, 60 }, /* 062: p=0.006935 ( 0, 0) */ + { 0x010b, 0x7f46, 65, 61 }, /* 063: p=0.005631 ( 0, 0) */ + { 0x010b, 0x7f46, 66, 62 }, /* 064: p=0.005631 ( 0, 0) */ + { 0x00d5, 0x7f6c, 67, 63 }, /* 065: p=0.004495 ( 0, 0) */ + { 0x00d5, 0x7f6c, 68, 64 }, /* 066: p=0.004495 ( 0, 0) */ + { 0x00a5, 0x7f8d, 69, 65 }, /* 067: p=0.003484 ( 0, 0) */ + { 0x00a5, 0x7f8d, 70, 66 }, /* 068: p=0.003484 ( 0, 0) */ + { 0x007b, 0x7faa, 71, 67 }, /* 069: p=0.002592 ( 0, 0) */ + { 0x007b, 0x7faa, 72, 68 }, /* 070: p=0.002592 ( 0, 0) */ + { 0x0057, 0x7fc3, 73, 69 }, /* 071: p=0.001835 ( 0, 0) */ + { 0x0057, 0x7fc3, 74, 70 }, /* 072: p=0.001835 ( 0, 0) */ + { 0x0039, 0x7fd8, 75, 71 }, /* 073: p=0.001211 ( 0, 0) */ + { 0x0039, 0x7fd8, 76, 72 }, /* 074: p=0.001211 ( 0, 0) */ + { 0x0023, 0x7fe7, 77, 73 }, /* 075: p=0.000740 ( 0, 0) */ + { 0x0023, 0x7fe7, 78, 74 }, /* 076: p=0.000740 ( 0, 0) */ + { 0x0013, 0x7ff2, 79, 75 }, /* 077: p=0.000402 ( 0, 0) */ + { 0x0013, 0x7ff2, 80, 76 }, /* 078: p=0.000402 ( 0, 0) */ + { 0x0007, 0x7ffa, 81, 77 }, /* 079: p=0.000153 ( 0, 0) */ + { 0x0007, 0x7ffa, 82, 78 }, /* 080: p=0.000153 ( 0, 0) */ + { 0x0001, 0x7fff, 81, 79 }, /* 081: p=0.000027 ( 0, 0) */ + { 0x0001, 0x7fff, 82, 80 }, /* 082: p=0.000027 ( 0, 0) */ + { 0x620b, 0x0000, 9, 85 }, /* 083: p=0.411764 ( 2, 3) */ + { 0x294a, 0x0000, 86, 216 }, /* 084: p=0.199988 ( 1, 0) */ + { 0x8000, 0x0000, 5, 6 }, /* 085: p=0.500000 ( 3, 3) */ + { 0x0db3, 0x0000, 88, 168 }, /* 086: p=0.071422 ( 4, 0) */ + { 0x538e, 0x0000, 89, 137 }, /* 087: p=0.363634 ( 1, 2) */ + { 0x0490, 0x0000, 90, 134 }, /* 088: p=0.024388 ( 13, 0) */ + { 0x3e3e, 0x0000, 91, 135 }, /* 089: p=0.285711 ( 1, 3) */ + { 0x017c, 0x0000, 92, 112 }, /* 090: p=0.007999 ( 41, 0) */ + { 0x294a, 0x0000, 93, 133 }, /* 091: p=0.199997 ( 1, 5) */ + { 0x007c, 0x0000, 94, 104 }, /* 092: p=0.002611 ( 127, 0) */ + { 0x1b75, 0x0000, 95, 131 }, /* 093: p=0.137929 ( 1, 8) */ + { 0x0028, 0x0000, 96, 100 }, /* 094: p=0.000849 ( 392, 0) */ + { 0x12fc, 0x0000, 97, 129 }, /* 095: p=0.097559 ( 1, 12) */ + { 0x000d, 0x0000, 82, 98 }, /* 096: p=0.000276 ( 1208, 0) */ + { 0x0cfb, 0x0000, 99, 125 }, /* 097: p=0.067795 ( 1, 18) */ + { 0x0034, 0x0000, 76, 72 }, /* 098: p=0.001102 ( 1208, 1) */ + { 0x08cd, 0x0000, 101, 123 }, /* 099: p=0.046511 ( 1, 27) */ + { 0x00a0, 0x0000, 70, 102 }, /* 100: p=0.003387 ( 392, 1) */ + { 0x05de, 0x0000, 103, 119 }, /* 101: p=0.031249 ( 1, 41) */ + { 0x0118, 0x0000, 66, 60 }, /* 102: p=0.005912 ( 392, 2) */ + { 0x03e9, 0x0000, 105, 117 }, /* 103: p=0.020942 ( 1, 62) */ + { 0x01ed, 0x0000, 106, 110 }, /* 104: p=0.010362 ( 127, 1) */ + { 0x0298, 0x0000, 107, 115 }, /* 105: p=0.013937 ( 1, 94) */ + { 0x0145, 0x0000, 66, 108 }, /* 106: p=0.006849 ( 193, 1) */ + { 0x01b6, 0x0000, 109, 113 }, /* 107: p=0.009216 ( 1, 143) */ + { 0x0237, 0x0000, 60, 54 }, /* 108: p=0.011925 ( 193, 2) */ + { 0x0121, 0x0000, 65, 111 }, /* 109: p=0.006097 ( 1, 217) */ + { 0x035b, 0x0000, 56, 48 }, /* 110: p=0.017995 ( 127, 2) */ + { 0x01f9, 0x0000, 59, 53 }, /* 111: p=0.010622 ( 2, 217) */ + { 0x05de, 0x0000, 114, 130 }, /* 112: p=0.031249 ( 41, 1) */ + { 0x02fc, 0x0000, 55, 49 }, /* 113: p=0.016018 ( 2, 143) */ + { 0x03e9, 0x0000, 116, 128 }, /* 114: p=0.020942 ( 62, 1) */ + { 0x0484, 0x0000, 51, 45 }, /* 115: p=0.024138 ( 2, 94) */ + { 0x0298, 0x0000, 118, 126 }, /* 116: p=0.013937 ( 94, 1) */ + { 0x06ca, 0x0000, 47, 39 }, /* 117: p=0.036082 ( 2, 62) */ + { 0x01b6, 0x0000, 120, 124 }, /* 118: p=0.009216 ( 143, 1) */ + { 0x0a27, 0x0000, 41, 121 }, /* 119: p=0.053434 ( 2, 41) */ + { 0x0121, 0x0000, 66, 122 }, /* 120: p=0.006097 ( 217, 1) */ + { 0x0e57, 0x0000, 37, 31 }, /* 121: p=0.074626 ( 3, 41) */ + { 0x01f9, 0x0000, 60, 54 }, /* 122: p=0.010622 ( 217, 2) */ + { 0x0f25, 0x0000, 37, 29 }, /* 123: p=0.078651 ( 2, 27) */ + { 0x02fc, 0x0000, 56, 50 }, /* 124: p=0.016018 ( 143, 2) */ + { 0x1629, 0x0000, 33, 127 }, /* 125: p=0.112902 ( 2, 18) */ + { 0x0484, 0x0000, 52, 46 }, /* 126: p=0.024138 ( 94, 2) */ + { 0x1ee8, 0x0000, 27, 21 }, /* 127: p=0.153845 ( 3, 18) */ + { 0x06ca, 0x0000, 48, 40 }, /* 128: p=0.036082 ( 62, 2) */ + { 0x200f, 0x0000, 27, 19 }, /* 129: p=0.159089 ( 2, 12) */ + { 0x0a27, 0x0000, 42, 132 }, /* 130: p=0.053434 ( 41, 2) */ + { 0x2dae, 0x0000, 21, 15 }, /* 131: p=0.218748 ( 2, 8) */ + { 0x0e57, 0x0000, 38, 32 }, /* 132: p=0.074626 ( 41, 3) */ + { 0x4320, 0x0000, 15, 7 }, /* 133: p=0.304346 ( 2, 5) */ + { 0x11a0, 0x0000, 136, 164 }, /* 134: p=0.090907 ( 13, 1) */ + { 0x620b, 0x0000, 9, 85 }, /* 135: p=0.411764 ( 2, 3) */ + { 0x0bbe, 0x0000, 138, 162 }, /* 136: p=0.061537 ( 20, 1) */ + { 0x8000, 0x0000, 135, 248 }, /* 137: p=0.500000 ( 2, 2) */ + { 0x07f3, 0x0000, 140, 160 }, /* 138: p=0.042104 ( 30, 1) */ + { 0x294a, 0x0000, 141, 247 }, /* 139: p=0.199988 ( 0, 1) */ + { 0x053e, 0x0000, 142, 158 }, /* 140: p=0.027971 ( 46, 1) */ + { 0x0db3, 0x0000, 143, 199 }, /* 141: p=0.071422 ( 0, 4) */ + { 0x0378, 0x0000, 144, 156 }, /* 142: p=0.018604 ( 70, 1) */ + { 0x0490, 0x0000, 145, 167 }, /* 143: p=0.024388 ( 0, 13) */ + { 0x024d, 0x0000, 146, 154 }, /* 144: p=0.012384 ( 106, 1) */ + { 0x017c, 0x0000, 147, 101 }, /* 145: p=0.007999 ( 0, 41) */ + { 0x0185, 0x0000, 148, 152 }, /* 146: p=0.008197 ( 161, 1) */ + { 0x007c, 0x0000, 149, 159 }, /* 147: p=0.002611 ( 0, 127) */ + { 0x0100, 0x0000, 68, 150 }, /* 148: p=0.005405 ( 245, 1) */ + { 0x0028, 0x0000, 151, 155 }, /* 149: p=0.000849 ( 0, 392) */ + { 0x01c0, 0x0000, 62, 56 }, /* 150: p=0.009421 ( 245, 2) */ + { 0x000d, 0x0000, 81, 153 }, /* 151: p=0.000276 ( 0, 1208) */ + { 0x02a7, 0x0000, 58, 52 }, /* 152: p=0.014256 ( 161, 2) */ + { 0x0034, 0x0000, 75, 71 }, /* 153: p=0.001102 ( 1, 1208) */ + { 0x0403, 0x0000, 54, 46 }, /* 154: p=0.021472 ( 106, 2) */ + { 0x00a0, 0x0000, 69, 157 }, /* 155: p=0.003387 ( 1, 392) */ + { 0x0608, 0x0000, 48, 42 }, /* 156: p=0.032110 ( 70, 2) */ + { 0x0118, 0x0000, 65, 59 }, /* 157: p=0.005912 ( 2, 392) */ + { 0x0915, 0x0000, 44, 38 }, /* 158: p=0.047945 ( 46, 2) */ + { 0x01ed, 0x0000, 161, 165 }, /* 159: p=0.010362 ( 1, 127) */ + { 0x0db4, 0x0000, 40, 32 }, /* 160: p=0.071428 ( 30, 2) */ + { 0x0145, 0x0000, 65, 163 }, /* 161: p=0.006849 ( 1, 193) */ + { 0x1417, 0x0000, 34, 26 }, /* 162: p=0.102940 ( 20, 2) */ + { 0x0237, 0x0000, 59, 53 }, /* 163: p=0.011925 ( 2, 193) */ + { 0x1dd6, 0x0000, 30, 166 }, /* 164: p=0.148935 ( 13, 2) */ + { 0x035b, 0x0000, 55, 47 }, /* 165: p=0.017995 ( 2, 127) */ + { 0x294a, 0x0000, 24, 18 }, /* 166: p=0.199999 ( 13, 3) */ + { 0x11a0, 0x0000, 169, 195 }, /* 167: p=0.090907 ( 1, 13) */ + { 0x31a3, 0x0000, 170, 212 }, /* 168: p=0.235291 ( 4, 1) */ + { 0x0bbe, 0x0000, 171, 193 }, /* 169: p=0.061537 ( 1, 20) */ + { 0x235a, 0x0000, 172, 208 }, /* 170: p=0.173910 ( 6, 1) */ + { 0x07f3, 0x0000, 173, 191 }, /* 171: p=0.042104 ( 1, 30) */ + { 0x18b3, 0x0000, 174, 206 }, /* 172: p=0.124998 ( 9, 1) */ + { 0x053e, 0x0000, 175, 189 }, /* 173: p=0.027971 ( 1, 46) */ + { 0x1073, 0x0000, 176, 204 }, /* 174: p=0.085105 ( 14, 1) */ + { 0x0378, 0x0000, 177, 187 }, /* 175: p=0.018604 ( 1, 70) */ + { 0x0b35, 0x0000, 178, 200 }, /* 176: p=0.058822 ( 21, 1) */ + { 0x024d, 0x0000, 179, 185 }, /* 177: p=0.012384 ( 1, 106) */ + { 0x0778, 0x0000, 180, 198 }, /* 178: p=0.039603 ( 32, 1) */ + { 0x0185, 0x0000, 181, 183 }, /* 179: p=0.008197 ( 1, 161) */ + { 0x04ed, 0x0000, 182, 194 }, /* 180: p=0.026315 ( 49, 1) */ + { 0x0100, 0x0000, 67, 59 }, /* 181: p=0.005405 ( 1, 245) */ + { 0x0349, 0x0000, 184, 192 }, /* 182: p=0.017621 ( 74, 1) */ + { 0x02a7, 0x0000, 57, 51 }, /* 183: p=0.014256 ( 2, 161) */ + { 0x022e, 0x0000, 186, 190 }, /* 184: p=0.011730 ( 112, 1) */ + { 0x0403, 0x0000, 53, 45 }, /* 185: p=0.021472 ( 2, 106) */ + { 0x0171, 0x0000, 64, 188 }, /* 186: p=0.007767 ( 170, 1) */ + { 0x0608, 0x0000, 47, 41 }, /* 187: p=0.032110 ( 2, 70) */ + { 0x0283, 0x0000, 58, 52 }, /* 188: p=0.013513 ( 170, 2) */ + { 0x0915, 0x0000, 43, 37 }, /* 189: p=0.047945 ( 2, 46) */ + { 0x03cc, 0x0000, 54, 48 }, /* 190: p=0.020349 ( 112, 2) */ + { 0x0db4, 0x0000, 39, 31 }, /* 191: p=0.071428 ( 2, 30) */ + { 0x05b6, 0x0000, 50, 42 }, /* 192: p=0.030434 ( 74, 2) */ + { 0x1417, 0x0000, 33, 25 }, /* 193: p=0.102940 ( 2, 20) */ + { 0x088a, 0x0000, 44, 196 }, /* 194: p=0.045161 ( 49, 2) */ + { 0x1dd6, 0x0000, 29, 197 }, /* 195: p=0.148935 ( 2, 13) */ + { 0x0c16, 0x0000, 40, 34 }, /* 196: p=0.063291 ( 49, 3) */ + { 0x294a, 0x0000, 23, 17 }, /* 197: p=0.199999 ( 3, 13) */ + { 0x0ce2, 0x0000, 40, 32 }, /* 198: p=0.067307 ( 32, 2) */ + { 0x31a3, 0x0000, 201, 243 }, /* 199: p=0.235291 ( 1, 4) */ + { 0x1332, 0x0000, 36, 202 }, /* 200: p=0.098590 ( 21, 2) */ + { 0x235a, 0x0000, 203, 239 }, /* 201: p=0.173910 ( 1, 6) */ + { 0x1adc, 0x0000, 30, 24 }, /* 202: p=0.135134 ( 21, 3) */ + { 0x18b3, 0x0000, 205, 237 }, /* 203: p=0.124998 ( 1, 9) */ + { 0x1be7, 0x0000, 30, 22 }, /* 204: p=0.139999 ( 14, 2) */ + { 0x1073, 0x0000, 207, 235 }, /* 205: p=0.085105 ( 1, 14) */ + { 0x294a, 0x0000, 26, 16 }, /* 206: p=0.199998 ( 9, 2) */ + { 0x0b35, 0x0000, 209, 231 }, /* 207: p=0.058822 ( 1, 21) */ + { 0x3a07, 0x0000, 20, 210 }, /* 208: p=0.269229 ( 6, 2) */ + { 0x0778, 0x0000, 211, 229 }, /* 209: p=0.039603 ( 1, 32) */ + { 0x4e30, 0x0000, 14, 8 }, /* 210: p=0.344827 ( 6, 3) */ + { 0x04ed, 0x0000, 213, 225 }, /* 211: p=0.026315 ( 1, 49) */ + { 0x4fa6, 0x0000, 14, 214 }, /* 212: p=0.349998 ( 4, 2) */ + { 0x0349, 0x0000, 215, 223 }, /* 213: p=0.017621 ( 1, 74) */ + { 0x6966, 0x0000, 8, 2 }, /* 214: p=0.434782 ( 4, 3) */ + { 0x022e, 0x0000, 217, 221 }, /* 215: p=0.011730 ( 1, 112) */ + { 0x8000, 0x0000, 218, 87 }, /* 216: p=0.500000 ( 1, 1) */ + { 0x0171, 0x0000, 63, 219 }, /* 217: p=0.007767 ( 1, 170) */ + { 0x538e, 0x0000, 220, 246 }, /* 218: p=0.363634 ( 2, 1) */ + { 0x0283, 0x0000, 57, 51 }, /* 219: p=0.013513 ( 2, 170) */ + { 0x3e3e, 0x0000, 222, 244 }, /* 220: p=0.285711 ( 3, 1) */ + { 0x03cc, 0x0000, 53, 47 }, /* 221: p=0.020349 ( 2, 112) */ + { 0x294a, 0x0000, 224, 242 }, /* 222: p=0.199997 ( 5, 1) */ + { 0x05b6, 0x0000, 49, 41 }, /* 223: p=0.030434 ( 2, 74) */ + { 0x1b75, 0x0000, 226, 240 }, /* 224: p=0.137929 ( 8, 1) */ + { 0x088a, 0x0000, 43, 227 }, /* 225: p=0.045161 ( 2, 49) */ + { 0x12fc, 0x0000, 228, 238 }, /* 226: p=0.097559 ( 12, 1) */ + { 0x0c16, 0x0000, 39, 33 }, /* 227: p=0.063291 ( 3, 49) */ + { 0x0cfb, 0x0000, 230, 234 }, /* 228: p=0.067795 ( 18, 1) */ + { 0x0ce2, 0x0000, 39, 31 }, /* 229: p=0.067307 ( 2, 32) */ + { 0x08cd, 0x0000, 112, 232 }, /* 230: p=0.046511 ( 27, 1) */ + { 0x1332, 0x0000, 35, 233 }, /* 231: p=0.098590 ( 2, 21) */ + { 0x0f25, 0x0000, 38, 30 }, /* 232: p=0.078651 ( 27, 2) */ + { 0x1adc, 0x0000, 29, 23 }, /* 233: p=0.135134 ( 3, 21) */ + { 0x1629, 0x0000, 34, 236 }, /* 234: p=0.112902 ( 18, 2) */ + { 0x1be7, 0x0000, 29, 21 }, /* 235: p=0.139999 ( 2, 14) */ + { 0x1ee8, 0x0000, 28, 22 }, /* 236: p=0.153845 ( 18, 3) */ + { 0x294a, 0x0000, 25, 15 }, /* 237: p=0.199998 ( 2, 9) */ + { 0x200f, 0x0000, 28, 20 }, /* 238: p=0.159089 ( 12, 2) */ + { 0x3a07, 0x0000, 19, 241 }, /* 239: p=0.269229 ( 2, 6) */ + { 0x2dae, 0x0000, 22, 16 }, /* 240: p=0.218748 ( 8, 2) */ + { 0x4e30, 0x0000, 13, 7 }, /* 241: p=0.344827 ( 3, 6) */ + { 0x4320, 0x0000, 16, 8 }, /* 242: p=0.304346 ( 5, 2) */ + { 0x4fa6, 0x0000, 13, 245 }, /* 243: p=0.349998 ( 2, 4) */ + { 0x620b, 0x0000, 10, 2 }, /* 244: p=0.411764 ( 3, 2) */ + { 0x6966, 0x0000, 7, 1 }, /* 245: p=0.434782 ( 3, 4) */ + { 0x8000, 0x0000, 244, 83 }, /* 246: p=0.500000 ( 2, 2) */ + { 0x8000, 0x0000, 249, 250 }, /* 247: p=0.500000 ( 1, 1) */ + { 0x620b, 0x0000, 10, 2 }, /* 248: p=0.411764 ( 3, 2) */ + { 0x538e, 0x0000, 89, 137 }, /* 249: p=0.363634 ( 1, 2) */ + { 0x538e, 0x0000, 220, 246 }, /* 250: p=0.363634 ( 2, 1) */ +#endif +}; + + + +//////////////////////////////////////////////////////////////// +// CONSTRUCTOR/DESTRUCTOR +//////////////////////////////////////////////////////////////// + +class ZPCodec::Encode : public ZPCodec +{ +public: + Encode(GP<ByteStream> gbs, const bool djvucompat); + virtual ~Encode(); +private: + void init(void); +}; + +ZPCodec::Encode::Encode(GP<ByteStream> gbs, const bool djvucompat) +: ZPCodec(gbs,true,djvucompat) +{ + init(); + // Codebit counter +#ifdef ZPCODEC_BITCOUNT + bitcount = 0; +#endif +} + +ZPCodec::Encode::~Encode() +{ + eflush(); +} + +class ZPCodec::Decode : public ZPCodec +{ +public: + Decode(GP<ByteStream> gbs, const bool djvucompat); + virtual ~Decode(); +private: + void init(void); +}; + +ZPCodec::Decode::Decode(GP<ByteStream> gbs, const bool djvucompat) +: ZPCodec(gbs,false,djvucompat) +{ + init(); + // Codebit counter +#ifdef ZPCODEC_BITCOUNT + bitcount = 0; +#endif +} + +ZPCodec::Decode::~Decode() {} + +ZPCodec::ZPCodec(GP<ByteStream> xgbs, const bool xencoding, const bool djvucompat) +: gbs(xgbs), bs(xgbs), encoding(xencoding), fence(0), subend(0), buffer(0), nrun(0) +{ + // Create machine independent ffz table + for (int i=0; i<256; i++) + { + ffzt[i]=0; + for (int j=i; j&0x80; j<<=1) + ffzt[i] += 1; + } + // Initialize table + newtable(default_ztable); + // Patch table table (and lose DjVu compatibility). + if (!djvucompat) + { + for (int j=0; j<256; j++) + { + unsigned short a = 0x10000-p[j]; + while (a>=0x8000) a=(unsigned short)(a<<1); + if (m[j]>0 && a+p[j]>=0x8000 && a>=m[j]) + { + BitContext x = default_ztable[j].dn; + BitContext y = default_ztable[x].dn; + dn[j] = y; + } + } + } +} + +ZPCodec::~ZPCodec() {} + +GP<ZPCodec> +ZPCodec::create(GP<ByteStream> gbs, const bool encoding, const bool djvucompat) +{ + GP<ZPCodec> retval; + if(encoding) + { + retval=new ZPCodec::Encode(gbs,djvucompat); + }else + { + retval=new ZPCodec::Decode(gbs,djvucompat); + } + return retval; +} + +//////////////////////////////////////////////////////////////// +// Z CODER DECODE ALGORITHM +//////////////////////////////////////////////////////////////// + + + +void +ZPCodec::Decode::init(void) +{ + assert(sizeof(unsigned int)==4); + assert(sizeof(unsigned short)==2); + a = 0; + /* Read first 16 bits of code */ + if (! bs->read((void*)&byte, 1)) + byte = 0xff; + code = (byte<<8); + if (! bs->read((void*)&byte, 1)) + byte = 0xff; + code = code | byte; + /* Preload buffer */ + delay = 25; + scount = 0; + preload(); + /* Compute initial fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; +} + + +void +ZPCodec::preload(void) +{ + while (scount<=24) + { + if (bs->read((void*)&byte, 1) < 1) + { + byte = 0xff; + if (--delay < 1) + G_THROW( ByteStream::EndOfFile ); + } + buffer = (buffer<<8) | byte; + scount += 8; + } +} + + +inline int +ZPCodec::ffz(unsigned int x) +{ + // DO NOT DEFINE FASTBSR : + // Test shows that it hardly helps on a PPro, + // and rumors are that BSR is very slow on PPlain. +#if defined(FASTBSR) && defined(_MSC_VER) && defined(_M_IX86) + int r; + __asm { + mov ebx, x + xor ebx, 0xffff + mov eax, -1 + bsr eax, ebx + mov r, eax + } + return 15 - r; +#elif defined(FASTBSR) && defined(__GNUC__) && defined(__i386__) + int r, dummy; + __asm__ const ( "movl %2,%1\n\t" + "xorl $0xffff, %1\n\t" + "movl $-1, %0\n\t" + "bsrl %1, %0" + : "=&q" (r), "=q" (dummy) : "rm" (x) ); + return 15 - r; +#else + return (x>=0xff00) ? (ffzt[x&0xff]+8) : (ffzt[(x>>8)&0xff]); +#endif +} + + +int +ZPCodec::decode_sub(BitContext &ctx, unsigned int z) +{ + /* Save bit */ + int bit = (ctx & 1); + /* Avoid interval reversion */ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Test MPS/LPS */ + if (z > code) + { + /* LPS branch */ + z = 0x10000 - z; + a = a + z; + code = code + z; + /* LPS adaptation */ + ctx = dn[ctx]; + /* LPS renormalization */ + int shift = ffz(a); + scount -= shift; + a = (unsigned short)(a<<shift); + code = (unsigned short)(code<<shift) | ((buffer>>scount) & ((1<<shift)-1)); +#ifdef ZPCODEC_BITCOUNT + bitcount += shift; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return bit ^ 1; + } + else + { + /* MPS adaptation */ + if (a >= m[ctx]) + ctx = up[ctx]; + /* MPS renormalization */ + scount -= 1; + a = (unsigned short)(z<<1); + code = (unsigned short)(code<<1) | ((buffer>>scount) & 1); +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return bit; + } +} + + +int +ZPCodec::decode_sub_simple(int mps, unsigned int z) +{ + /* Test MPS/LPS */ + if (z > code) + { + /* LPS branch */ + z = 0x10000 - z; + a = a + z; + code = code + z; + /* LPS renormalization */ + int shift = ffz(a); + scount -= shift; + a = (unsigned short)(a<<shift); + code = (unsigned short)(code<<shift) | ((buffer>>scount) & ((1<<shift)-1)); +#ifdef ZPCODEC_BITCOUNT + bitcount += shift; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps ^ 1; + } + else + { + /* MPS renormalization */ + scount -= 1; + a = (unsigned short)(z<<1); + code = (unsigned short)(code<<1) | ((buffer>>scount) & 1); +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps; + } +} + + +int +ZPCodec::decode_sub_nolearn(int mps, unsigned int z) +{ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Test MPS/LPS */ + if (z > code) + { + /* LPS branch */ + z = 0x10000 - z; + a = a + z; + code = code + z; + /* LPS renormalization */ + int shift = ffz(a); + scount -= shift; + a = (unsigned short)(a<<shift); + code = (unsigned short)(code<<shift) | ((buffer>>scount) & ((1<<shift)-1)); +#ifdef ZPCODEC_BITCOUNT + bitcount += shift; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps ^ 1; + } + else + { + /* MPS renormalization */ + scount -= 1; + a = (unsigned short)(z<<1); + code = (unsigned short)(code<<1) | ((buffer>>scount) & 1); +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps; + } +} + + + + + +//////////////////////////////////////////////////////////////// +// Z CODER ENCODE ALGORITHM +//////////////////////////////////////////////////////////////// + + + + +void +ZPCodec::Encode::init(void) +{ + assert(sizeof(unsigned int)==4); + assert(sizeof(unsigned short)==2); + a = 0; + scount = 0; + byte = 0; + delay = 25; + subend = 0; + buffer = 0xffffff; + nrun = 0; +} + +void +ZPCodec::outbit(int bit) +{ + if (delay > 0) + { + if (delay < 0xff) // delay=0xff suspends emission forever + delay -= 1; + } + else + { + /* Insert a bit */ + byte = (byte<<1) | bit; + /* Output a byte */ + if (++scount == 8) + { + if (!encoding) + G_THROW( ERR_MSG("ZPCodec.no_encoding") ); + if (bs->write((void*)&byte, 1) != 1) + G_THROW( ERR_MSG("ZPCodec.write_error") ); + scount = 0; + byte = 0; + } + } +} + +void +ZPCodec::zemit(int b) +{ + /* Shift new bit into 3bytes buffer */ + buffer = (buffer<<1) + b; + /* Examine bit going out of the 3bytes buffer */ + b = (buffer >> 24); + buffer = (buffer & 0xffffff); + /* The following lines have been changed in order to emphazise the + * similarity between this bit counting and the scheme of Witten, Neal & Cleary + * (WN&C). Corresponding changes have been made in outbit and eflush. + * Variable 'nrun' is similar to the 'bits_to_follow' in the W&N code. + */ + switch(b) + { + /* Similar to WN&C upper renormalization */ + case 1: + outbit(1); + while (nrun-- > 0) + outbit(0); + nrun = 0; + break; + /* Similar to WN&C lower renormalization */ + case 0xff: + outbit(0); + while (nrun-- > 0) + outbit(1); + nrun = 0; + break; + /* Similar to WN&C central renormalization */ + case 0: + nrun += 1; + break; + default: + assert(0); + } + /* Code bit counter */ +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif +} + +void +ZPCodec::eflush() +{ + /* adjust subend */ + if (subend > 0x8000) + subend = 0x10000; + else if (subend > 0) + subend = 0x8000; + /* zemit many mps bits */ + while (buffer != 0xffffff || subend ) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + } + /* zemit pending run */ + outbit(1); + while (nrun-- > 0) + outbit(0); + nrun = 0; + /* zemit 1 until full byte */ + while (scount > 0) + outbit(1); + /* prevent further emission */ + delay = 0xff; +} + +void +ZPCodec::encode_mps(BitContext &ctx, unsigned int z) +{ + /* Avoid interval reversion */ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Adaptation */ + if (a >= m[ctx]) + ctx = up[ctx]; + /* Code MPS */ + a = z; + /* Export bits */ + if (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_lps(BitContext &ctx, unsigned int z) +{ + /* Avoid interval reversion */ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Adaptation */ + ctx = dn[ctx]; + /* Code LPS */ + z = 0x10000 - z; + subend += z; + a += z; + /* Export bits */ + while (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_mps_simple(unsigned int z) +{ + /* Code MPS */ + a = z; + /* Export bits */ + if (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + +void +ZPCodec::encode_lps_simple(unsigned int z) +{ + /* Code LPS */ + z = 0x10000 - z; + subend += z; + a += z; + /* Export bits */ + while (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_mps_nolearn(unsigned int z) +{ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Code MPS */ + a = z; + /* Export bits */ + if (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_lps_nolearn(unsigned int z) +{ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Code LPS */ + z = 0x10000 - z; + subend += z; + a += z; + /* Export bits */ + while (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + + + + + +//////////////////////////////////////////////////////////////// +// TABLE AND PARAMETER MANAGEMENT +//////////////////////////////////////////////////////////////// + + + + +void +ZPCodec::newtable(ZPCodec::Table *table) +{ + for (int i=0; i<256; i++) + { + p[i] = table[i].p; + m[i] = table[i].m; + up[i] = table[i].up; + dn[i] = table[i].dn; + } +} + +static float +p_to_plps(unsigned short p) +{ + float fplps; + float fp = (float)(p) / (float)(0x10000); + const float log2 = (float)0.69314718055994530942; +#ifdef ZCODER + fplps = fp - (fp+0.5) * log(fp+0.5) + (fp-0.5)*log2; +#endif +#ifdef ZPCODER + if (fp <= (1.0/6.0) ) + fplps = fp * 2 * log2; + else + fplps = (float)((1.5*fp-0.25) - (1.5*fp+0.25)*log(1.5*fp+0.25) + (0.5*fp-0.25)*log2); +#endif + return fplps; +} + + +BitContext +ZPCodec::state(float prob1) +{ + // Return a state representing 'prob1' in the steady chain + // FixMe: This is quite slow! + int mps = (prob1 <= 0.5 ? 0 : 1); + float plps = (float)(mps ? 1.0 - prob1 : prob1); + // Locate steady chain (ordered, decreasing) + int sz = 0; + int lo = (mps ? 1 : 2); + while (p[lo+sz+sz+2] < p[lo+sz+sz]) sz+=1; + // Bisection + while (sz > 1) + { + int nsz = sz >> 1; + float nplps = p_to_plps( p[lo+nsz+nsz] ); + if (nplps < plps) + { sz=nsz; } + else + { lo=lo+nsz+nsz; sz=sz-nsz; } + } + // Choose closest one + float f1 = p_to_plps(p[lo])-plps; + float f2 = plps-p_to_plps(p[lo+2]); + return (f1<f2) ? lo : lo+2; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/ZPCodec.h b/kviewshell/plugins/djvu/libdjvu/ZPCodec.h new file mode 100644 index 00000000..4eba6901 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ZPCodec.h @@ -0,0 +1,747 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ZPCodec.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _ZPCODEC_H +#define _ZPCODEC_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "GContainer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + + + +/** @name ZPCodec.h + + Files #"ZPCodec.h"# and #"ZPCodec.cpp"# implement a fast binary adaptive + quasi-arithmetic coder named ZP-Coder. Because of its speed and + convenience, the ZP-Coder is used in several parts of the DjVu reference + library (See \Ref{BSByteStream.h}, \Ref{JB2Image.h}, \Ref{IW44Image.h}). + The following comments avoid the theory (see the historical remarks for + useful pointers) and concentrate on the user perspective on the ZP-Coder. + + {\bf Introduction} --- + Encoding consists of transforming a sequence of {\em message bits} into a + sequence of {\em code bits}. Decoding consists of retrieving the message + bits using only the code bits. We can make the code smaller than the + message as soon as we can predict a message bit on the basis of a {\em + coding context} composed of previously encoded or decoded bits. If the + prediction is always correct, we do not even need to encode the message + bit. If the prediction is totally unreliable, we need to generate one code + bit in order to unambiguously specify the message bit. In other words, + the more reliable the prediction, the more compression we get. + + The ZP-Coder handles prediction by means of {\em context variables} (see + \Ref{BitContext}). There must be a context variable for each possible + combination of context bits. Both the encoder and the decoder use same + context variable for coding each message bit. For instance, we can code a + binary image by successively coding all the pixels (the message bits) in + row and column order. It is reasonable to assume that each pixel can be + reasonably well predicted by looking at a few (say 10) neighboring pixels + located above and to the left of the current pixel. Since these 10 pixels + make 1024 combinations, we need 1024 context variables. Each pixel is + encoded using the context variable corresponding to the values of the 10 + neighboring pixels. Each pixel will be decoded by specifying the same + context variable corresponding to the values of these 10 pixels. This is + possible because these 10 pixels (located above and to the left) have + already been decoded and therefore are known by the decoder program. + + The context variables are initially set to zero, which mean that we do not + know yet how to predict the current message bit on the basis of the + context bits. While coding the message bits, the ZP-Coder automatically + estimates the frequencies of #0#s and #1#s coded using each context + variable. These frequencies actually provide a prediction (the most + probable bit value) and an estimation of the prediction reliability (how + often the prediction was correct in the past). All this statistical + information is stored into the context variable after coding each bit. In + other words, the more we code bits within a particular context, the better + the ZP-Coder adapts its prediction model, and the more compression we can + obtain. + + All this adaptation works indeed because both the encoder program and the + decoder program are always synchronized. Both the encoder and the decoder + see the same message bits encoded (or decoded) with the same context + variables. Both the encoder and the decoder apply the same rules to + update the context variables and improve the predictors. Both the encoder + and the decoder programs use the same predictors for any given message + bit. The decoder could not work if this was not the case. + + Just before encoding a message bit, all the context variables in the + encoder program contain certain values. Just before decoding this message + bit, all the context variables in the decoder program must contain the same + values as for the encoder program. This is guaranteed as long as + each prediction only depends on already coded bits: {\em the coding context, + on which the each prediction is based, must be composed of message bits which + have already been coded. } + + {\bf Usage} --- + Once you know how to organize the predictions (i.e. which coding context + to use, how many context variables to initialize, etc.), using the + ZP-Coder is straightforward (see \Ref{ZPCodec Examples}): + \begin{itemize} + \item The {\em encoder program} allocates context variables and + initializes them to zero. It then constructs a \Ref{ZPCodec} object for + encoding. For each message bit, the encoder program retrieves the context + bits, selects a context variable on the basis of the context bits and + calls member function \Ref{ZPCodec::encoder} with the message bit and a + reference to the context variable. + \item The {\em decoder program} allocates context variables and + initializes them to zero. It then constructs a \Ref{ZPCodec} object for + decoding. For each message bit, the decoder program retrieves the context + bits, selects a context variable on the basis of the context bits and + calls member function \Ref{ZPCodec::decoder} with a reference to the + context variable. This function returns the message bit. + \end{itemize} + Functions #encoder# and #decoder# only require a few machine cycles to + perform two essential tasks, namely {\em coding} and {\em context + adaptation}. Function #decoder# often returns after two arithmetic + operations only. To make your program fast, you just need to feed message + bits and context variables fast enough. + + {\bf History} --- The ZP-Coder is similar in function and performance to + the seminal Q-Coder (Pennebaker, Mitchell, Langdon, Arps, IBM J. Res + Dev. 32, 1988). An improved version of the Q-Coder, named QM-Coder, has + been described in certain parts of the JPEG standard. Unfortunate patent + policies have made these coders very difficult to use in general purpose + applications. The Z-Coder is constructed using a new approach based on an + extension of the Golomb codes (Bottou, Howard, Bengio, IEEE DCC 98, 1998 + \URL[DjVu]{http://www.research.att.com/~leonb/DJVU/bottou-howard-bengio/} + \URL[PostScript]{http://www.research.att.com/~leonb/PS/bottou-howard-bengio.ps.gz}) + This new approach does not infringe the QM-Coder patents. Unfortunately + the Z-Coder is dangerously close to the patented Arithmetic MEL Coder. + Therefore we wrote the ZP-Coder (pronounce Zee-Prime Coder) which we + believe is clear of legal problems. Needless to say, AT&T has patents + pending for both the Z-Coder and the ZP-Coder, licenced to LizardTech. + The good news however is that we can grant a license to use the ZP-Coder + in ``free software'' without further complication. See the Copyright + for more information. + + @memo + Binary adaptive quasi-arithmetic coder. + @version + #$Id: ZPCodec.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# + @author + L\'eon Bottou <leonb@research.att.com> */ +//@{ + + +/** Context variable. + Variables of type #BitContext# hold a single byte describing how to encode + or decode message bits with similar statistical properties. This single + byte simultaneously represents the current estimate of the bit probability + distribution (which is determined by the frequencies of #1#s and #0#s + already coded with this context) and the confidence in this estimate + (which determines how fast the estimate can change.) + + A coding program typically allocates hundreds of context variables. Each + coding context is initialized to zero before encoding or decoding. Value + zero represents equal probabilities for #1#s and #0#s with a minimal + confidence and therefore a maximum adaptation speed. Each message bit is + encoded using a coding context determined as a function of previously + encoded message bits. The decoder therefore can examine the previously + decoded message bits and decode the current bit using the same context as + the encoder. This is critical for proper decoding. +*/ +typedef unsigned char BitContext; + + +/** Performs ZP-Coder encoding and decoding. A ZPCodec object must either + constructed for encoding or for decoding. The ZPCodec object is connected + with a \Ref{ByteStream} object specified at construction time. A ZPCodec + object constructed for decoding reads code bits from the ByteStream and + returns a message bit whenever function \Ref{decoder} is called. A + ZPCodec constructed for encoding processes the message bits provided by + function \Ref{encoder} and writes the corresponding code bits to + ByteStream #bs#. + + You should never directly access a ByteStream object connected to a valid + ZPCodec object. The most direct way to access the ByteStream object + consists of using the "pass-thru" versions of functions \Ref{encoder} and + \Ref{decoder}. + + The ByteStream object can be accessed again after the destruction of the + ZPCodec object. Note that the encoder always flushes its internal buffers + and writes a few final code bytes when the ZPCodec object is destroyed. + Note also that the decoder often reads a few bytes beyond the last code byte + written by the encoder. This lag means that you must reposition the + ByteStream after the destruction of the ZPCodec object and before re-using + the ByteStream object (see \Ref{IFFByteStream}.) + + Please note also that the decoder has no way to reliably indicate the end + of the message bit sequence. The content of the message must be designed + in a way which indicates when to stop decoding. Simple ways to achieve + this consists of announcing the message length at the beginning (like a + pascal style string), or of defining a termination code (like a null + terminated string). */ + +class ZPCodec : public GPEnabled { +protected: + ZPCodec (GP<ByteStream> gbs, const bool encoding, const bool djvucompat=false); +public: + class Encode; + class Decode; + + /// Non-virtual destructor. + ~ZPCodec(); + /** Constructs a ZP-Coder. If argument #encoding# is zero, the ZP-Coder + object will read code bits from the ByteStream #bs# and return a message + bit whenever function #decoder# is called. If flag #encoding# is set + the ZP-Coder object will process the message bits provided by function + #encoder# and write code bits to ByteStream #bs#. Optional flag + #djvucompat# selects a slightly less efficient adaptation table which is + used by the DjVu project. This is required in order to ensure the + bitstream compatibility. You should not use this flag unless you want + to decode JB2, IW44 or BZZ encoded data. */ + static GP<ZPCodec> create( + GP<ByteStream> gbs, const bool encoding, const bool djvucompat=false); + + /** Encodes bit #bit# using context variable #ctx#. Argument #bit# must be + #0# or #1#. This function should only be used with ZP-Coder objects + created for encoding. It may modify the contents of variable #ctx# in + order to perform context adaptation. */ + void encoder(int bit, BitContext &ctx); + + /** Decodes a bit using context variable #ctx#. This function should only be + used with ZP-Coder objects created for decoding. It may modify the + contents of variable #ctx# in order to perform context adaptation. */ + int decoder(BitContext &ctx); + + /** Encodes bit #bit# without compression (pass-thru encoder). Argument + #bit# must be #0# or #1#. No compression will be applied. Calling this + function always increases the length of the code bit sequence by one + bit. */ + void encoder(int bit); + + /** Decodes a bit without compression (pass-thru decoder). This function + retrieves bits encoded with the pass-thru encoder. */ + int decoder(void); +#ifdef ZPCODEC_BITCOUNT + /** Counter for code bits (requires #-DZPCODEC_BITCOUNT#). This member + variable is available when the ZP-Coder is compiled with option + #-DZPCODEC_BITCOUNT#. Variable #bitcount# counts the number of code + bits processed by the coder since the construction of the object. This + variable can be used to evaluate how many code bits are spent on various + components of the message. */ + int bitcount; +#endif + // Table management (advanced stuff) + struct Table { + unsigned short p; + unsigned short m; + BitContext up; + BitContext dn; + }; + void newtable(ZPCodec::Table *table); + BitContext state(float prob1); + // Non-adaptive encoder/decoder + void encoder_nolearn(int pix, BitContext &ctx); + int decoder_nolearn(BitContext &ctx); + inline int IWdecoder(void); + inline void IWencoder(const bool bit); +protected: + // coder status + GP<ByteStream> gbs; // Where the data goes/comes from + ByteStream *bs; // Where the data goes/comes from + const bool encoding; // Direction (0=decoding, 1=encoding) + unsigned char byte; + unsigned char scount; + unsigned char delay; + unsigned int a; + unsigned int code; + unsigned int fence; + unsigned int subend; + unsigned int buffer; + unsigned int nrun; + // table + unsigned int p[256]; + unsigned int m[256]; + BitContext up[256]; + BitContext dn[256]; + // machine independent ffz + char ffzt[256]; + // encoder private + void einit (void); + void eflush (void); + void outbit(int bit); + void zemit(int b); + void encode_mps(BitContext &ctx, unsigned int z); + void encode_lps(BitContext &ctx, unsigned int z); + void encode_mps_simple(unsigned int z); + void encode_lps_simple(unsigned int z); + void encode_mps_nolearn(unsigned int z); + void encode_lps_nolearn(unsigned int z); + // decoder private + void dinit(void); + void preload(void); + int ffz(unsigned int x); + int decode_sub(BitContext &ctx, unsigned int z); + int decode_sub_simple(int mps, unsigned int z); + int decode_sub_nolearn(int mps, unsigned int z); +private: + // no copy allowed (hate c++) + ZPCodec(const ZPCodec&); + ZPCodec& operator=(const ZPCodec&); +#ifdef ZPCODEC_FRIEND + friend ZPCODEC_FRIEND; +#endif +}; + + + + + + +// INLINE CODE + +inline void +ZPCodec::encoder(int bit, BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (bit != (ctx & 1)) + { + encode_lps(ctx, z); + }else if (z >= 0x8000) + { + encode_mps(ctx, z); + }else + { + a = z; + } +} + +inline int +ZPCodec::IWdecoder(void) +{ + return decode_sub_simple(0,0x8000 + ((a+a+a) >> 3)); +} + +inline int +ZPCodec::decoder(BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (z <= fence) + { a = z; return (ctx&1); } + return decode_sub(ctx, z); +} + +inline void +ZPCodec::encoder_nolearn(int bit, BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (bit != (ctx & 1)) + encode_lps_nolearn(z); + else if (z >= 0x8000) + encode_mps_nolearn(z); + else + a = z; +} + +inline int +ZPCodec::decoder_nolearn(BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (z <= fence) + { a = z; return (ctx&1); } + return decode_sub_nolearn( (ctx&1), z); +} + +inline void +ZPCodec::encoder(int bit) +{ + if (bit) + encode_lps_simple(0x8000 + (a>>1)); + else + encode_mps_simple(0x8000 + (a>>1)); +} + +inline int +ZPCodec::decoder(void) +{ + return decode_sub_simple(0, 0x8000 + (a>>1)); +} + +inline void +ZPCodec::IWencoder(const bool bit) +{ + const int z = 0x8000 + ((a+a+a) >> 3); + if (bit) + { + encode_lps_simple(z); + }else + { + encode_mps_simple(z); + } +} + +// ------------ ADDITIONAL DOCUMENTATION + +/** @name ZPCodec Examples + + Binary adaptive coders are efficient and very flexible. Unfortunate + intellectual property issues however have limited their popularity. As a + consequence, few programmers have a direct experience of using such a + coding device. The few examples provided in this section demonstrate how + we think the ZP-Coder should be used. + + {\bf Encoding Multivalued Symbols} --- + Since the ZP-Coder is a strictly binary coder, every message must be + reduced to a sequence of bits (#0#s or #1#s). It is often convenient to + consider that a message is a sequence of symbols taking more than two + values. For instance, a character string may be a sequence of bytes, and + each byte can take 256 values. Each byte of course is composed of eight + bits that we can encode in sequence. The real issue however consists of + deciding how we will use context variables in order to let the ZP-Coder + learn the probability distribution of the byte values. + + The most significant bit #b0# decides whether the byte is in range 0..127 + or in range 128..255. We let the ZP-Coder learn how to predict this bit + by allocating one context variable for it. The second most significant + byte #b1# has two distinct meanings depending of bit #b0#. If bit #b0# is + #0#, bit #b1# decides whether the byte is in range 0..63 or 64..127. If + bit #b0# is #1#, bit #b1# decides whether the byte is in range 128..191 or + 192..255. The prediction for bit #b1# must therefore depend on the value + of #b0#. This is why we will allocate two context variables for this bit. + If bit #b0# is #0#, we will use the first variable; if bit #b0# is #1#, we + will use the second variable. The next bit #b2# has four meanings and + therefore we will use four context variables, etc. This analysis leads to + a total of #1+2+4+...+128# = #255# context variables for encoding one + byte. This encoding procedure can be understood as a binary decision + tree with a dedicated context variable for predicting each decision. + \begin{verbatim} + [>=128]----n---[>=64?]----n----[>31?] ... + \ `---y----[>95?] ... + \ + `--y---[>=192?]----n---[>=160?] ... + `---y---[>=224?] ... + \end{verbatim} + The following decoding function illustrates a very compact way to + implement such a decision tree. Argument #ctx# points to an array of 255 + #BitContext# variables. Macro #REPEAT8# is a shorthand notation for eight + repetitions of its argument. + \begin{verbatim} + int decode_8_bits(ZPCodec &zp, BitContext *ctx ) + { + int n = 1; + REPEAT8( { n = (n<<1) | (zp.decoder(ctx[n-1])); } ); + return n & 0xff; + } + \end{verbatim} + The binary representation of variable #n# is always composed of a #1# + followed by whichever bits have been decoded so far. This extra bit #1# in + fact is a nice trick to flatten out the tree structure and directly + address the array of context variables. Bit #b0# is decoded using the + first context variable since #n# is initially #1#. Bit #b1# is decoded + using one of the next two variables in the array, since #n# is either #2# + (#10# in binary) or #3# (#11# in binary). Bit #b2# will be decoded using + one of the next four variables, since #n# ranges from #4# (#100# in + binary) to #7# (#111# in binary). The final result is given by removing + the extra #1# in variable #n#. + + The corresponding encoding function is almost as compact. Argument #ctx# + again is an array of 255 #BitContext# variables. Each bit of byte #x# is + encoded and shifted into variable #n# as in the decoding function. + Variable #x# in fact contains the bits to be encoded. Variable #n# + contains a #1# followed by the already encoded bits. + \begin{verbatim} + void encode_8_bits(ZPCodec &zp, int x, BitContext *ctx ) + { + int n = 1; + REPEAT8( { int b=((x&0x80)?1:0); x=(x<<1); + zp.encoder(b,ctx[n-1]); n=(n<<1)|(b); } ); + } + \end{verbatim} + The ZP-Coder automatically adjusts the content of the context variables + while coding (recall the context variable argument is passed to functions + #encoder# and #decoder# by reference). The whole array of 255 context + variables can be understood as a "byte context variable". The estimated + probability of each byte value is indeed the product of the estimated + probabilities of the eight binary decisions that lead to that value in the + decision tree. All these probabilities are adapted by the underlying + adaptation algorithm of the ZP-Coder. + + {\bf Application} --- + We consider now a simple applications consisting of encoding the + horizontal and vertical coordinates of a cloud of points. Each coordinate + requires one byte. The following function illustrates a possible + implementation: + \begin{verbatim} + void encode_points(const char *filename, int n, int *x, int *y) + { + StdioByteStream bs(filename, "wb"); + bs.write32(n); // Write number of points. + ZPCodec zp(bs, 1); // Construct encoder and context vars. + BitContext ctxX[255], ctxY[255]; + memset(ctxX, 0, sizeof(ctxX)); + memset(ctxY, 0, sizeof(ctxY)); + for (int i=0; i<n; i++) { // Encode coordinates. + encode_8_bits(zp, x[i], ctxX); + encode_8_bits(zp, y[i], ctxY); + } + } + \end{verbatim} + The decoding function is very similar to the encoding function: + \begin{verbatim} + int decode_points(const char *filename, int *x, int *y) + { + StdioByteStream bs(filename,"rb"); + int n = bs.read32(); // Read number of points. + ZPCodec zp(bs, 0); // Construct decoder and context vars. + BitContext ctxX[255], ctxY[255]; + memset(ctxX, 0, sizeof(ctxX)); + memset(ctxY, 0, sizeof(ctxY)); + for (int i=0; i<n; i++) { // Decode coordinates. + x[i] = decode_8_bits(zp, ctxX); + y[i] = decode_8_bits(zp, ctxY); + } + return n; // Return number of points. + } + \end{verbatim} + The ZP-Coder automatically estimates the probability distributions of both + the horizontal and vertical coordinates. These estimates are used to + efficiently encode the point coordinates. This particular implementation + is a good option if we assume that the order of the points is significant + and that successive points are independent. It would be much smarter + otherwise to sort the points and encode relative displacements between + successive points. + + + {\bf Huffman Coding Tricks} --- + Programmers with experience in Huffman codes can see the similarity in the + ZP-Coder. Huffman codes also organize the symbol values as a decision + tree. The tree is balanced in such a way that each decision is as + unpredictable as possible (i.e. both branches must be equally probable). + This is very close to the ZP-Coder technique described above. Since we + allocate one context variable for each decision, our tree need not be + balanced: the context variable will track the decision statistics and the + ZP-Coder will compensate optimally. + + There are good reasons however to avoid unbalanced trees with the ZP-Coder. + Frequent symbol values may be located quite deep in a poorly balanced + tree. This increases the average number of message bits (the number of + decisions) required to code a symbol. The ZP-Coder will be called more + often, making the coding program slower. Furthermore, each message + bit is encoded using an estimated distribution. All these useless message + bits mean that the ZP-Coder has more distributions to adapt. This + extra adaptation work will probably increase the file size. + + Huffman codes are very fast when the tree structure is fixed beforehand. + Such {\em static Huffman codes} are unfortunately not very efficient + because the tree never matches the actual data distribution. This is why + such programs almost always define a data dependent tree structure. This + structure must then be encoded in the file since the decoder must know it + before decoding the symbols. Static Huffman codes however become very + efficient when decisions are encoded with the ZP-Coder. The tree + structure represents a priori knowledge about the distribution of the + symbol values. Small data discrepancies will be addressed transparently + by the ZP-Coder. + + + {\bf Encoding Numbers} --- + This technique is illustrated with the following number encoding example. + The multivalued technique described above is not practical with large + numbers because the decision tree has too many nodes and requires too many + context variables. This problem can be solved by using a priori knowledge + about the probability distribution of our numbers. + + Assume for instance that the distribution is symmetrical and that small + numbers are much more probable than large numbers. We will first group + our numbers into several sets. Each number is coded by first coding which + set contains the number and then coding a position within the set. Each + set contains #2^n# numbers that we consider roughly equiprobable. Since + the most probable values occur much more often, we want to model their + probability more precisely. Therefore we use small sets for the most + probable values and large sets for the least probable values, as + demonstrated below. + \begin{verbatim} + A---------------- {0} (size=1) + `------B---C---- {1} or {-1} (size=1) + \ `--- {2,3} or {-2,-3} (size=2) + D------ {4...131} or {-4...-131} (size=128) + `----- {132...32899} or {-132...-32899} (size=32768) + \end{verbatim} + We then organize a decision tree for coding the set identifier. This + decision tree is balanced using whatever a priori knowledge we have about + the probability distribution of the number values, just like a static + Huffman tree. Each decision (except the sign decision) is then coded + using a dedicated context variable. + \begin{verbatim} + if (! zp.decoder(ctx_A)) { // decision A + return 0; + } else { + if (! zp.decoder(ctx_B)) { // + decision B + if (! zp.decoder(ctx_C)) { // ++ decision C + if (! zp.decoder()) // +++ sign decision + return +1; + else + return -1; + } else { + if (! zp.decoder()) // +++ sign decision + return + 2 + zp.decoder(); + else + return - 2 - zp.decoder(); + } + } else { + if (! zp.decoder(ctx_D)) { // ++ decision D + if (! zp.decoder()) // +++ sign decision + return + 4 + decode_7_bits(zp); + else + return - 4 - decode_7_bits(zp); + } else { + if (! zp.decoder()) // +++ sign decision + return + 132 + decode_15_bits(zp); + else + return - 132 - decode_15_bits(zp); + } + } + } + \end{verbatim} + Note that the call #zp.decoder()# for coding the sign decision does not use + a context variable. This is a "pass-thru" variant of \Ref{decoder} which + bypasses the ZP-Coder and just reads a bit from the code sequence. There + is a corresponding "pass-thru" version of \Ref{encoder} for encoding such + bits. Similarly, functions #decode_7_bits# and #decode_15_bits# do not + take an array of context variables because, unlike function #decode_8_bits# + listed above, they are based on the pass-thru decoder instead of the + regular decoder. + + The ZP-Coder will not learn the probabilities of the numbers within a set + since no context variables have been allocated for that purpose. This + could be improved by allocating additional context variables for encoding + the position within the smaller sets and using the regular decoding + functions instead of the pass-thru variants. Only experimentation can tell + what works best for your particular encoding problem. + + + {\bf Understanding Adaptation} --- + We have so far explained that the ZP-Coder adaptation algorithm is able to + quickly estimate of the probability distribution of the message bits coded + using a particular context variable. It is also able to track slow + variations when the actual probabilities change while coding. + + Let us consider the ``cloud of points'' application presented above. + Suppose that we first code points located towards the left side and then + slowly move towards points located on the right side. The ZP-Coder will + first estimate that the X coordinates are rather on the left side. This + estimation will be progressively revised after seeing more points on the + right side. Such an ordering of the points obviously violates the point + independence assumption on which our code is based. Despite our inexact + assumptions, the tracking mechanism allows for better prediction of the X + coordinates and therefore better compression. + + However, this is not a perfect solution. The ZP-Coder tracks the changes + because every point seems to be a little bit more on the right side than + suggested by the previous points. The ZP-Coder coding algorithm is always + slightly misadjusted and we always lose a little on possible compression + ratio. This is not much of a problem when the probabilities drift slowly. + On the other hand, this can be very significant if the probabilities change + drastically. + + Adaptation is always associated with a small loss of efficiency. The + ZP-Coder updates the probability model whenever it suspects, {\em after + coding}, that the current settings were not optimal. The model will be + better next time, but a slight loss in compression has occurred. The + design of ZP-Coder of course minimizes this effect as much as possible. + Yet you will pay a price if you ask too much to the adaptation algorithm. + If you have millions of context variables, it will be difficult to train + them all. If the probability distributions change drastically while + coding, it will be difficult to track the changes fast enough. + + Adaptation on the other hand is a great simplification. A good data + compression program must (a) represent the data in order to make its + predictability apparent, and (b) perform the predictions and generate the + code bits. The ZP-Coder is an efficient and effortless solution for + implementing task (b). + + + {\bf Practical Debugging Tricks} --- + Sometimes you write an encoding program and a decoding program. + Unfortunately there is a bug: the decoding program decodes half the file + and then just outputs garbage. There is a simple way to locate the + problem. In the encoding program, after each call to #encoder#, print the + encoded bit and the value of the context variable. In the decoding + program, after each call to #decoder#, print the decoded bit and the value + of the context variable. Both program should print exactly the same thing. + When you find the difference, you find the bug. + + @memo Suggestions for efficiently using the ZP-Coder. */ +//@} + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/configure.in.in b/kviewshell/plugins/djvu/libdjvu/configure.in.in new file mode 100644 index 00000000..c2a1d2b6 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/configure.in.in @@ -0,0 +1,674 @@ +dnl Copyright (c) 2002 Leon Bottou and Yann Le Cun. +dnl Copyright (c) 2001 AT&T +dnl +dnl Most of these macros are derived from macros listed +dnl at the GNU Autoconf Macro Archive +dnl http://www.gnu.org/software/ac-archive/ +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA02111 USA +dnl + +dnl ------------------------------------------------------- +dnl @synopsis AC_CHECK_CXX_OPT(OPTION, +dnl ACTION-IF-OKAY,ACTION-IF-NOT-OKAY) +dnl Check if compiler accepts option OPTION. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CHECK_CXX_OPT],[ + opt="$1" + AC_MSG_CHECKING([if $CXX accepts $opt]) + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + AC_MSG_RESULT(yes) + rm conftest.* + $2 + else + AC_MSG_RESULT(no) + rm conftest.* + $3 + fi +]) + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_MEMBER_TEMPLATES +dnl If the compiler supports member templates, +dnl define HAVE_MEMBER_TEMPLATES. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_MEMBER_TEMPLATES], +[AC_CACHE_CHECK(whether the compiler supports member templates, +ac_cv_cxx_member_templates, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +template<class T, int N> class A +{ public: + template<int N2> A<T,N> operator=(const A<T,N2>& z) { return A<T,N>(); } +};],[A<double,4> x; A<double,7> y; x = y; return 0;], + ac_cv_cxx_member_templates=yes, ac_cv_cxx_member_templates=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_member_templates" = yes; then + AC_DEFINE(HAVE_MEMBER_TEMPLATES,1, + [define if the compiler supports member templates]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_NAMESPACES +dnl Define HAVE_NAMESPACES if the compiler supports +dnl namespaces. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_NAMESPACES], +[AC_CACHE_CHECK(whether the compiler implements namespaces, +ac_cv_cxx_namespaces, +[ AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([namespace Outer { namespace Inner { int i = 0; }}], + [using namespace Outer::Inner; return i;], + ac_cv_cxx_namespaces=yes, ac_cv_cxx_namespaces=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_namespaces" = yes && test "$ac_debug" = no; then + AC_DEFINE(HAVE_NAMESPACES,1, + [define if the compiler implements namespaces]) +fi +]) + + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_TYPENAME +dnl Define HAVE_TYPENAME if the compiler recognizes +dnl keyword typename. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_TYPENAME], +[AC_CACHE_CHECK(whether the compiler recognizes typename, +ac_cv_cxx_typename, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([template<typename T>class X {public:X(){}};], +[X<float> z; return 0;], + ac_cv_cxx_typename=yes, ac_cv_cxx_typename=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_typename" = yes; then + AC_DEFINE(HAVE_TYPENAME,1,[define if the compiler recognizes typename]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_STDINCLUDES +dnl Define HAVE_STDINCLUDES if the compiler has the +dnl new style include files (without the .h) +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_STDINCLUDES], +[AC_CACHE_CHECK(whether the compiler comes with standard includes, +ac_cv_cxx_stdincludes, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([#include <new> +struct X { int a; X(int a):a(a){}; }; +X* foo(void *x) { return new(x) X(2); } ],[], + ac_cv_cxx_stdincludes=yes, ac_cv_cxx_stdincludes=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_stdincludes" = yes; then + AC_DEFINE(HAVE_STDINCLUDES,1, + [define if the compiler comes with standard includes]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_BOOL +dnl If the compiler recognizes bool as a separate built-in type, +dnl define HAVE_BOOL. Note that a typedef is not a separate +dnl type since you cannot overload a function such that it +dnl accepts either the basic type or the typedef. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_BOOL], +[AC_CACHE_CHECK(whether the compiler recognizes bool as a built-in type, +ac_cv_cxx_bool, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +int f(int x){return 1;} +int f(char x){return 1;} +int f(bool x){return 1;} +],[bool b = true; return f(b);], + ac_cv_cxx_bool=yes, ac_cv_cxx_bool=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_bool" = yes; then + AC_DEFINE(HAVE_BOOL,1,[define if bool is a built-in type]) +fi +]) + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_EXCEPTIONS +dnl If the C++ compiler supports exceptions handling (try, +dnl throw and catch), define HAVE_EXCEPTIONS. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_EXCEPTIONS], +[AC_CACHE_CHECK(whether the compiler supports exceptions, +ac_cv_cxx_exceptions, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE(,[try { throw 1; } catch (int i) { return i; }], + ac_cv_cxx_exceptions=yes, ac_cv_cxx_exceptions=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_exceptions" = yes; then + AC_DEFINE(HAVE_EXCEPTIONS,1,[define if the compiler supports exceptions]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_RPO +dnl Defines option --enable-rpo and searches program RPO. +dnl Set output variables CXXRPOFLAGS and RPO. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_RPO], +[ CXXRPOFLAGS= + RPO_YES='#' + RPO_NO='' + if test x$GXX = xyes ; then + AC_ARG_ENABLE([rpo], + AC_HELP_STRING([--enable-rpo], + [Enable compilation with option -frepo]), + [ac_rpo=$enableval], [ac_rpo=no] ) + if test x$ac_rpo != xno ; then + CXXRPOFLAGS='-frepo -fno-rtti' + RPO_YES='' + RPO_NO='#' + fi + fi + AC_SUBST(CXXRPOFLAGS) + AC_SUBST(RPO_YES) + AC_SUBST(RPO_NO) +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl This macro figures out how to build C programs using POSIX +dnl threads. It sets the PTHREAD_LIBS output variable to the threads +dnl library and linker flags, and the PTHREAD_CFLAGS output variable +dnl to any special C compiler flags that are needed. (The user can also +dnl force certain compiler flags/libs to be tested by setting these +dnl environment variables.). +dnl ------------------------------------------------------------------ +AC_DEFUN([AC_PATH_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_pthread_ok=no +# First, check if the POSIX threads header, pthread.h, is available. +# If it isn't, don't bother looking for the threads libraries. +AC_CHECK_HEADER(pthread.h, , acx_pthread_ok=noheader) +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works. +if test x${PTHREAD_LIBS+set} = xset || + test x${PTHREAD_CFLAGS+set} = xset ; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([provided PTHREAD_LIBS/PTHREAD_CFLAGS.]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" +fi +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. +acx_pthread_flags="pthreads none -Kthread -kthread lthread + -pthread -pthreads -mthreads pthread + --thread-safe -mt" +# The ordering *is* (sometimes) important. +# Some notes on the individual items follow: +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +case "${host_cpu}-${host_os}" in + *solaris*) + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include <pthread.h>], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + *solaris* | alpha*-osf*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" +fi +AC_ARG_VAR(PTHREAD_LIBS, [Flags for linking pthread programs.]) +AC_ARG_VAR(PTHREAD_CFLAGS, [Flags for compiling pthread programs.]) +# execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + AC_DEFINE(HAVE_PTHREAD,1,[Define if pthreads are available]) + ifelse([$1],,:,[$1]) +else + ifelse([$2],,:,[$2]) +fi +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_COTHREADS([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Define HAVE_COTHREAD if cothreads can be used. +dnl Define HAVE_COTHREAD_PATCH if cothread libgcc patch is available +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_COTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_cothread=no +if test x$GXX = xyes ; then + AC_MSG_CHECKING([whether cothreads work with ${host_cpu}]) + case ${host_cpu} in + i?86|powerpc*|mips*|alpha*|hppa*) + acx_cothread=yes + ;; + esac + AC_MSG_RESULT($acx_cothread) +fi +if test x$acx_cothread != xno ; then + AC_MSG_CHECKING([whether libgcc contains the cothread patch]) + AC_LANG_PUSH([C++]) + AC_TRY_LINK([extern "C" { void *(*__get_eh_context_ptr)(); + void *__new_eh_context(void); }], + [ __get_eh_context_ptr = &__new_eh_context;], + [acx_cothread_patch=yes], [acx_cothread_patch=no]) + AC_LANG_POP([C++]) + AC_MSG_RESULT($acx_cothread_patch) + if test x$acx_cothread_patch = xno ; then + AC_MSG_CHECKING([if the cothread patch is critical]) + echo 'void foo() { throw "Hello"; }' > conftest.cc + compile="$CXX $CXXFLAGS -c conftest.cc" + check="nm conftest.o | grep sjthrow | cat > conftest.out" + acx_cothread_patch=yes + if AC_TRY_EVAL(compile) && AC_TRY_EVAL(check) ; then + if test -z "`cat conftest.out`" ; then + acx_cothread_patch=no + fi + fi + AC_MSG_RESULT($acx_cothread_patch) + rm conftest.* + if test x$acx_cothread_patch = xyes ; then + acx_cothread=no + AC_MSG_WARN([Cothread cannot work without the patch]) + else + AC_MSG_WARN([Applying the patch is recommended anyway]) + fi + AC_MSG_WARN([See the INSTALL file for more information]) + fi +fi +# Must do. +if test x$acx_cothread = xyes ; then + AC_DEFINE(HAVE_COTHREAD,1, + [Define if cothreads are available.]) + if test x$acx_cothread_patch = xyes ; then + AC_DEFINE(HAVE_COTHREAD_PATCH,1, + [Define if libgcc contains the cothread patch.]) + fi + ifelse([$1],,:,[$1]) +else + ifelse([$2],,:,[$2]) +fi +]) + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_THREADS([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process optional option --enable-threads +dnl Check availability of pthreads or cothreads +dnl using AC_PATH_PTHREAD and AC_PATH_COTHREAD. +dnl Set output variable THREADS_LIBS and THREADS_CFLAGS +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_THREADS], [ +AC_ARG_ENABLE(threads, + AC_HELP_STRING([--enable-threads], + [select threading model (default is auto)]), + ac_use_threads=$enableval, ac_use_threads=auto) +ac_threads=no +if test x$ac_use_threads != xno ; then + case x$ac_use_threads in + x|xyes|xauto|xposix|xpthread) + AC_PATH_PTHREAD( + [ ac_threads=pthread + ac_use_threads=pthread + THREAD_LIBS="$PTHREAD_LIBS" + THREAD_CFLAGS="$PTHREAD_CFLAGS -DTHREADMODEL=POSIXTHREADS" + ] ) + ;; + esac + case x$ac_use_threads in + xposix|xpthread) + ;; + x|xyes|xauto|xcothread) + AC_PATH_COTHREAD( + [ ac_threads=cothread + THREAD_CFLAGS="-DTHREADMODEL=COTHREADS" + ] ) + ;; + *) + AC_MSG_ERROR( +[Invalid argument for --enable-threads +Valid arguments are: yes, no, posix, pthread, cothread, auto.]) + ;; + esac +fi +AC_SUBST(THREAD_LIBS) +AC_SUBST(THREAD_CFLAGS) +AC_MSG_CHECKING([threading model]) +AC_MSG_RESULT($ac_threads) +if test $ac_threads != no ; then + AC_MSG_RESULT([setting THREAD_CFLAGS=$THREAD_CFLAGS]) + AC_MSG_RESULT([setting THREAD_LIBS=$THREAD_LIBS]) + ifelse([$1],,:,[$1]) +else + ifelse([$2],,:,[$2]) +fi +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_TIFF([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-tiff +dnl Search LIBTIFF. Define HAVE_TIFF. +dnl Set output variable TIFF_CFLAGS and TIFF_LIBS +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_TIFF], +[ + AC_ARG_VAR(TIFF_LIBS) + AC_ARG_VAR(TIFF_CFLAGS) + ac_tiff=no + AC_ARG_WITH(tiff, + AC_HELP_STRING([--with-tiff=DIR], + [where libtiff is located]), + [ac_tiff=$withval], [ac_tiff=yes] ) + # Process specification + if test x$ac_tiff = xyes ; then + test x${TIFF_LIBS+set} != xset && TIFF_LIBS="-ltiff" + elif test x$ac_tiff != xno ; then + test x${TIFF_LIBS+set} != xset && TIFF_LIBS="-L$ac_tiff -ltiff" + test x${TIFF_CFLAGS+set} != xset && TIFF_CFLAGS="-I$ac_tiff" + fi + # Try linking + if test x$ac_tiff != xno ; then + AC_MSG_CHECKING([for the libtiff library]) + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $TIFF_CFLAGS" + CXXFLAGS="$CXXFLAGS $TIFF_CFLAGS" + LIBS="$LIBS $TIFF_LIBS" + AC_TRY_LINK([ +#ifdef __cplusplus +extern "C" { +#endif +#include <stdio.h> +#include <tiffio.h> +#ifdef __cplusplus +} +#endif ],[ +TIFFOpen(0,0);], + [ac_tiff=yes], [ac_tiff=no] ) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + AC_MSG_RESULT($ac_tiff) + fi + # Finish + if test x$ac_tiff = xno; then + TIFF_CFLAGS= ; TIFF_LIBS= + ifelse([$2],,:,[$2]) + else + AC_DEFINE(HAVE_TIFF,1,[Define if you have libtiff.]) + AC_MSG_RESULT([setting TIFF_CFLAGS=$TIFF_CFLAGS]) + AC_MSG_RESULT([setting TIFF_LIBS=$TIFF_LIBS]) + ifelse([$1],,:,[$1]) + fi +]) + +# C++ +AC_LANG(C++) +AC_CXX_BOOL +AC_CXX_EXCEPTIONS +AC_CXX_TYPENAME +AC_CXX_STDINCLUDES +AC_CXX_NAMESPACES +AC_CXX_MEMBER_TEMPLATES +AC_CXX_RPO + +# ---------------------------------------- +# Libraries +# ---------------------------------------- + +AC_CHECK_LIB(m,sqrt) +AC_CHECK_LIB(iconv,libiconv) + +# ---------------------------------------- +# Header Files +# ---------------------------------------- + +AC_HEADER_STDC +AC_HEADER_DIRENT +AC_HEADER_TIME +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(wchar.h wctype.h sys/mman.h iconv.h) +AC_CHECK_HEADERS(stdint.h sys/ipc.h sys/shm.h) + +# ---------------------------------------- +# Types +# ---------------------------------------- + +AC_CHECK_TYPES(wchar_t) +AC_CHECK_TYPES(long long int) +AC_CHECK_TYPES(mbstate_t,,,[#include "wchar.h"]) + +# ---------------------------------------- +# Functions +# ---------------------------------------- + +AC_FUNC_MMAP +AC_FUNC_FORK +AC_CHECK_FUNCS(wcrtomb iswspace) +AC_CHECK_FUNCS(putc_unlocked strerror vsnprintf) +AC_CHECK_FUNCS(gethostname iconv strftime getpwuid) + +# ---------------------------------------- +# Test auxilliary packages +# ---------------------------------------- + +# Search TIFF library +AC_PATH_TIFF(, +[ no_tiff=yes + AC_MSG_WARN([Tiff support is disabled]) ]) + +# Search MULTITHREADING library +AC_PATH_THREADS(, +[ no_threads=yes + AC_MSG_WARN([Thread support is disabled]) ]) + +# ---------------------------------------- +# Stuff added to config.h +# ---------------------------------------- + +# Fence +AH_TOP([ +#ifndef DJVU_CONFIG_H +#define DJVU_CONFIG_H +/* config.h: begin */ +]) + +# L18N Macros +AH_BOTTOM([ +/* - Miscellaneous */ +#define UNIX 1 +#define NEED_GNUG_PRAGMAS 0 + +/* - BOOL */ +#if !defined(HAVE_BOOL) && !defined(bool) +#define bool char +#define true 1 +#define false 0 +#endif + +/* - WCHAR etc.*/ +#if ! defined(HAVE_WCHAR_T) +#define HAS_WCHAR 0 +#define HAS_WCTYPE 0 +#define HAS_MBSTATE 0 +#else +#define HAS_WCHAR 1 +#if defined(HAVE_WCTYPE_H) && defined(HAVE_ISWSPACE) +#define HAS_WCTYPE 1 +#endif +#if defined(HAVE_MBSTATE_T) && defined(HAVE_WCRTOMB) +#define HAS_MBSTATE 1 +#endif +#endif +#if defined(HAVE_ICONV_H) && defined(HAVE_ICONV) +#define HAS_ICONV 1 +#else +#define HAS_ICONV 0 +#endif + +/* - I18N MESSAGES HELL */ +#define HAS_CTRL_C_IN_ERR_MSG 1 + +/* - CONTAINERS */ +#ifndef HAVE_MEMBER_TEMPLATES +#define GCONTAINER_NO_MEMBER_TEMPLATES +#endif +#ifndef HAVE_TYPENAME +#define GCONTAINER_NO_TYPENAME +#endif + +/* - COTHREAD */ +#ifdef HAVE_COTHREAD +#ifndef HAVE_COTHREAD_PATCH +#define NO_LIBGCC_HOOKS +#endif +#endif + +/* - JPEG */ +#ifdef HAVE_LIBJPEG +#define NEED_JPEG_DECODER +#endif + +/* - MMAP */ +#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H) +#define HAS_MEMMAP 1 +#else +#define HAS_MEMMAP 0 +#endif + +/* config.h: end */ +#endif +]) diff --git a/kviewshell/plugins/djvu/libdjvu/debug.cpp b/kviewshell/plugins/djvu/libdjvu/debug.cpp new file mode 100644 index 00000000..20c93e50 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/debug.cpp @@ -0,0 +1,299 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: debug.cpp,v 1.14 2005/05/27 14:26:00 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "debug.h" + +#if ( DEBUGLVL > 0 ) + +#include "GThreads.h" +#include "GContainer.h" +#include "GString.h" +#include "GString.h" +#include "ByteStream.h" +#include "GURL.h" + +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#ifndef UNIX +#ifndef WIN32 +#ifndef macintosh +#define UNIX +#endif +#endif +#endif + +static GCriticalSection debug_lock; +#ifdef RUNTIME_DEBUG_ONLY +static int debug_level = 0; +#else +static int debug_level = DEBUGLVL; +#endif +static int debug_id; +static FILE *debug_file; +static int debug_file_count; + +#if THREADMODEL==NOTHREADS +static DjVuDebug debug_obj; +#else +static GMap<long, DjVuDebug> & +debug_map(void) +{ + static GMap<long, DjVuDebug> xmap; + return xmap; +} +#endif + +DjVuDebug::DjVuDebug() + : block(0), indent(0) +{ + id = debug_id++; +#ifdef UNIX + if (debug_file_count++ == 0 && !debug_file) + set_debug_file(stderr); +#endif +} + +DjVuDebug::~DjVuDebug() +{ +#ifdef UNIX + if (--debug_file_count == 0) + { + if (debug_file && (debug_file != stderr)) + fclose(debug_file); + debug_file = 0; + } +#endif +} + +void +DjVuDebug::format(const char *fmt, ... ) +{ + if (! block) + { + va_list ap; + va_start(ap, fmt); + GUTF8String buffer(fmt,ap); + va_end(ap); + GCriticalSectionLock glock(&debug_lock); + if (debug_file) + { + fprintf(debug_file,"%s", (const char*)buffer); + fflush(debug_file); + } +#ifdef WIN32 + else + { + USES_CONVERSION; + OutputDebugString(A2CT((const char *)buffer)); + } +#endif + } +} + +void +DjVuDebug::set_debug_level(int lvl) +{ + debug_level = lvl; +} + +void +DjVuDebug::set_debug_file(FILE * file) +{ + GCriticalSectionLock glock(&debug_lock); + if (debug_file && (debug_file != stderr)) + fclose(debug_file); + debug_file = file; +} + +void +DjVuDebug::modify_indent(int rindent) +{ + indent += rindent; +} + +DjVuDebug& +DjVuDebug::lock(int lvl, int noindent) +{ + int threads_num=1; + debug_lock.lock(); + // Find Debug object +#if THREADMODEL==NOTHREADS + // Get no-threads debug object + DjVuDebug &dbg = debug_obj; +#else + // Get per-thread debug object + long threadid = (long) GThread::current(); + DjVuDebug &dbg = debug_map()[threadid]; + threads_num=debug_map().size(); +#endif + // Check level + dbg.block = (lvl > debug_level); + // Output thread id and indentation + if (! noindent) + { + if (threads_num>1) + dbg.format("[T%d] ", dbg.id); + int ind = dbg.indent; + char buffer[257]; + memset(buffer,' ', sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = 0; + while (ind > (int)sizeof(buffer)-1) + { + dbg.format("%s", buffer); + ind -= sizeof(buffer)-1; + } + if (ind > 0) + { + buffer[ind] = 0; + dbg.format("%s", buffer); + } + } + // Return + return dbg; +} + +void +DjVuDebug::unlock() +{ + debug_lock.unlock(); +} + +#define OP(type, fmt) \ +DjVuDebug& DjVuDebug::operator<<(type arg)\ +{ format(fmt, arg); return *this; } + +DjVuDebug& DjVuDebug::operator<<(bool arg) +{ + format("%s", arg ? "TRUE" : "FALSE"); return *this; +} + +OP(char, "%c") +OP(unsigned char, "%c") +OP(int, "%d") +OP(unsigned int, "%u") +OP(short int, "%hd") +OP(unsigned short int, "%hu") +OP(long, "%ld") +OP(unsigned long, "%lu") +OP(float, "%g") +OP(double, "%g") +OP(const void * const, "0x%08x") + +DjVuDebug& DjVuDebug::operator<<(const char * const ptr) +{ + GUTF8String buffer(ptr?ptr:"(null)"); + if(buffer.length() > 255) + { + buffer=buffer.substr(0,252)+"..."; + } + format("%s", (const char *)buffer); + return *this; +} + +DjVuDebug& DjVuDebug::operator<<(const unsigned char * const ptr) +{ + return operator<<( (const char *) ptr ); +} + +DjVuDebug& DjVuDebug::operator<<(const GUTF8String &ptr) +{ + GUTF8String buffer(ptr); + if(buffer.length() > 255) + buffer=buffer.substr(0,252)+"..."; + format("%s", (const char *)buffer); + return *this; +} + +DjVuDebugIndent::DjVuDebugIndent(int inc) + : inc(inc) +{ + DjVuDebug &dbg = DjVuDebug::lock(0,1); + dbg.modify_indent(inc); + dbg.unlock(); +} + +DjVuDebugIndent::~DjVuDebugIndent() +{ + DjVuDebug &dbg = DjVuDebug::lock(0,1); + dbg.modify_indent(-inc); + dbg.unlock(); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/debug.h b/kviewshell/plugins/djvu/libdjvu/debug.h new file mode 100644 index 00000000..c39390a2 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/debug.h @@ -0,0 +1,304 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: debug.h,v 1.12 2005/05/27 14:26:01 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include <stdio.h> +#ifdef WIN32 +# include <atlbase.h> // USES_CONVERSION, A2CT macro +# include <windows.h> // OutputDebugString +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +# endif +#endif + +/** @name debug.h + + Files #"debug.h"# and #"debug.cpp"# implement means to print debug + messages in a multithread safe way. Message are also marked with a thread + identifier. Under Windows, debug messages are directly sent to the + debugger using the Win32 function #OutputDebugString#. Under Unix, debug + messages are printed on the controlling terminal, preferably using device + #/dev/tty#. + + The preprocessor variable #DEBUGLVL# defines which debug code is going to + be compiled. Selecting #-DDEBUGLVL=0# (the default) disables all + debugging code. Selecting a positive values (e.g. #-DDEBUGLVL=4#) enables + more and more debugging code. + + Message output is controlled by the current debugging level (an integer + between #0# and #DEBUGLVL#). Greater values enable more messages. The + initial debugging level is set to the maximum value. The debugging level + can be changed using macro \Ref{DEBUG_SET_LEVEL}. + + Message indentation can be modified using macro \Ref{DEBUG_MAKE_INDENT}. + Messages are generated by macro \Ref{DEBUG_MSG} or its variants. The + argument of the macro can contain several components separated by operator + #<<#, as demonstrated in the example below: + \begin{verbatim} + DEBUG_MSG("The value of a[" << n << "] is " << a[n] << '\n'); + \end{verbatim} + + One more preprocessor variable #RUNTIME_DEBUG_ONLY# enables compilation + of debug code, but does not enable the debug messages automatically. + In order to see them the program should use \Ref{DEBUG_SET_LEVEL} to + change the level to anything greater than 0. Normally this happens when + user specifies option #-debug# in the command line. Usage of + #RUNTIME_DEBUG_ONLY# implies #DEBUGLVL=1# if not specified otherwise. + + Finally, #-DNO_DEBUG# or #-DNDEBUG# can be used instead of #-DDEBUGLVL=0#, + and #-D_DEBUG# can be used instead of #-DDEBUGLVL=0#. + + {\bf Historical Comment} --- Debug macros are rarely used in the reference + DjVu library because Leon thinks that debugging messages unnecessarily + clutter the code. Debug macros are used everywhere in the plugin code + because Andrew thinks that code without debugging messages is close to + useless. No agreement could be reached. Neither could they agree on + if cluttering header files with huge documentation chunks helps to + improve code readability. + + @memo + Macros for printing debug messages. + @version + #$Id: debug.h,v 1.12 2005/05/27 14:26:01 leonb Exp $# + @author + Andrew Erofeev <eaf@geocities.com> -- initial implementation \\ + Leon Bottou <leonb@research.att.com> -- cleanups */ +//@{ + +#ifndef DEBUGLVL +# ifdef NDEBUG +# define DEBUGLVL 0 +# endif +#endif +#ifndef DEBUGLVL +# ifdef NO_DEBUG +# define DEBUGLVL 0 +# endif +#endif +#ifndef DEBUGLVL +# ifdef RUNTIME_DEBUG_ONLY +# define DEBUGLVL 1 +# endif +#endif +#ifndef DEBUGLVL +# ifdef _DEBUG +# define DEBUGLVL 1 +# endif +#endif +#ifndef DEBUGLVL +# define DEBUGLVL 0 +#endif + +#if DEBUGLVL <= 0 + +# ifndef NO_DEBUG +# define NO_DEBUG +# endif +# ifndef NDEBUG +# define NDEBUG +# endif +# ifdef _DEBUG +# undef _DEBUG +# endif + +# define DEBUG_MAKE_INDENT(x) +# define DEBUG_SET_LEVEL(level) +# define DEBUG_MSG_LVL(level,x) +# define DEBUG_MSGN_LVL(level,x) + +#else + +# ifdef NO_DEBUG +# undef NO_DEBUG +# endif +# ifdef NDEBUG +# undef NDEBUG +# endif +# ifndef _DEBUG +# define _DEBUG +# endif + +class GUTF8String; + +// ------------ SUPPORT + +class DjVuDebug // DJVU_CLASS +{ +private: + int id; + int block; + int indent; + void format(const char *fmt, ... ); +public: + // construction + DjVuDebug(); + ~DjVuDebug(); + // access + static void set_debug_level(int lvl); + static void set_debug_file(FILE * file); + void modify_indent(int rindent); + static DjVuDebug& lock(int lvl, int noindent); + void unlock(); + // printing + DjVuDebug & operator<<(bool b); + DjVuDebug & operator<<(char c); + DjVuDebug & operator<<(unsigned char c); + DjVuDebug & operator<<(int i); + DjVuDebug & operator<<(unsigned int i); + DjVuDebug & operator<<(short int i); + DjVuDebug & operator<<(unsigned short int i); + DjVuDebug & operator<<(long i); + DjVuDebug & operator<<(unsigned long i); + DjVuDebug & operator<<(const char * const ptr); + DjVuDebug & operator<<(const unsigned char * const ptr); + DjVuDebug& operator<<(const GUTF8String &ptr); + DjVuDebug & operator<<(float f); + DjVuDebug & operator<<(double d); + DjVuDebug & operator<<(const void * const p); +}; + +class DjVuDebugIndent // DJVU_CLASS +{ +private: + int inc; +public: + DjVuDebugIndent(int inc=2); + ~DjVuDebugIndent(); +//#define DEBUG_MAKE_INDENT_2(x, y) DjVuDebugIndent debug_indent ## y (x) +//#define DEBUG_MAKE_INDENT_1(x, y) DEBUG_MAKE_INDENT_2(x, y) +#define DEBUG_MAKE_INDENT_1(x, y) DjVuDebugIndent debug_indent ## y (x) +}; + +// ------------ MAIN MACROS + +# define DEBUG_MAKE_INDENT(x) DEBUG_MAKE_INDENT_1(x, __LINE__) +# define DEBUG_SET_LEVEL(level) DjVuDebug::set_debug_level(level) +# define DEBUG_MSG_LVL(level,x) { ( DjVuDebug::lock(level,0) << x ).unlock(); } +# define DEBUG_MSGN_LVL(level,x) { ( DjVuDebug::lock(level,1) << x ).unlock(); } +# define DEBUG_MSGF_LVL(level,x) { ( DjVuDebug::lock(level,1) << x ).unlock(); } + +#endif + + +// ------------ EAF MACROS + +#if ( DEBUGLVL >= 1 ) +/** Generates a level 1 message */ +# define DEBUG1_MSG(x) DEBUG_MSG_LVL(1,x) +# define DEBUG1_MSGF(x) DEBUG_MSGF_LVL(1,x) +#else +# define DEBUG1_MSG(x) +# define DEBUG1_MSGF(x) +#endif +#if ( DEBUGLVL >= 2 ) +/** Generates a level 2 message */ +# define DEBUG2_MSG(x) DEBUG_MSG_LVL(2,x) +# define DEBUG2_MSGF(x) DEBUG_MSGF_LVL(2,x) +#else +# define DEBUG2_MSG(x) +# define DEBUG2_MSGF(x) +#endif +#if ( DEBUGLVL >= 3 ) +/** Generates a level 3 message */ +# define DEBUG3_MSG(x) DEBUG_MSG_LVL(3,x) +# define DEBUG3_MSGF(x) DEBUG_MSGF_LVL(3,x) +#else +# define DEBUG3_MSG(x) +# define DEBUG3_MSGF(x) +#endif +#if ( DEBUGLVL >= 4 ) +/** Generates a level 4 message */ +# define DEBUG4_MSG(x) DEBUG_MSG_LVL(4,x) +# define DEBUG4_MSGF(x) DEBUG_MSGF_LVL(4,x) +#else +# define DEBUG4_MSG(x) +# define DEBUG4_MSGF(x) +#endif + +#define DEBUG_RUNTIME_SET_LEVEL(level) DEBUG_SET_LEVEL(level) +/** Generates a level 1 message. */ +#define DEBUG_MSG(x) DEBUG1_MSG(x) +/** Generates a level 1 message without indentation. */ +#define DEBUG_MSGF(x) DEBUG1_MSGF(x) +/** Generates a level 1 message terminated with a newline. */ +#define DEBUG_MSGN(x) DEBUG_MSG(x<<'\n') + +//@} + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif // DEBUG_H diff --git a/kviewshell/plugins/djvu/pageRangeWidget.cpp b/kviewshell/plugins/djvu/pageRangeWidget.cpp new file mode 100644 index 00000000..338624b8 --- /dev/null +++ b/kviewshell/plugins/djvu/pageRangeWidget.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <kdebug.h> + +#include "pageRangeWidget.h" + + +PageRangeWidget::PageRangeWidget( Q_UINT16 _from, Q_UINT16 _to, Q_UINT16 _current, QWidget *parent, const char *name) : PageRangeWidget_base(parent, name) +{ + // Paranoid security checks + if ((from == 0) || (to == 0)) + return; + if (_from > _to) { + kdError() << "PageRangeWidget::PageRangeWidget(..): from > to" << endl; + _to = _from; + } + if (_current < _from) { + kdError() << "PageRangeWidget::PageRangeWidget(..): _current < _from" << endl; + _current = _from; + } + if (_current > _to) { + kdError() << "PageRangeWidget::PageRangeWidget(..): _current > _to" << endl; + _current = _to; + } + + connect(from, SIGNAL(valueChanged(int)), this, SLOT(fromValueChanged(int))); + connect(to, SIGNAL(valueChanged(int)), this, SLOT(toValueChanged(int))); + + from->setRange(_from, _to); + from->setValue(_current); + to->setRange(_from, _to); + to->setValue(_current); +} + + +void PageRangeWidget::toValueChanged(int val) +{ + if (val < from->value()) + from->setValue(val); +} + + +void PageRangeWidget::fromValueChanged(int val) +{ + if (val > to->value()) + to->setValue(val); +} + +#include "pageRangeWidget.moc" + diff --git a/kviewshell/plugins/djvu/pageRangeWidget.h b/kviewshell/plugins/djvu/pageRangeWidget.h new file mode 100644 index 00000000..ca9b4a00 --- /dev/null +++ b/kviewshell/plugins/djvu/pageRangeWidget.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005 by Stefan Kebekus * + * kebekus@kde.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef PAGERANGEWIDGET_H +#define PAGERANGEWIDGET_H + +#include <knuminput.h> + +#include "pageRangeWidget_base.h" + + +class PageRangeWidget : public PageRangeWidget_base +{ +Q_OBJECT + +public: + PageRangeWidget( Q_UINT16 _from, Q_UINT16 _to, Q_UINT16 _current, QWidget *parent = 0, const char *name = 0 ); + + Q_UINT16 getFrom() const {return (from == 0) ? 0 : from->value(); } + Q_UINT16 getTo() const {return (to == 0) ? 0 : to->value(); } + +private slots: + void toValueChanged(int val); + void fromValueChanged(int val); +}; + + +#endif // PAGERANGETOWIDGET_H diff --git a/kviewshell/plugins/djvu/pageRangeWidget_base.ui b/kviewshell/plugins/djvu/pageRangeWidget_base.ui new file mode 100644 index 00000000..29784c3a --- /dev/null +++ b/kviewshell/plugins/djvu/pageRangeWidget_base.ui @@ -0,0 +1,75 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>PageRangeWidget_base</class> +<widget class="QWidget"> + <property name="name"> + <cstring>PageRangeWidget_base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>641</width> + <height>49</height> + </rect> + </property> + <property name="caption"> + <string>fromToWidget_base</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>From page:</string> + </property> + </widget> + <widget class="KIntNumInput"> + <property name="name"> + <cstring>from</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>To page:</string> + </property> + </widget> + <widget class="KIntNumInput"> + <property name="name"> + <cstring>to</cstring> + </property> + </widget> + </hbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/kviewshell/plugins/djvu/prefs.kcfgc b/kviewshell/plugins/djvu/prefs.kcfgc new file mode 100644 index 00000000..93a7650b --- /dev/null +++ b/kviewshell/plugins/djvu/prefs.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=djvumultipage.kcfg +ClassName=Prefs +Singleton=true +Mutators=true
\ No newline at end of file |