diff options
Diffstat (limited to 'kdvi')
98 files changed, 12126 insertions, 0 deletions
diff --git a/kdvi/AUTHORS b/kdvi/AUTHORS new file mode 100644 index 00000000..0d869c99 --- /dev/null +++ b/kdvi/AUTHORS @@ -0,0 +1,39 @@ +The programm KDVI is based on the dvi-previewer xdvik, See below. KDVI +was written by Markku Hihnala with the help of many +collaborators. Later KDVI became a plug-in to KViewShell and Stefan +Kebekus re-implemented parts of the program. The current version of +KDVI shares a substantial amount of code with xdvi, not not very much +with the first versions of KDVI. + +------------------------------------------------------------------------------ + + +AUTHORS OF xdvik +================ + +This program, xdvik, was modified by kb@cs.umb.edu from Paul Vojta's +xdvi distribution for common path searching, GNU-style configuration, +etc. Send bug reports to tex-k@cs.umb.edu, not to Paul. See the +README for more info. + +Here are the credits for the original xdvi program: + +This program is the combined work of many people, including but not restricted to: + Eric Cooper, CMU + Bob Scheifler, MIT LCS + Paal Kvamme, Norwegian Institute of Technology + H\aa vard Eidnes, Norwegian Institute of Technology + Mark Eichin, MIT SIPB + Paul Vojta, UC Berkeley + Jeffrey Lee, U of Toronto + Donald Richardson, Clarkson Univ. + +In addition to the various comp.sources.x archives, current versions +of this program (the original xdvi, not xdvik) can also be obtained +via anonymous ftp from the following location: + + export.lcs.mit.edu [18.30.0.212] file contrib/xdvi.tar.Z +To ease the load on expo, you may also check other X archives, for example: + gatekeeper.dec.com [16.1.0.2] file pub/X11/contrib/xdvi.shar.Z + +Paul Vojta is now the primary maintainer. diff --git a/kdvi/ChangeLog b/kdvi/ChangeLog new file mode 100644 index 00000000..8345a95e --- /dev/null +++ b/kdvi/ChangeLog @@ -0,0 +1,9 @@ +Mon Jun 29 08:43:45 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu> + + * KDVI uses KFileDialog now. + +Version 0.4.2 + * [Robert Williams] Added version.h and ChangeLog + * [Robert Williams] Renamed Kdvi.kdelnk to kdvi.kdelnk + * [Robert Williams] Added -caption "%c" to kdvi.kdelnk + * [Robert Williams] Added getHelpMenu() diff --git a/kdvi/Makefile.am b/kdvi/Makefile.am new file mode 100644 index 00000000..dce4053a --- /dev/null +++ b/kdvi/Makefile.am @@ -0,0 +1,66 @@ +# set the include path for X, qt and KDE +INCLUDES= -I$(top_srcdir)/kviewshell \ + -I$(top_builddir)/kviewshell \ + $(all_includes) $(LIBFREETYPE_CFLAGS) +# claim, which subdirectories you want to install +SUBDIRS = . pix + +bin_PROGRAMS = kdvi + +# you can add here more. This one gets installed +kde_module_LTLIBRARIES= kdvipart.la +noinst_PROGRAMS = squeeze + +# just to make sure, automake makes them +METASOURCES = AUTO + +kdvipart_la_SOURCES = renderedDviPagePixmap.cpp dviPageCache.cpp \ + kdvi_multipage.cpp kdvi_multipage_texthandling.cpp \ + dviRenderer.cpp bigEndianByteReader.cpp infodialog.cpp \ + psheader.c dviRenderer_draw.cpp dviRenderer_prescan.cpp dviRenderer_export.cpp \ + dviFile.cpp fontpool.cpp fontprogress.cpp psgs.cpp \ + fontMap.cpp fontEncoding.cpp fontEncodingPool.cpp \ + special.cpp util.cpp vf.cpp glyph.cpp \ + optionDialogFontsWidget.cpp optionDialogFontsWidget_base.ui \ + optionDialogSpecialWidget.cpp optionDialogSpecialWidget_base.ui \ + TeXFont.cpp TeXFont_PK.cpp TeXFont_PFB.cpp TeXFont_TFM.cpp \ + TeXFontDefinition.cpp dviWidget.cpp dvisourcesplitter.cpp \ + prefs.kcfgc + +kde_kcfg_DATA = kdvi.kcfg + +kdvipart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kdvipart_la_LIBADD = $(LIBFREETYPE_LIBS) -lkparts \ + $(top_builddir)/kviewshell/libkmultipage.la + +# Which sources should be compiled for squeeze. +squeeze_SOURCES = squeeze.c + +KDE_OPTIONS = nofinal + +kdvi_SOURCES = main.cpp +kdvi_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kdvi_LDADD = ../kviewshell/libifaces.la ../kviewshell/libkviewshell.la -lkparts + +## this option you can leave out. Just, if you use "make dist", you need it +noinst_HEADERS = dvi.h dviRenderer.h xdvi.h + +messages: rc.cpp + $(PREPARETIPS) > tips.cpp + $(XGETTEXT) *.cpp -o $(podir)/kdvi.pot + rm -f tips.cpp + +xdg_apps_DATA = kdvi.desktop + +tip_DATA = tips +tipdir = $(kde_datadir)/kdvi + +partdir = $(kde_datadir)/kdvi +part_DATA = ../kviewshell/kviewshell.rc kdvi_part.rc + +kde_services_DATA = kdvimultipage.desktop + +psheader.c: psheader.txt squeeze + ./squeeze $(srcdir)/psheader.txt $@ + +CLEANFILES = psheader.c diff --git a/kdvi/TODO b/kdvi/TODO new file mode 100644 index 00000000..44a4433c --- /dev/null +++ b/kdvi/TODO @@ -0,0 +1,30 @@ +ToDo-List for kdvi + +URGENT / URGENT BUGFIXING + +o improve performance and perceived performance (see also under 'highly desirable') + +o do not render the same PS code more than once if all pages have the same PS code +o add "papersize" which does not display the margin +o get rid of useless README.kdvi +o Proper handling of the base-url +o add "tt.dvi.gz" to the list of recent files, not "/tmp/kviews....." +o If one presses PgDn for a while, kdvi spends minutes browsing through pages... + +HIGHLY DESIRABLE + +o Speedup, in particular for ghostscript and glyph enlarging. + +o Support papersize information given by the dvi-file on a page-by-page + basis. + +o asynchronous rendering of pages, so that browsing with pg up/down looks faster + + +NOT SO URGENT + +o Internal printing using QPrinter +o Magnifier window +o Two page view +o Support for even more TeX specials +o more robust Error handling with throw/catch; no need to abort just because a PK-file is bad.
\ No newline at end of file diff --git a/kdvi/TeXFont.cpp b/kdvi/TeXFont.cpp new file mode 100644 index 00000000..829b00fb --- /dev/null +++ b/kdvi/TeXFont.cpp @@ -0,0 +1,9 @@ +#include <config.h> + +#include "glyph.h" +#include "TeXFont.h" + +TeXFont::~TeXFont() +{ + ; +} diff --git a/kdvi/TeXFont.h b/kdvi/TeXFont.h new file mode 100644 index 00000000..8ff70499 --- /dev/null +++ b/kdvi/TeXFont.h @@ -0,0 +1,48 @@ +// -*- C++ -*- +// TeXFont.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#ifndef _TEXFONT_H +#define _TEXFONT_H + +#include "TeXFontDefinition.h" +#include "glyph.h" + + +class TeXFont { + public: + TeXFont(TeXFontDefinition *_parent) + { + parent = _parent; + errorMessage = QString::null; + }; + + virtual ~TeXFont(); + + void setDisplayResolution() + { + for(unsigned int i=0; i<TeXFontDefinition::max_num_of_chars_in_font; i++) + glyphtable[i].shrunkenCharacter.resize(0, 0); + }; + + virtual glyph* getGlyph(Q_UINT16 character, bool generateCharacterPixmap=false, const QColor& color=Qt::black) = 0; + + // Checksum of the font. Used e.g. by PK fonts. This field is filled + // in by the constructor, or set to 0.0, if the font format does not + // contain checksums. + Q_UINT32 checksum; + + // If the font or if some glyphs could not be loaded, error messages + // will be put here. + QString errorMessage; + + protected: + glyph glyphtable[TeXFontDefinition::max_num_of_chars_in_font]; + TeXFontDefinition *parent; +}; + +#endif diff --git a/kdvi/TeXFontDefinition.cpp b/kdvi/TeXFontDefinition.cpp new file mode 100644 index 00000000..9b745c83 --- /dev/null +++ b/kdvi/TeXFontDefinition.cpp @@ -0,0 +1,250 @@ +// $Id$ +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <qfile.h> + +#include "dviRenderer.h" +#include "fontpool.h" +#include "kdvi.h" +#ifdef HAVE_FREETYPE +#include "TeXFont_PFB.h" +#endif +#include "TeXFont_PK.h" +#include "TeXFont_TFM.h" +#include "TeXFontDefinition.h" +#include "xdvi.h" + +extern const int MFResolutions[]; + +#define PK_PRE 247 +#define PK_ID 89 +#define PK_MAGIC (PK_PRE << 8) + PK_ID +#define GF_PRE 247 +#define GF_ID 131 +#define GF_MAGIC (GF_PRE << 8) + GF_ID +#define VF_PRE 247 +#define VF_ID_BYTE 202 +#define VF_MAGIC (VF_PRE << 8) + VF_ID_BYTE + +// #define DEBUG_FONT + + +TeXFontDefinition::TeXFontDefinition(QString nfontname, double _displayResolution_in_dpi, Q_UINT32 chk, Q_INT32 _scaled_size_in_DVI_units, + class fontPool *pool, double _enlargement) +{ +#ifdef DEBUG_FONT + kdDebug(4300) << "TeXFontDefinition::TeXFontDefinition(...); fontname=" << nfontname << ", enlargement=" << _enlargement << endl; +#endif + + enlargement = _enlargement; + font_pool = pool; + fontname = nfontname; + font = 0; + displayResolution_in_dpi = _displayResolution_in_dpi; + checksum = chk; + flags = TeXFontDefinition::FONT_IN_USE; + file = 0; + filename = QString::null; + scaled_size_in_DVI_units = _scaled_size_in_DVI_units; + + macrotable = 0; + + // By default, this font contains only empty characters. After the + // font has been loaded, this function pointer will be replaced by + // another one. + set_char_p = &dviRenderer::set_empty_char; +} + + +TeXFontDefinition::~TeXFontDefinition() +{ +#ifdef DEBUG_FONT + kdDebug(4300) << "discarding font " << fontname << " at " << (int)(enlargement * MFResolutions[font_pool->getMetafontMode()] + 0.5) << " dpi" << endl; +#endif + + if (font != 0) { + delete font; + font = 0; + } + if (macrotable != 0) { + delete [] macrotable; + macrotable = 0; + } + + if (flags & FONT_LOADED) { + if (file != 0) { + fclose(file); + file = 0; + } + if (flags & FONT_VIRTUAL) + vf_table.clear(); + } +} + + +void TeXFontDefinition::fontNameReceiver(const QString& fname) +{ +#ifdef DEBUG_FONT + kdDebug(4300) << "void TeXFontDefinition::fontNameReceiver( " << fname << " )" << endl; +#endif + + flags |= TeXFontDefinition::FONT_LOADED; + filename = fname; +#ifdef HAVE_FREETYPE + fullFontName = QString::null; + fullEncodingName = QString::null; +#endif + + file = fopen(QFile::encodeName(filename), "r"); + // Check if the file could be opened. If not, try to find the file + // in the DVI file's directory. If that works, modify the filename + // accordingly and go on. + if (file == 0) { + QString filename_test(font_pool->getExtraSearchPath() + "/" + filename); + file = fopen( QFile::encodeName(filename_test), "r"); + if (file == 0) { + kdError(4300) << i18n("Cannot find font %1, file %2.").arg(fontname).arg(filename) << endl; + return; + } else + filename = filename_test; + } + + set_char_p = &dviRenderer::set_char; + int magic = two(file); + + if (fname.endsWith("pk")) + if (magic == PK_MAGIC) { + fclose(file); + file = 0; + font = new TeXFont_PK(this); + set_char_p = &dviRenderer::set_char; + if ((checksum != 0) && (checksum != font->checksum)) + kdWarning(4300) << i18n("Checksum mismatch for font file %1").arg(filename) << endl; + fontTypeName = "TeX PK"; + return; + } + + if (fname.endsWith(".vf")) + if (magic == VF_MAGIC) { + read_VF_index(); + set_char_p = &dviRenderer::set_vf_char; + fontTypeName = i18n("TeX virtual"); + return; + } + + if (fname.endsWith(".tfm")) { + fclose(file); + file = 0; + font = new TeXFont_TFM(this); + set_char_p = &dviRenderer::set_char; + fontTypeName = i18n("TeX Font Metric"); + return; + } + + // None of these known types? Then it should be one of the font + // formats that are handled by the FreeType library + fclose(file); + file = 0; +#ifdef HAVE_FREETYPE + // Find the encoding for that font + const QString &enc = font_pool->fontsByTeXName.findEncoding(fontname); + + if (enc.isEmpty() == false) { +#ifdef DEBUG_FONT + kdDebug(4300) << "Font " << fontname << " uses encoding " << enc << endl; +#endif + font = new TeXFont_PFB(this, font_pool->encodingPool.findByName(enc), font_pool->fontsByTeXName.findSlant(fontname) ); + } else { +#ifdef DEBUG_FONT + kdDebug(4300) << "Font " << fontname << " does not have an encoding." << endl; +#endif + font = new TeXFont_PFB(this); + } + + set_char_p = &dviRenderer::set_char; + fontTypeName = i18n("FreeType"); + return; +#else + // If we don't have the FreeType library, we should never have + // reached this point. Complain, and leave this font blank + kdError(4300) << i18n("Cannot recognize format for font file %1").arg(filename) << endl; +#endif +} + + +void TeXFontDefinition::reset() +{ + if (font != 0) { + delete font; + font = 0; + } + + if (macrotable != 0) { + delete [] macrotable; + macrotable = 0; + } + + if (flags & FONT_LOADED) { + if (file != 0) { + fclose(file); + file = 0; + } + if (flags & FONT_VIRTUAL) + vf_table.clear(); + } + + filename = QString::null; + flags = TeXFontDefinition::FONT_IN_USE; + set_char_p = &dviRenderer::set_empty_char; +} + + +void TeXFontDefinition::setDisplayResolution(double _displayResolution_in_dpi) +{ + displayResolution_in_dpi = _displayResolution_in_dpi; + if (font != 0) + font->setDisplayResolution(); +} + + +/** mark_as_used marks the font, and all the fonts it referrs to, as + used, i.e. their FONT_IN_USE-flag is set. */ + +void TeXFontDefinition::mark_as_used() +{ +#ifdef DEBUG_FONT + kdDebug(4300) << "TeXFontDefinition::mark_as_used()" << endl; +#endif + + if (flags & TeXFontDefinition::FONT_IN_USE) + return; + + flags |= TeXFontDefinition::FONT_IN_USE; + + // For virtual fonts, also go through the list of referred fonts + if (flags & TeXFontDefinition::FONT_VIRTUAL) { + QIntDictIterator<TeXFontDefinition> it(vf_table); + while( it.current() ) { + it.current()->mark_as_used(); + ++it; + } + } +} + + +macro::macro() +{ + pos = 0; /* address of first byte of macro */ + end = 0; /* address of last+1 byte */ + dvi_advance_in_units_of_design_size_by_2e20 = 0; /* DVI units to move reference point */ + free_me = false; +} + + +macro::~macro() +{ + if ((pos != 0L) && (free_me == true)) + delete [] pos; +} diff --git a/kdvi/TeXFontDefinition.h b/kdvi/TeXFontDefinition.h new file mode 100644 index 00000000..e3effc2a --- /dev/null +++ b/kdvi/TeXFontDefinition.h @@ -0,0 +1,126 @@ +// -*- C++ -*- +/* + * The layout of a font information block. + * There is one of these for every loaded font or magnification thereof. + * Duplicates are eliminated: this is necessary because of possible recursion + * in virtual fonts. + * + * Also note the strange units. The design size is in 1/2^20 point + * units (also called micro-points), and the individual character widths + * are in the TFM file in 1/2^20 ems units, i.e., relative to the design size. + * + * We then change the sizes to SPELL units (unshrunk pixel / 2^16). + */ + +#ifndef _FONT_H +#define _FONT_H + +#include <qintdict.h> +#include <qstring.h> + +#include <stdio.h> + +class dviRenderer; +class TeXFont; + +typedef void (dviRenderer::*set_char_proc)(unsigned int, unsigned int); + + +// Per character information for virtual fonts + +class macro { + public: + macro(); + ~macro(); + + unsigned char *pos; /* address of first byte of macro */ + unsigned char *end; /* address of last+1 byte */ + Q_INT32 dvi_advance_in_units_of_design_size_by_2e20; /* DVI units to move reference point */ + bool free_me; // if memory at pos should be returned on destruction +}; + + +class TeXFontDefinition { + public: + // Currently, kdvi supports fonts with at most 256 characters to + // comply with "The DVI Driver Standard, Level 0". If you change + // this value here, make sure to go through all the source and + // ensure that character numbers are stored in ints rather than + // unsigned chars. + static const unsigned int max_num_of_chars_in_font = 256; + enum font_flags { + FONT_IN_USE = 1, // used for housekeeping + FONT_LOADED = 2, // if font file has been read + FONT_VIRTUAL = 4, // if font is virtual + FONT_KPSE_NAME = 8 // if kpathsea has already tried to find the font name + }; + + + TeXFontDefinition(QString nfontname, double _displayResolution_in_dpi, Q_UINT32 chk, Q_INT32 _scaled_size_in_DVI_units, + class fontPool *pool, double _enlargement); + ~TeXFontDefinition(); + + void reset(); + void fontNameReceiver(const QString&); + + // Members for character fonts + void setDisplayResolution(double _displayResolution_in_dpi); + + bool isLocated() const {return ((flags & FONT_KPSE_NAME) != 0);} + void markAsLocated() {flags |= FONT_KPSE_NAME;} + + void mark_as_used(); + class fontPool *font_pool; // Pointer to the pool that contains this font. + QString fontname; // name of font, such as "cmr10" + unsigned char flags; // flags byte (see values below) + double enlargement; + Q_INT32 scaled_size_in_DVI_units; // Scaled size from the font definition command; in DVI units + set_char_proc set_char_p; // proc used to set char + + // Resolution of the display device (resolution will usually be + // scaled, according to the zoom) + double displayResolution_in_dpi; + + FILE *file; // open font file or NULL + QString filename; // name of font file + + TeXFont *font; + macro *macrotable; // used by (loaded) virtual fonts + QIntDict<TeXFontDefinition> vf_table; // used by (loaded) virtual fonts, list of fonts used by this vf, + // acessible by number + TeXFontDefinition *first_font; // used by (loaded) virtual fonts, list of fonts used by this vf + +#ifdef HAVE_FREETYPE + const QString &getFullFontName() const {return fullFontName;} + const QString &getFullEncodingName() const {return fullEncodingName;} +#endif + const QString &getFontTypeName() const {return fontTypeName;} + +#ifdef HAVE_FREETYPE + /** For FREETYPE fonts, which use a map file, this field will + contain the full name of the font (e.g. 'Computer Modern'). If + the name does not exist, or cannot be found, this field will be + QString::null. Only subclasses of TeXFont should write into this + field. */ + QString fullFontName; + + /** For FREETYPE fonts, which use a map file, this field will + contain the full name of the font encoding (e.g. 'TexBase1'). If + the encoding name does not exist, or cannot be found, this field + will be QString::null. Only subclasses of TeXFont should write + into this field. */ + QString fullEncodingName; +#endif + + private: + Q_UINT32 checksum; + + /** This will be set to a human-readable description of the font, + e.g. "virtual" or "TeX PK", or "Type 1" */ + QString fontTypeName; + + // Functions related to virtual fonts + void read_VF_index(void ); +}; + +#endif diff --git a/kdvi/TeXFont_PFB.cpp b/kdvi/TeXFont_PFB.cpp new file mode 100644 index 00000000..927c84bc --- /dev/null +++ b/kdvi/TeXFont_PFB.cpp @@ -0,0 +1,294 @@ +// TeXFont_PFB.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +// This file is compiled only if the FreeType library is present on +// the system + +// Add header files alphabetically + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <qimage.h> + +#include "fontpool.h" + +#ifdef HAVE_FREETYPE + +#include "glyph.h" +#include "TeXFont_PFB.h" + +//#define DEBUG_PFB 1 + + +TeXFont_PFB::TeXFont_PFB(TeXFontDefinition *parent, fontEncoding *enc, double slant) + : TeXFont(parent) +{ +#ifdef DEBUG_PFB + if (enc != 0) + kdDebug(4300) << "TeXFont_PFB::TeXFont_PFB( parent=" << parent << ", encoding=" << enc->encodingFullName << " )" << endl; + else + kdDebug(4300) << "TeXFont_PFB::TeXFont_PFB( parent=" << parent << ", encoding=0 )" << endl; +#endif + + fatalErrorInFontLoading = false; + + int error = FT_New_Face( parent->font_pool->FreeType_library, parent->filename.local8Bit(), 0, &face ); + + if ( error == FT_Err_Unknown_File_Format ) { + errorMessage = i18n("The font file %1 could be opened and read, but its font format is unsupported.").arg(parent->filename); + kdError(4300) << errorMessage << endl; + fatalErrorInFontLoading = true; + return; + } else + if ( error ) { + errorMessage = i18n("The font file %1 is broken, or it could not be opened or read.").arg(parent->filename); + kdError(4300) << errorMessage << endl; + fatalErrorInFontLoading = true; + return; + } + + // Take care of slanting, and transform all characters in the font, if necessary. + if (slant != 0.0) { + // Construct a transformation matrix for vertical shear which will + // be used to transform the characters. + transformationMatrix.xx = 0x10000; + transformationMatrix.xy = (FT_Fixed)(slant * 0x10000); + transformationMatrix.yx = 0; + transformationMatrix.yy = 0x10000; + + FT_Set_Transform( face, &transformationMatrix, 0); + } + + if (face->family_name != 0) + parent->fullFontName = face->family_name; + + // Finally, we need to set up the charMap array, which maps TeX + // character codes to glyph indices in the font. (Remark: the + // charMap, and the font encoding procedure is necessary, because + // TeX is only able to address character codes 0-255 while + // e.g. Type1 fonts may contain several thousands of characters) + if (enc != 0) { + parent->fullEncodingName = enc->encodingFullName.remove(QString::fromLatin1( "Encoding" )); + parent->fullEncodingName = enc->encodingFullName.remove(QString::fromLatin1( "encoding" )); + + // An encoding vector is given for this font, i.e. an array of + // character names (such as: 'parenleft' or 'dotlessj'). We use + // the FreeType library function 'FT_Get_Name_Index()' to + // associate glyph indices to those names. +#ifdef DEBUG_PFB + kdDebug(4300) << "Trying to associate glyph indices to names from the encoding vector." << endl; +#endif + for(int i=0; i<256; i++) { + charMap[i] = FT_Get_Name_Index( face, (FT_String *)(enc->glyphNameVector[i].ascii()) ); +#ifdef DEBUG_PFB + kdDebug(4300) << i << ": " << enc->glyphNameVector[i] << ", GlyphIndex=" << charMap[i] << endl; +#endif + } + } else { + // If there is no encoding vector available, we check if the font + // itself contains a charmap that could be used. An admissible + // charMap will be stored under platform_id=7 and encoding_id=2. + FT_CharMap found = 0; + for (int n = 0; n<face->num_charmaps; n++ ) { + FT_CharMap charmap = face->charmaps[n]; + if ( charmap->platform_id == 7 && charmap->encoding_id == 2 ) { + found = charmap; + break; + } + } + + if ((found != 0) && (FT_Set_Charmap( face, found ) == 0)) { + // Feed the charMap array with the charmap data found in the + // previous step. +#ifdef DEBUG_PFB + kdDebug(4300) << "No encoding given: using charmap platform=7, encoding=2 that is contained in the font." << endl; +#endif + for(int i=0; i<256; i++) + charMap[i] = FT_Get_Char_Index( face, i ); + } else { + if ((found == 0) && (face->charmap != 0)) { +#ifdef DEBUG_PFB + kdDebug(4300) << "No encoding given: using charmap platform=" << face->charmap->platform_id << + ", encoding=" << face->charmap->encoding_id << " that is contained in the font." << endl; +#endif + for(int i=0; i<256; i++) + charMap[i] = FT_Get_Char_Index( face, i ); + } else { + // As a last resort, we use the identity map. +#ifdef DEBUG_PFB + kdDebug(4300) << "No encoding given, no suitable charmaps found in the font: using identity charmap." << endl; +#endif + for(int i=0; i<256; i++) + charMap[i] = i; + } + } + } +} + + +TeXFont_PFB::~TeXFont_PFB() +{ + FT_Done_Face( face ); +} + + +glyph *TeXFont_PFB::getGlyph(Q_UINT16 ch, bool generateCharacterPixmap, const QColor& color) +{ +#ifdef DEBUG_PFB + kdDebug(4300) << "TeXFont_PFB::getGlyph( ch=" << ch << ", '" << (char)(ch) << "', generateCharacterPixmap=" << generateCharacterPixmap << " )" << endl; +#endif + + // Paranoia checks + if (ch >= TeXFontDefinition::max_num_of_chars_in_font) { + kdError(4300) << "TeXFont_PFB::getGlyph(): Argument is too big." << endl; + return glyphtable; + } + + // This is the address of the glyph that will be returned. + struct glyph *g = glyphtable+ch; + + + if (fatalErrorInFontLoading == true) + return g; + + if ((generateCharacterPixmap == true) && ((g->shrunkenCharacter.isNull()) || (color != g->color)) ) { + int error; + unsigned int res = (unsigned int)(parent->displayResolution_in_dpi/parent->enlargement +0.5); + g->color = color; + + // Character height in 1/64th of points (reminder: 1 pt = 1/72 inch) + // Only approximate, may vary from file to file!!!! @@@@@ + + long int characterSize_in_printers_points_by_64 = (long int)((64.0*72.0*parent->scaled_size_in_DVI_units*parent->font_pool->getCMperDVIunit())/2.54 + 0.5 ); + error = FT_Set_Char_Size(face, 0, characterSize_in_printers_points_by_64, res, res ); + if (error) { + QString msg = i18n("FreeType reported an error when setting the character size for font file %1.").arg(parent->filename); + if (errorMessage.isEmpty()) + errorMessage = msg; + kdError(4300) << msg << endl; + g->shrunkenCharacter.resize(1,1); + g->shrunkenCharacter.fill(QColor(255, 255, 255)); + return g; + } + + // load glyph image into the slot and erase the previous one + if (parent->font_pool->getUseFontHints() == true) + error = FT_Load_Glyph(face, charMap[ch], FT_LOAD_DEFAULT ); + else + error = FT_Load_Glyph(face, charMap[ch], FT_LOAD_NO_HINTING ); + + if (error) { + QString msg = i18n("FreeType is unable to load glyph #%1 from font file %2.").arg(ch).arg(parent->filename); + if (errorMessage.isEmpty()) + errorMessage = msg; + kdError(4300) << msg << endl; + g->shrunkenCharacter.resize(1,1); + g->shrunkenCharacter.fill(QColor(255, 255, 255)); + return g; + } + + // convert to an anti-aliased bitmap + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) { + QString msg = i18n("FreeType is unable to render glyph #%1 from font file %2.").arg(ch).arg(parent->filename); + if (errorMessage.isEmpty()) + errorMessage = msg; + kdError(4300) << msg << endl; + g->shrunkenCharacter.resize(1,1); + g->shrunkenCharacter.fill(QColor(255, 255, 255)); + return g; + } + + FT_GlyphSlot slot = face->glyph; + + if ((slot->bitmap.width == 0) || (slot->bitmap.rows == 0)) { + if (errorMessage.isEmpty()) + errorMessage = i18n("Glyph #%1 is empty.").arg(ch); + kdError(4300) << i18n("Glyph #%1 from font file %2 is empty.").arg(ch).arg(parent->filename) << endl; + g->shrunkenCharacter.resize( 15, 15 ); + g->shrunkenCharacter.fill(QColor(255, 0, 0)); + g->x2 = 0; + g->y2 = 15; + } else { + QImage imgi(slot->bitmap.width, slot->bitmap.rows, 32); + imgi.setAlphaBuffer(true); + + // Do QPixmaps fully support the alpha channel? If yes, we use + // that. Otherwise, use other routines as a fallback + if (parent->font_pool->QPixmapSupportsAlpha) { + // If the alpha channel is properly supported, we set the + // character glyph to a colored rectangle, and define the + // character outline only using the alpha channel. That + // ensures good quality rendering for overlapping characters. + uchar *srcScanLine = slot->bitmap.buffer; + for(int row=0; row<slot->bitmap.rows; row++) { + uchar *destScanLine = imgi.scanLine(row); + for(int col=0; col<slot->bitmap.width; col++) { + destScanLine[4*col+0] = color.blue(); + destScanLine[4*col+1] = color.green(); + destScanLine[4*col+2] = color.red(); + destScanLine[4*col+3] = srcScanLine[col]; + } + srcScanLine += slot->bitmap.pitch; + } + } else { + // If the alpha channel is not supported... QT seems to turn + // the alpha channel into a crude bitmap which is used to mask + // the resulting QPixmap. In this case, we define the + // character outline using the image data, and use the alpha + // channel only to store "maximally opaque" or "completely + // transparent" values. When characters are rendered, + // overlapping characters are no longer correctly drawn, but + // quality is still sufficient for most purposes. One notable + // exception is output from the gftodvi program, which will be + // partially unreadable. + Q_UINT16 rInv = 0xFF - color.red(); + Q_UINT16 gInv = 0xFF - color.green(); + Q_UINT16 bInv = 0xFF - color.blue(); + + for(Q_UINT16 y=0; y<slot->bitmap.rows; y++) { + Q_UINT8 *srcScanLine = slot->bitmap.buffer + y*slot->bitmap.pitch; + unsigned int *destScanLine = (unsigned int *)imgi.scanLine(y); + for(Q_UINT16 col=0; col<slot->bitmap.width; col++) { + Q_UINT16 data = *srcScanLine; + // The value stored in "data" now has the following meaning: + // data = 0 -> white; data = 0xff -> use "color" + *destScanLine = qRgba(0xFF - (rInv*data + 0x7F) / 0xFF, + 0xFF - (gInv*data + 0x7F) / 0xFF, + 0xFF - (bInv*data + 0x7F) / 0xFF, + (data > 0x03) ? 0xff : 0x00); + destScanLine++; + srcScanLine++; + } + } + } + + g->shrunkenCharacter.convertFromImage (imgi, 0); + g->x2 = -slot->bitmap_left; + g->y2 = slot->bitmap_top; + } + } + + // Load glyph width, if that hasn't been done yet. + if (g->dvi_advance_in_units_of_design_size_by_2e20 == 0) { + int error = FT_Load_Glyph(face, charMap[ch], FT_LOAD_NO_SCALE); + if (error) { + QString msg = i18n("FreeType is unable to load metric for glyph #%1 from font file %2.").arg(ch).arg(parent->filename); + if (errorMessage.isEmpty()) + errorMessage = msg; + kdError(4300) << msg << endl; + g->dvi_advance_in_units_of_design_size_by_2e20 = 1; + } + g->dvi_advance_in_units_of_design_size_by_2e20 = (Q_INT32)(((Q_INT64)(1<<20) * (Q_INT64)face->glyph->metrics.horiAdvance) / (Q_INT64)face->units_per_EM); + } + + return g; +} + +#endif // HAVE_FREETYPE diff --git a/kdvi/TeXFont_PFB.h b/kdvi/TeXFont_PFB.h new file mode 100644 index 00000000..68211cdb --- /dev/null +++ b/kdvi/TeXFont_PFB.h @@ -0,0 +1,41 @@ +// -*- C++ -*- +// TeXFont_PFB.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +// This file is compiled only if the FreeType library is present on +// the system + +#ifndef _TEXFONT_PFB_H +#define _TEXFONT_PFB_H + +#include "TeXFont.h" + +#include <ft2build.h> +#include FT_FREETYPE_H + +class fontEncoding; +class glyph; + + +class TeXFont_PFB : public TeXFont { + public: + TeXFont_PFB(TeXFontDefinition *parent, fontEncoding *enc=0, double slant=0.0 ); + ~TeXFont_PFB(); + + glyph* getGlyph(Q_UINT16 character, bool generateCharacterPixmap=false, const QColor& color=Qt::black); + + private: + FT_Face face; + bool fatalErrorInFontLoading; + Q_UINT16 charMap[256]; + + // This matrix is used internally to describes the slant, if + // nonzero. Otherwise, this is undefined. + FT_Matrix transformationMatrix; +}; + +#endif diff --git a/kdvi/TeXFont_PK.cpp b/kdvi/TeXFont_PK.cpp new file mode 100644 index 00000000..6a3c9b3a --- /dev/null +++ b/kdvi/TeXFont_PK.cpp @@ -0,0 +1,781 @@ +/* + * Copyright (c) 1994 Paul Vojta. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * NOTE: + * xdvi is based on prior work as noted in the modification history, below. + */ + +/* + * DVI previewer for X. + * + * Eric Cooper, CMU, September 1985. + * + * Code derived from dvi-imagen.c. + * + * Modification history: + * 1/1986 Modified for X.10 --Bob Scheifler, MIT LCS. + * 7/1988 Modified for X.11 --Mark Eichin, MIT + * 12/1988 Added 'R' option, toolkit, magnifying glass + * --Paul Vojta, UC Berkeley. + * 2/1989 Added tpic support --Jeffrey Lee, U of Toronto + * 4/1989 Modified for System V --Donald Richardson, Clarkson Univ. + * 3/1990 Added VMS support --Scott Allendorf, U of Iowa + * 7/1990 Added reflection mode --Michael Pak, Hebrew U of Jerusalem + * 1/1992 Added greyscale code --Till Brychcy, Techn. Univ. Muenchen + * and Lee Hetherington, MIT + * 4/1994 Added DPS support, bounding box + * --Ricardo Telichevesky + * and Luis Miguel Silveira, MIT RLE. + */ + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <math.h> +#include <qbitmap.h> +#include <qfile.h> +#include <qimage.h> +#include <qpainter.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "fontpool.h" +#include "glyph.h" +#include "xdvi.h" +#include "TeXFontDefinition.h" +#include "TeXFont_PK.h" + + +//#define DEBUG_PK + +#define PK_PRE 247 +#define PK_ID 89 +#define PK_MAGIC (PK_PRE << 8) + PK_ID + + +extern void oops(QString message); + + + +TeXFont_PK::TeXFont_PK(TeXFontDefinition *parent) + : TeXFont(parent) +{ +#ifdef DEBUG_PK + kdDebug(4300) << "TeXFont_PK::TeXFont_PK( parent=" << parent << ")" << endl; +#endif + + for(unsigned int i=0; i<TeXFontDefinition::max_num_of_chars_in_font; i++) + characterBitmaps[i] = 0; + file = fopen(QFile::encodeName(parent->filename), "r"); + if (file == 0) + kdError(4300) << i18n("Cannot open font file %1.").arg(parent->filename) << endl; +#ifdef DEBUG_PK + else + kdDebug(4300) << "TeXFont_PK::TeXFont_PK(): file opened successfully" << endl; +#endif + + read_PK_index(); + +#ifdef DEBUG_PK + kdDebug(4300) << "TeXFont_PK::TeXFont_PK() ended" << endl; +#endif +} + + +TeXFont_PK::~TeXFont_PK() +{ + //@@@ Release bitmaps + + if (file != 0) { + fclose(file); + file = 0; + } +} + + +glyph* TeXFont_PK::getGlyph(Q_UINT16 ch, bool generateCharacterPixmap, const QColor& color) +{ +#ifdef DEBUG_PK + kdDebug(4300) << "TeXFont_PK::getGlyph( ch=" << ch << ", generateCharacterPixmap=" << generateCharacterPixmap << " )" << endl; +#endif + + // Paranoia checks + if (ch >= TeXFontDefinition::max_num_of_chars_in_font) { + kdError(4300) << "TeXFont_PK::getGlyph(): Argument is too big." << endl; + return glyphtable; + } + + // This is the address of the glyph that will be returned. + struct glyph *g = glyphtable+ch; + + // Check if the glyph is loaded. If not, load it now. + if (characterBitmaps[ch] == 0) { + // If the character is not defined in the PK file, mark the + // character as missing, and print an error message + if (g->addr == 0) { + kdError(4300) << i18n("TexFont_PK::operator[]: Character %1 not defined in font %2").arg(ch).arg(parent->filename) << endl; + g->addr = -1; + return g; + } + + // If the character has already been marked as missing, just + // return a pointer to the glyph (which will then be empty) + if (g->addr == -1) + return g; + + // Otherwise, try to load the character + fseek(file, g->addr, 0); + read_PK_char(ch); + // Check if the character could be loaded. If not, mark the + // character as 'missing', and return a pointer. + if (characterBitmaps[ch]->bits == 0) { + g->addr = -1; + return g; + } + } + + // At this point, g points to a properly loaded character. Generate + // a smoothly scaled QPixmap if the user asks for it. + if ((generateCharacterPixmap == true) && + ((g->shrunkenCharacter.isNull()) || (color != g->color)) && + (characterBitmaps[ch]->w != 0)) { + g->color = color; + double shrinkFactor = 1200 / parent->displayResolution_in_dpi; + + // All is fine? Then we rescale the bitmap in order to produce the + // required pixmap. Rescaling a character, however, is an art + // that requires some explanation... + // + // If we would just divide the size of the character and the + // coordinates by the shrink factor, then the result would look + // quite ugly: due to the ineviatable rounding errors in the + // integer arithmetic, the characters would be displaced by up to + // a pixel. That doesn't sound much, but on low-resolution + // devices, such as a notebook screen, the effect would be a + // "dancing line" of characters, which looks really bad. + + // Calculate the coordinates of the hot point in the shrunken + // bitmap. For simplicity, let us consider the x-coordinate + // first. In principle, the hot point should have an x-coordinate + // of (g->x/shrinkFactor). That, however, will generally NOT be an + // integral number. The cure is to translate the source image + // somewhat, so that the x-coordinate of the hot point falls onto + // the round-up of this number, i.e. + g->x2 = (int)ceil(g->x/shrinkFactor); + + // Translating and scaling then means that the pixel in the scaled + // image which covers the range [x,x+1) corresponds to the range + // [x*shrinkFactor+srcXTrans, (x+1)*shrinkFactor+srcXTrans), where + // srcXTrans is the following NEGATIVE number + double srcXTrans = shrinkFactor * (g->x/shrinkFactor - ceil(g->x/shrinkFactor)); + + // How big will the shrunken bitmap then become? If shrunk_width + // denotes that width of the scaled image, and + // characterBitmaps[ch]->w the width of the orininal image, we + // need to make sure that the following inequality holds: + // + // shrunk_width*shrinkFactor+srcXTrans >= characterBitmaps[ch]->w + // + // in other words, + int shrunk_width = (int)ceil( (characterBitmaps[ch]->w - srcXTrans)/shrinkFactor ); + + // Now do the same for the y-coordinate + g->y2 = (int)ceil(g->y/shrinkFactor); + double srcYTrans = shrinkFactor * (g->y/shrinkFactor - ceil(g->y/shrinkFactor )); + int shrunk_height = (int)ceil( (characterBitmaps[ch]->h - srcYTrans)/shrinkFactor ); + + // Turn the image into 8 bit + QByteArray translated(characterBitmaps[ch]->w * characterBitmaps[ch]->h); + Q_UINT8 *data = (Q_UINT8 *)translated.data(); + for(int x=0; x<characterBitmaps[ch]->w; x++) + for(int y=0; y<characterBitmaps[ch]->h; y++) { + Q_UINT8 bit = *(characterBitmaps[ch]->bits + characterBitmaps[ch]->bytes_wide*y + (x >> 3)); + bit = bit >> (x & 7); + bit = bit & 1; + data[characterBitmaps[ch]->w*y + x] = bit; + } + + // Now shrink the image. We shrink the X-direction first + QByteArray xshrunk(shrunk_width*characterBitmaps[ch]->h); + Q_UINT8 *xdata = (Q_UINT8 *)xshrunk.data(); + + // Do the shrinking. The pixel (x,y) that we want to calculate + // corresponds to the line segment from + // + // [shrinkFactor*x+srcXTrans, shrinkFactor*(x+1)+srcXTrans) + // + // The trouble is, these numbers are in general no integers. + + for(int y=0; y<characterBitmaps[ch]->h; y++) + for(int x=0; x<shrunk_width; x++) { + Q_UINT32 value = 0; + double destStartX = shrinkFactor*x+srcXTrans; + double destEndX = shrinkFactor*(x+1)+srcXTrans; + for(int srcX=(int)ceil(destStartX); srcX<floor(destEndX); srcX++) + if ((srcX >= 0) && (srcX < characterBitmaps[ch]->w)) + value += data[characterBitmaps[ch]->w*y + srcX] * 255; + + if (destStartX >= 0.0) + value += (Q_UINT32) (255.0*(ceil(destStartX)-destStartX) * data[characterBitmaps[ch]->w*y + (int)floor(destStartX)]); + if (floor(destEndX) < characterBitmaps[ch]->w) + value += (Q_UINT32) (255.0*(destEndX-floor(destEndX)) * data[characterBitmaps[ch]->w*y + (int)floor(destEndX)]); + + xdata[shrunk_width*y + x] = (int)(value/shrinkFactor + 0.5); + } + + // Now shrink the Y-direction + QByteArray xyshrunk(shrunk_width*shrunk_height); + Q_UINT8 *xydata = (Q_UINT8 *)xyshrunk.data(); + for(int x=0; x<shrunk_width; x++) + for(int y=0; y<shrunk_height; y++) { + Q_UINT32 value = 0; + double destStartY = shrinkFactor*y+srcYTrans; + double destEndY = shrinkFactor*(y+1)+srcYTrans; + for(int srcY=(int)ceil(destStartY); srcY<floor(destEndY); srcY++) + if ((srcY >= 0) && (srcY < characterBitmaps[ch]->h)) + value += xdata[shrunk_width*srcY + x]; + + if (destStartY >= 0.0) + value += (Q_UINT32) ((ceil(destStartY)-destStartY) * xdata[shrunk_width*(int)floor(destStartY) + x]); + if (floor(destEndY) < characterBitmaps[ch]->h) + value += (Q_UINT32) ((destEndY-floor(destEndY)) * xdata[shrunk_width*(int)floor(destEndY) + x]); + + xydata[shrunk_width*y + x] = (int)(value/shrinkFactor); + } + + QImage im32(shrunk_width, shrunk_height, 32); + im32.setAlphaBuffer(true); + // Do QPixmaps fully support the alpha channel? If yes, we use + // that. Otherwise, use other routines as a fallback + if (parent->font_pool->QPixmapSupportsAlpha) { + // If the alpha channel is properly supported, we set the + // character glyph to a colored rectangle, and define the + // character outline only using the alpha channel. That ensures + // good quality rendering for overlapping characters. + im32.fill(qRgb(color.red(), color.green(), color.blue())); + for(Q_UINT16 y=0; y<shrunk_height; y++) { + Q_UINT8 *destScanLine = (Q_UINT8 *)im32.scanLine(y); + for(Q_UINT16 col=0; col<shrunk_width; col++) + destScanLine[4*col+3] = xydata[shrunk_width*y + col]; + } + } else { + // If the alpha channel is not supported... QT seems to turn the + // alpha channel into a crude bitmap which is used to mask the + // resulting QPixmap. In this case, we define the character + // outline using the image data, and use the alpha channel only + // to store "maximally opaque" or "completely transparent" + // values. When characters are rendered, overlapping characters + // are no longer correctly drawn, but quality is still + // sufficient for most purposes. One notable exception is output + // from the gftodvi program, which will be partially unreadable. + Q_UINT16 rInv = 0xFF - color.red(); + Q_UINT16 gInv = 0xFF - color.green(); + Q_UINT16 bInv = 0xFF - color.blue(); + + Q_UINT8 *srcScanLine = xydata; + for(Q_UINT16 y=0; y<shrunk_height; y++) { + unsigned int *destScanLine = (unsigned int *)im32.scanLine(y); + for(Q_UINT16 col=0; col<shrunk_width; col++) { + Q_UINT16 data = *srcScanLine; + // The value stored in "data" now has the following meaning: + // data = 0 -> white; data = 0xff -> use "color" + *destScanLine = qRgba(0xFF - (rInv*data + 0x7F) / 0xFF, + 0xFF - (gInv*data + 0x7F) / 0xFF, + 0xFF - (bInv*data + 0x7F) / 0xFF, + (data > 0x03) ? 0xff : 0x00); + destScanLine++; + srcScanLine++; + } + } + } + + g->shrunkenCharacter.convertFromImage(im32,0); + g->shrunkenCharacter.setOptimization(QPixmap::BestOptim); + } + return g; +} + + + +#define ADD(a, b) ((Q_UINT32 *) (((char *) a) + b)) +#define SUB(a, b) ((Q_UINT32 *) (((char *) a) - b)) + + + +// This table is used for changing the bit order in a byte. The +// expression bitflp[byte] takes a byte in big endian and gives the +// little endian equivalent of that. +static const uchar bitflip[256] = { + 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, + 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, + 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, + 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, + 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, + 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, + 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, + 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, + 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, + 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, + 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, + 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, + 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, + 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, + 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, + 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255 +}; + +static Q_UINT32 bit_masks[33] = { + 0x0, 0x1, 0x3, 0x7, + 0xf, 0x1f, 0x3f, 0x7f, + 0xff, 0x1ff, 0x3ff, 0x7ff, + 0xfff, 0x1fff, 0x3fff, 0x7fff, + 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, + 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, + 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, + 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff +}; + + +#define PK_ID 89 +#define PK_CMD_START 240 +#define PK_X1 240 +#define PK_X2 241 +#define PK_X3 242 +#define PK_X4 243 +#define PK_Y 244 +#define PK_POST 245 +#define PK_NOOP 246 +#define PK_PRE 247 + + +int TeXFont_PK::PK_get_nyb(FILE *fp) +{ +#ifdef DEBUG_PK + kdDebug(4300) << "PK_get_nyb" << endl; +#endif + + unsigned temp; + if (PK_bitpos < 0) { + PK_input_byte = one(fp); + PK_bitpos = 4; + } + temp = PK_input_byte >> PK_bitpos; + PK_bitpos -= 4; + return (temp & 0xf); +} + + +int TeXFont_PK::PK_packed_num(FILE *fp) +{ +#ifdef DEBUG_PK + kdDebug(4300) << "PK_packed_num" << endl; +#endif + + int i,j; + + if ((i = PK_get_nyb(fp)) == 0) { + do { + j = PK_get_nyb(fp); + ++i; + } + while (j == 0); + while (i > 0) { + j = (j << 4) | PK_get_nyb(fp); + --i; + } + return (j - 15 + ((13 - PK_dyn_f) << 4) + PK_dyn_f); + } + else { + if (i <= PK_dyn_f) return i; + if (i < 14) + return (((i - PK_dyn_f - 1) << 4) + PK_get_nyb(fp) + + PK_dyn_f + 1); + if (i == 14) PK_repeat_count = PK_packed_num(fp); + else PK_repeat_count = 1; + return PK_packed_num(fp); + } +} + + +void TeXFont_PK::PK_skip_specials() +{ +#ifdef DEBUG_PK + kdDebug(4300) << "TeXFont_PK::PK_skip_specials() called" << endl; +#endif + + int i,j; + register FILE *fp = file; + +#ifdef DEBUG_PK + if (fp == 0) + kdDebug(4300) << "TeXFont_PK::PK_skip_specials(): file == 0" << endl; +#endif + + do { + PK_flag_byte = one(fp); + if (PK_flag_byte >= PK_CMD_START) { + switch (PK_flag_byte) { + case PK_X1 : + case PK_X2 : + case PK_X3 : + case PK_X4 : + i = 0; + for (j = PK_CMD_START; j <= PK_flag_byte; ++j) + i = (i << 8) | one(fp); + while (i--) (void) one(fp); + break; + case PK_Y : + (void) four(fp); + case PK_POST : + case PK_NOOP : + break; + default : + oops(i18n("Unexpected %1 in PK file %2").arg(PK_flag_byte).arg(parent->filename) ); + break; + } + } + } + while (PK_flag_byte != PK_POST && PK_flag_byte >= PK_CMD_START); + +#ifdef DEBUG_PK + kdDebug(4300) << "TeXFont_PK::PK_skip_specials() ended" << endl; +#endif +} + + +void TeXFont_PK::read_PK_char(unsigned int ch) +{ +#ifdef DEBUG_PK + kdDebug(4300) << "read_PK_char" << endl; +#endif + + int i, j; + int n; + int row_bit_pos; + bool paint_switch; + Q_UINT32 *cp; + register struct glyph *g; + register FILE *fp = file; + long fpwidth; + Q_UINT32 word = 0; + int word_weight, bytes_wide; + int rows_left, h_bit, count; + + g = glyphtable + ch; + PK_flag_byte = g->x2; + PK_dyn_f = PK_flag_byte >> 4; + paint_switch = ((PK_flag_byte & 8) != 0); + PK_flag_byte &= 0x7; + if (PK_flag_byte == 7) + n = 4; + else + if (PK_flag_byte > 3) + n = 2; + else + n = 1; + +#ifdef DEBUG_PK + kdDebug(4300) << "loading pk char " << ch << ", char type " << n << endl; +#endif + + if (characterBitmaps[ch] == 0) + characterBitmaps[ch] = new bitmap(); + + /* + * now read rest of character preamble + */ + if (n != 4) + fpwidth = num(fp, 3); + else { + fpwidth = sfour(fp); + (void) four(fp); /* horizontal escapement */ + } + (void) num(fp, n); /* vertical escapement */ + { + unsigned long w, h; + + w = num(fp, n); + h = num(fp, n); + if (w > 0x7fff || h > 0x7fff) + oops(i18n("The character %1 is too large in file %2").arg(ch).arg(parent->filename)); + characterBitmaps[ch]->w = w; + characterBitmaps[ch]->h = h; + } + g->x = snum(fp, n); + g->y = snum(fp, n); + + g->dvi_advance_in_units_of_design_size_by_2e20 = fpwidth; + + { + /* width must be multiple of 16 bits for raster_op */ + characterBitmaps[ch]->bytes_wide = ROUNDUP((int) characterBitmaps[ch]->w, 32) * 4; + register unsigned int size = characterBitmaps[ch]->bytes_wide * characterBitmaps[ch]->h; + characterBitmaps[ch]->bits = new char[size != 0 ? size : 1]; + } + + cp = (Q_UINT32 *) characterBitmaps[ch]->bits; + + /* + * read character data into *cp + */ + bytes_wide = ROUNDUP((int) characterBitmaps[ch]->w, 32) * 4; + PK_bitpos = -1; + + // The routines which read the character depend on the bit + // ordering. In principle, the bit order should be detected at + // compile time and the proper routing chosen. For the moment, as + // autoconf is somewhat complicated for the author, we prefer a + // simpler -even if somewhat slower approach and detect the ordering + // at runtime. That should of course be changed in the future. + + int wordSize; + bool bigEndian; + qSysInfo (&wordSize, &bigEndian); + + if (bigEndian) { + // Routine for big Endian machines. Applies e.g. to Motorola and + // (Ultra-)Sparc processors. + +#ifdef DEBUG_PK + kdDebug(4300) << "big Endian byte ordering" << endl; +#endif + + if (PK_dyn_f == 14) { /* get raster by bits */ + memset(characterBitmaps[ch]->bits, 0, (int) characterBitmaps[ch]->h * bytes_wide); + for (i = 0; i < (int) characterBitmaps[ch]->h; i++) { /* get all rows */ + cp = ADD(characterBitmaps[ch]->bits, i * bytes_wide); + row_bit_pos = 32; + for (j = 0; j < (int) characterBitmaps[ch]->w; j++) { /* get one row */ + if (--PK_bitpos < 0) { + word = one(fp); + PK_bitpos = 7; + } + if (--row_bit_pos < 0) { + cp++; + row_bit_pos = 32 - 1; + } + if (word & (1 << PK_bitpos)) + *cp |= 1 << row_bit_pos; + } + } + } else { /* get packed raster */ + rows_left = characterBitmaps[ch]->h; + h_bit = characterBitmaps[ch]->w; + PK_repeat_count = 0; + word_weight = 32; + word = 0; + while (rows_left > 0) { + count = PK_packed_num(fp); + while (count > 0) { + if (count < word_weight && count < h_bit) { + h_bit -= count; + word_weight -= count; + if (paint_switch) + word |= bit_masks[count] << word_weight; + count = 0; + } else + if (count >= h_bit && h_bit <= word_weight) { + if (paint_switch) + word |= bit_masks[h_bit] << (word_weight - h_bit); + *cp++ = word; + /* "output" row(s) */ + for (i = PK_repeat_count * bytes_wide / 4; i > 0; --i) { + *cp = *SUB(cp, bytes_wide); + ++cp; + } + rows_left -= PK_repeat_count + 1; + PK_repeat_count = 0; + word = 0; + word_weight = 32; + count -= h_bit; + h_bit = characterBitmaps[ch]->w; + } else { + if (paint_switch) + word |= bit_masks[word_weight]; + *cp++ = word; + word = 0; + count -= word_weight; + h_bit -= word_weight; + word_weight = 32; + } + } + paint_switch = 1 - paint_switch; + } + if (cp != ((Q_UINT32 *) (characterBitmaps[ch]->bits + bytes_wide * characterBitmaps[ch]->h))) + oops(i18n("Wrong number of bits stored: char. %1, font %2").arg(ch).arg(parent->filename)); + if (rows_left != 0 || h_bit != characterBitmaps[ch]->w) + oops(i18n("Bad pk file (%1), too many bits").arg(parent->filename)); + } + + // The data in the bitmap is now in the processor's bit order, + // that is, big endian. Since XWindows needs little endian, we + // need to change the bit order now. + register unsigned char* bitmapData = (unsigned char*) characterBitmaps[ch]->bits; + register unsigned char* endOfData = bitmapData + characterBitmaps[ch]->bytes_wide*characterBitmaps[ch]->h; + while(bitmapData < endOfData) { + *bitmapData = bitflip[*bitmapData]; + bitmapData++; + } + + } else { + + // Routines for small Endian start here. This applies e.g. to + // Intel and Alpha processors. + +#ifdef DEBUG_PK + kdDebug(4300) << "small Endian byte ordering" << endl; +#endif + + if (PK_dyn_f == 14) { /* get raster by bits */ + memset(characterBitmaps[ch]->bits, 0, (int) characterBitmaps[ch]->h * bytes_wide); + for (i = 0; i < (int) characterBitmaps[ch]->h; i++) { /* get all rows */ + cp = ADD(characterBitmaps[ch]->bits, i * bytes_wide); + row_bit_pos = -1; + for (j = 0; j < (int) characterBitmaps[ch]->w; j++) { /* get one row */ + if (--PK_bitpos < 0) { + word = one(fp); + PK_bitpos = 7; + } + if (++row_bit_pos >= 32) { + cp++; + row_bit_pos = 0; + } + if (word & (1 << PK_bitpos)) + *cp |= 1 << row_bit_pos; + } + } + } else { /* get packed raster */ + rows_left = characterBitmaps[ch]->h; + h_bit = characterBitmaps[ch]->w; + PK_repeat_count = 0; + word_weight = 32; + word = 0; + while (rows_left > 0) { + count = PK_packed_num(fp); + while (count > 0) { + if (count < word_weight && count < h_bit) { + if (paint_switch) + word |= bit_masks[count] << (32 - word_weight); + h_bit -= count; + word_weight -= count; + count = 0; + } else + if (count >= h_bit && h_bit <= word_weight) { + if (paint_switch) + word |= bit_masks[h_bit] << (32 - word_weight); + *cp++ = word; + /* "output" row(s) */ + for (i = PK_repeat_count * bytes_wide / 4; i > 0; --i) { + *cp = *SUB(cp, bytes_wide); + ++cp; + } + rows_left -= PK_repeat_count + 1; + PK_repeat_count = 0; + word = 0; + word_weight = 32; + count -= h_bit; + h_bit = characterBitmaps[ch]->w; + } else { + if (paint_switch) + word |= bit_masks[word_weight] << (32 - word_weight); + *cp++ = word; + word = 0; + count -= word_weight; + h_bit -= word_weight; + word_weight = 32; + } + } + paint_switch = 1 - paint_switch; + } + if (cp != ((Q_UINT32 *) (characterBitmaps[ch]->bits + bytes_wide * characterBitmaps[ch]->h))) + oops(i18n("Wrong number of bits stored: char. %1, font %2").arg(ch).arg(parent->filename)); + if (rows_left != 0 || h_bit != characterBitmaps[ch]->w) + oops(i18n("Bad pk file (%1), too many bits").arg(parent->filename)); + } + } // endif: big or small Endian? +} + + +void TeXFont_PK::read_PK_index() +{ +#ifdef DEBUG_PK + kdDebug(4300) << "TeXFont_PK::read_PK_index() called" << endl; +#endif + + if (file == 0) { + kdError(4300) << "TeXFont_PK::read_PK_index(): file == 0" << endl; + return; + } + + int magic = two(file); + if (magic != PK_MAGIC) { + kdError(4300) << "TeXFont_PK::read_PK_index(): file is not a PK file" << endl; + return; + } + + fseek(file, (long) one(file), SEEK_CUR); /* skip comment */ + (void) four(file); /* skip design size */ + + checksum = four(file); + + int hppp = sfour(file); + int vppp = sfour(file); + if (hppp != vppp) + kdWarning(4300) << i18n("Font has non-square aspect ratio ") << vppp << ":" << hppp << endl; + + // Read glyph directory (really a whole pass over the file). + for (;;) { + int bytes_left, flag_low_bits; + unsigned int ch; + + PK_skip_specials(); + if (PK_flag_byte == PK_POST) + break; + flag_low_bits = PK_flag_byte & 0x7; + if (flag_low_bits == 7) { + bytes_left = four(file); + ch = four(file); + } else + if (flag_low_bits > 3) { + bytes_left = ((flag_low_bits - 4) << 16) + two(file); + ch = one(file); + } else { + bytes_left = (flag_low_bits << 8) + one(file); + ch = one(file); + } + + glyphtable[ch].addr = ftell(file); + glyphtable[ch].x2 = PK_flag_byte; + fseek(file, (long) bytes_left, SEEK_CUR); +#ifdef DEBUG_PK + kdDebug(4300) << "Scanning pk char " << ch << "at " << glyphtable[ch].addr << endl; +#endif + } +#ifdef DEBUG_PK + kdDebug(4300) << "TeXFont_PK::read_PK_index() called" << endl; +#endif +} diff --git a/kdvi/TeXFont_PK.h b/kdvi/TeXFont_PK.h new file mode 100644 index 00000000..b555934e --- /dev/null +++ b/kdvi/TeXFont_PK.h @@ -0,0 +1,38 @@ +// -*- C++ -*- + +#ifndef _TEXFONT_PK_H +#define _TEXFONT_PK_H + +#include "TeXFont.h" + +class glyph; + +class TeXFont_PK : public TeXFont { + public: + TeXFont_PK(TeXFontDefinition *parent); + ~TeXFont_PK(); + + glyph* getGlyph(Q_UINT16 character, bool generateCharacterPixmap=false, const QColor& color=Qt::black); + + private: + FILE *file; // open font file or NULL + class bitmap *characterBitmaps[TeXFontDefinition::max_num_of_chars_in_font]; + + // For use by PK-decryption routines. I don't understand what these + // are good for -- Stefan Kebekus + int PK_flag_byte; + unsigned PK_input_byte; + int PK_bitpos; + int PK_dyn_f; + int PK_repeat_count; + + // PK-internal routines which were taken from xdvi. Again, I do not + // really know what they are good for -- Stefan Kebekus + inline void read_PK_char(unsigned int ch); + inline int PK_get_nyb(FILE *fp); + inline int PK_packed_num(FILE *fp); + inline void read_PK_index(); + inline void PK_skip_specials(); +}; + +#endif diff --git a/kdvi/TeXFont_TFM.cpp b/kdvi/TeXFont_TFM.cpp new file mode 100644 index 00000000..54edd2fc --- /dev/null +++ b/kdvi/TeXFont_TFM.cpp @@ -0,0 +1,163 @@ +// TeXFont_TFM.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +// Add header files alphabetically + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <qdatastream.h> +#include <qfile.h> + +#include "glyph.h" +#include "TeXFont_TFM.h" +#include "TeXFontDefinition.h" + +//#define DEBUG_TFM + + +TeXFont_TFM::TeXFont_TFM(TeXFontDefinition *parent) + : TeXFont(parent) +{ +#ifdef DEBUG_TFM + kdDebug(4300) << "TeXFont_TFM::TeXFont_TFM( parent=" << parent << " )" << endl; +#endif + + QFile file( parent->filename ); + if ( !file.open( IO_ReadOnly ) ) { + kdError(4300) << "TeXFont_TFM::TeXFont_TFM(): Could not read TFM file" << endl; + return; + } + QDataStream stream( &file ); + + // Data from the very beginning of the TFM file, as specified in + // "The DVI Driver Standard, Level 0", section D.2.1 + Q_UINT16 lf, lh, bc, ec, nw, nh, nd; + stream >> lf >> lh >> bc >> ec >> nw >> nh >> nd; +#ifdef DEBUG_TFM + kdDebug(4300) << "lf= " << lf << endl + << "lh= " << lh << endl + << "bc= " << bc << endl + << "ec= " << ec << endl + << "nw= " << nw << endl + << "nh= " << nh << endl + << "nd= " << nd << endl; +#endif + if ((bc > ec) || (ec >= TeXFontDefinition::max_num_of_chars_in_font)) { + kdError(4300) << "TeXFont_TFM::TeXFont_TFM( filename=" << parent->filename << " ): The font has an invalid bc and ec entries." << endl; + file.close(); + return; + } + + // Data from the HEADER section of the TFM data. + file.at(24); + stream >> checksum >> design_size_in_TeX_points.value; +#ifdef DEBUG_TFM + kdDebug(4300) << "checksum = " << checksum << endl + << "design_size = " << design_size_in_TeX_points.toDouble() << " TeX Points" << endl + << " = " << design_size_in_TeX_points.toDouble()*254.0/7227.0 << " cm" << endl; +#endif + + // Width table + fix_word widthTable_in_units_of_design_size[TeXFontDefinition::max_num_of_chars_in_font]; + for(unsigned int i=0; i<TeXFontDefinition::max_num_of_chars_in_font; i++) + widthTable_in_units_of_design_size[i].value = 0; + + file.at( 24 + 4*lh + 4*(ec-bc) ); + for(unsigned int i=0; i<nw; i++) { + + stream >> widthTable_in_units_of_design_size[i].value; + // Some characters, which are used as parts of glyphs, have width + // 0 --the real width is caculated in a lig_kern program and + // depends on the preceding character. We cannot calculate the + // real width here and take 0.4 times the design size as an + // approximation. + if (widthTable_in_units_of_design_size[i].value == 0) + widthTable_in_units_of_design_size[i].fromDouble(0.4); + } + + // Height table + fix_word heightTable_in_units_of_design_size[16]; + for(unsigned int i=0; i<16; i++) + heightTable_in_units_of_design_size[i].value = 0; + for(unsigned int i=0; i<nh; i++) { + stream >> heightTable_in_units_of_design_size[i].value; + } + + // Char-Info table + file.at( 24 + 4*lh ); + for(unsigned int characterCode=bc; characterCode<ec; characterCode++) { + glyph *g = glyphtable+characterCode; + + Q_UINT8 byte; + stream >> byte; + if (byte >= nw) + kdError(4300) << "TeXFont_TFM::TeXFont_TFM( filename=" << parent->filename << " ): The font has an invalid Char-Info table." << endl; + else { + characterWidth_in_units_of_design_size[characterCode] = widthTable_in_units_of_design_size[byte]; + g->dvi_advance_in_units_of_design_size_by_2e20 = widthTable_in_units_of_design_size[byte].value; + } + + stream >> byte; + byte = byte >> 4; + if (byte >= nh) + kdError(4300) << "TeXFont_TFM::TeXFont_TFM( filename=" << parent->filename << " ): The font has an invalid Char-Info table." << endl; + else + characterHeight_in_units_of_design_size[characterCode] = heightTable_in_units_of_design_size[byte]; + + stream >> byte; + stream >> byte; + } + file.close(); +} + + +TeXFont_TFM::~TeXFont_TFM() +{ +} + + +glyph *TeXFont_TFM::getGlyph(Q_UINT16 characterCode, bool generateCharacterPixmap, const QColor& color) +{ +#ifdef DEBUG_TFM + kdDebug(4300) << "TeXFont_TFM::getGlyph( ch=" << ch << ", generateCharacterPixmap=" << generateCharacterPixmap << " )" << endl; +#endif + + // Paranoia checks + if (characterCode >= TeXFontDefinition::max_num_of_chars_in_font) { + kdError(4300) << "TeXFont_TFM::getGlyph(): Argument is too big." << endl; + return glyphtable; + } + + // This is the address of the glyph that will be returned. + struct glyph *g = glyphtable+characterCode; + + if ((generateCharacterPixmap == true) && ((g->shrunkenCharacter.isNull()) || (color != g->color)) ) { + g->color = color; + Q_UINT16 pixelWidth = (Q_UINT16)(parent->displayResolution_in_dpi * + design_size_in_TeX_points.toDouble() * + characterWidth_in_units_of_design_size[characterCode].toDouble() * 100.0/7227.0 + 0.5); + Q_UINT16 pixelHeight = (Q_UINT16)(parent->displayResolution_in_dpi * + design_size_in_TeX_points.toDouble() * + characterHeight_in_units_of_design_size[characterCode].toDouble() * 100.0/7227.0 + 0.5); + + // Just make sure that weired TFM files never lead to giant + // pixmaps that eat all system memory... + if (pixelWidth > 50) + pixelWidth = 50; + if (pixelHeight > 50) + pixelHeight = 50; + + g->shrunkenCharacter.resize( pixelWidth, pixelHeight ); + g->shrunkenCharacter.fill(color); + g->x2 = 0; + g->y2 = pixelHeight; + } + + return g; +} diff --git a/kdvi/TeXFont_TFM.h b/kdvi/TeXFont_TFM.h new file mode 100644 index 00000000..e68ba872 --- /dev/null +++ b/kdvi/TeXFont_TFM.h @@ -0,0 +1,38 @@ +// -*- C++ -*- +// TeXFont_TFM.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#ifndef _TEXFONT_TFM_H +#define _TEXFONT_TFM_H + +#include "TeXFont.h" + + +class fix_word { + public: + void fromINT32(Q_INT32 val) {value = val;} + void fromDouble(double val) {value = (Q_INT32)(val * (1<<20) + 0.5);} + double toDouble() {return (double(value)) / (double(1<<20));} + + Q_INT32 value; +}; + +class TeXFont_TFM : public TeXFont { + public: + TeXFont_TFM(TeXFontDefinition *parent); + ~TeXFont_TFM(); + + glyph* getGlyph(Q_UINT16 character, bool generateCharacterPixmap=false, const QColor& color=Qt::black); + + private: + fix_word characterWidth_in_units_of_design_size[256]; + fix_word characterHeight_in_units_of_design_size[256]; + + fix_word design_size_in_TeX_points; +}; + +#endif diff --git a/kdvi/bigEndianByteReader.cpp b/kdvi/bigEndianByteReader.cpp new file mode 100644 index 00000000..63d11f83 --- /dev/null +++ b/kdvi/bigEndianByteReader.cpp @@ -0,0 +1,111 @@ +// bigEndianByteReader.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> + +#include <kdebug.h> + +#include "bigEndianByteReader.h" +#include "dvi.h" + +//#define DEBUG_ENDIANREADER + +Q_UINT8 bigEndianByteReader::readUINT8() +{ + // This check saveguards us against segmentation fault. It is also + // necessary for virtual fonts, which do not end whith EOP. + if (command_pointer >= end_pointer) { +#ifdef DEBUG_ENDIANREADER + kdError(4300) << "bigEndianByteReader::readUINT8() tried to read past end of data chunk" << endl; + kdError(4300) << "end_pointer = " << end_pointer << endl; + kdError(4300) << "command_pointer = " << command_pointer << endl; +#endif + return EOP; + } + + return *(command_pointer++); +} + +Q_UINT16 bigEndianByteReader::readUINT16() +{ + // This check saveguards us against segmentation fault. It is also + // necessary for virtual fonts, which do not end whith EOP. + if (command_pointer >= end_pointer) + return EOP; + + Q_UINT16 a; + a = *(command_pointer++); + a = (a << 8) | *(command_pointer++); + return a; +} + +Q_UINT32 bigEndianByteReader::readUINT32() +{ + // This check saveguards us against segmentation fault. It is also + // necessary for virtual fonts, which do not end whith EOP. + if (command_pointer >= end_pointer) + return EOP; + + Q_UINT32 a; + a = *(command_pointer++); + a = (a << 8) | *(command_pointer++); + a = (a << 8) | *(command_pointer++); + a = (a << 8) | *(command_pointer++); + return a; +} + +void bigEndianByteReader::writeUINT32(Q_UINT32 a) +{ + // This check saveguards us against segmentation fault. It is also + // necessary for virtual fonts, which do not end whith EOP. + if (command_pointer >= end_pointer) + return; + + command_pointer[3] = (Q_UINT8)(a & 0xFF); + a = a >> 8; + command_pointer[2] = (Q_UINT8)(a & 0xFF); + a = a >> 8; + command_pointer[1] = (Q_UINT8)(a & 0xFF); + a = a >> 8; + command_pointer[0] = (Q_UINT8)(a & 0xFF); + + command_pointer += 4; + return; +} + +Q_UINT32 bigEndianByteReader::readUINT(Q_UINT8 size) +{ + // This check saveguards us against segmentation fault. It is also + // necessary for virtual fonts, which do not end whith EOP. + if (command_pointer >= end_pointer) + return EOP; + + Q_UINT32 a = 0; + while (size > 0) { + a = (a << 8) + *(command_pointer++); + size--; + } + return a; +} + +Q_INT32 bigEndianByteReader::readINT(Q_UINT8 length) +{ + // This check saveguards us against segmentation fault. It is also + // necessary for virtual fonts, which do not end whith EOP. + if (command_pointer >= end_pointer) + return EOP; + + Q_INT32 a = *(command_pointer++); + + if (a & 0x80) + a -= 0x100; + + while ((--length) > 0) + a = (a << 8) | *(command_pointer++); + + return a; +} diff --git a/kdvi/bigEndianByteReader.h b/kdvi/bigEndianByteReader.h new file mode 100644 index 00000000..674ca1c1 --- /dev/null +++ b/kdvi/bigEndianByteReader.h @@ -0,0 +1,62 @@ +// -*- C++ -*- +/* This file is part of KDVI (C) 2001 by Stefan Kebekus (kebekus@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. +*/ + +/** + * Byte reading routines which read big endian numbers from memory and + * convert them to native integers. + * + * @author Stefan Kebekus (kebekus@kde.org) + * + **/ + +#ifndef _bigEndianByteReader_H +#define _bigEndianByteReader_H + +#include <qglobal.h> + +class bigEndianByteReader { + public: + /** Set this pointer to the location where the number resides which + you want to read. */ + Q_UINT8 * command_pointer; + + /** This pointer marks the end of the memory area where bytes can be + read. It should point to the first byte which CANNOT be + read. The idea is to have a safety net which protects us against + SEGFAULTs. This is also used in virtual fonts, where the macro + does not have an EOP command at the end of the macro. */ + Q_UINT8 * end_pointer; + + /** If command_pointer >= end_pointer, this method return EOP (=140) + and exists. Otherwise, the method returns the unsigned byte + and increases the command_pointer by one. */ + Q_UINT8 readUINT8(); + + /** Similar to the method above, only that the method reads a big + endian 2-byte word and increases the pointer by two. */ + Q_UINT16 readUINT16(); + + /** Similar to the method above, only that the method reads a big + endian 4-byte word and increases the pointer by four. */ + Q_UINT32 readUINT32(); + + void writeUINT32(Q_UINT32 a); + + /** Similar to the method above, only that the method reads a big + endian number of length size, where 1 <= size <= 4. Note that + the value 3 is allowed (and is acually used in DVI files)!!! */ + Q_UINT32 readUINT(Q_UINT8 size); + + /** Similar to the method above, only that the method reads a SIGNED + number */ + Q_INT32 readINT(Q_UINT8); + +}; + +#endif //ifndef _bigEndianByteReader_H diff --git a/kdvi/configure.in.in b/kdvi/configure.in.in new file mode 100644 index 00000000..ba3ee342 --- /dev/null +++ b/kdvi/configure.in.in @@ -0,0 +1,55 @@ + +dnl the following is just to fool the toplevel configure.in +LTLIBOBJS= +AC_SUBST(LTLIBOBJS) + +compile_kdvi=yes +for j in $DO_NOT_COMPILE; do + if test "kdvi" = $j; then + compile_kdvi=no + fi +done + +dnl AC_CONFIG_SUBDIRS has to be done before KDE_CREATE_SUBDIRSLIST +if test "$compile_kdvi" = "yes"; then + + KDE_FIND_PATH(kpsewhich, KPSEWHICH, [/usr/bin /bin /usr/sbin /opt/teTeX/bin /opt/local/bin /opt/bin /usr/local/bin], [ ]) + + have_kpsewhich=no + test_kpsewhich="`${KPSEWHICH-kpsewhich} -show-path cnf 2>/dev/null`" + test -n "${test_kpsewhich}" && have_kpsewhich=yes + +fi + +AC_CHECK_HEADERS(sys/types.h sys/params.h limits.h) + +# Check for freetype2 +KDE_FIND_PATH(freetype-config, FREETYPE_CONFIG, [${prefix}/bin ${exec_prefix}/bin /usr/local/bin /opt/local/bin],) + +if test -n "$FREETYPE_CONFIG"; then + vers=`$FREETYPE_CONFIG --version 2>/dev/null | awk 'BEGIN { FS = "."; } { printf "%d", ($1 * 1000 + $2) * 1000 + $3;}'` + if test -n "$vers" && test "$vers" -ge 8000002 + then + LIBFREETYPE_LIBS="`$FREETYPE_CONFIG --libs`" + FREETYPE_RPATH= + for args in $LIBFREETYPE_LIBS; do + case $args in + -L*) + FREETYPE_RPATH="$FREETYPE_RPATH $args" + ;; + esac + done + FREETYPE_RPATH=`echo $FREETYPE_RPATH | sed -e "s/-L/-R/g"` + LIBFREETYPE_CFLAGS="`$FREETYPE_CONFIG --cflags`" + + AC_DEFINE_UNQUOTED(HAVE_FREETYPE, 1, [Defines if your system has the freetype library]) + fi +fi + +AC_SUBST(LIBFREETYPE_LIBS) +AC_SUBST(LIBFREETYPE_CFLAGS) +AC_SUBST(FREETYPE_RPATH) + +if test -z "$LIBFREETYPE_LIBS"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE kdvi" +fi diff --git a/kdvi/dvi.h b/kdvi/dvi.h new file mode 100644 index 00000000..62178f49 --- /dev/null +++ b/kdvi/dvi.h @@ -0,0 +1,68 @@ +// -*- C++ -*- +/* + * Mnemonics for bytes in dvi file. + */ + +#ifndef DVI_H +#define DVI_H + +#define SETCHAR0 0 +#define SET1 128 +#define SETRULE 132 +#define PUT1 133 +#define PUTRULE 137 +#define NOP 138 +#define BOP 139 +#define EOP 140 +#define PUSH 141 +#define POP 142 +#define RIGHT1 143 +#define RIGHT2 144 +#define RIGHT3 145 +#define RIGHT4 146 +#define W0 147 +#define W1 148 +#define W2 149 +#define W3 150 +#define W4 151 +#define X0 152 +#define X1 153 +#define X2 154 +#define X3 155 +#define X4 156 +#define DOWN1 157 +#define DOWN2 158 +#define DOWN3 159 +#define DOWN4 160 +#define Y0 161 +#define Y1 162 +#define Y2 163 +#define Y3 164 +#define Y4 165 +#define Z0 166 +#define Z1 167 +#define Z2 168 +#define Z3 169 +#define Z4 170 +#define FNTNUM0 171 +#define FNT1 235 +#define FNT2 236 +#define FNT3 237 +#define FNT4 238 +#define XXX1 239 +#define XXX2 240 +#define XXX3 241 +#define XXX4 242 +#define FNTDEF1 243 +#define FNTDEF2 244 +#define FNTDEF3 245 +#define FNTDEF4 246 +#define PRE 247 +#define POST 248 +#define POSTPOST 249 +#define SREFL 250 +#define EREFL 251 + +#define TRAILER 223 /* Trailing bytes at end of file */ + +#endif diff --git a/kdvi/dviFile.cpp b/kdvi/dviFile.cpp new file mode 100644 index 00000000..521fb39f --- /dev/null +++ b/kdvi/dviFile.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (c) 1994 Paul Vojta. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * NOTE: + * xdvi is based on prior work as noted in the modification history, below. + */ + +/* + * DVI previewer for X. + * + * Eric Cooper, CMU, September 1985. + * + * Code derived from dvi-imagen.c. + * + * Modification history: + * 1/1986 Modified for X.10 --Bob Scheifler, MIT LCS. + * 7/1988 Modified for X.11 --Mark Eichin, MIT + * 12/1988 Added 'R' option, toolkit, magnifying glass + * --Paul Vojta, UC Berkeley. + * 2/1989 Added tpic support --Jeffrey Lee, U of Toronto + * 4/1989 Modified for System V --Donald Richardson, Clarkson Univ. + * 3/1990 Added VMS support --Scott Allendorf, U of Iowa + * 7/1990 Added reflection mode --Michael Pak, Hebrew U of Jerusalem + * 1/1992 Added greyscale code --Till Brychcy, Techn. Univ. Muenchen + * and Lee Hetherington, MIT + * 4/1994 Added DPS support, bounding box + * --Ricardo Telichevesky + * and Luis Miguel Silveira, MIT RLE. + */ + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <qdir.h> +#include <qfileinfo.h> +#include <stdlib.h> +#include <kprocio.h> + +extern "C" { +#include "dvi.h" +} + +#include "../kviewshell/pageSize.h" +#include "dviFile.h" +#include "fontpool.h" +#include "xdvi.h" + + +dvifile::dvifile(const dvifile *old, fontPool *fp) +{ + errorMsg = QString::null; + errorCounter = 0; + page_offset = 0; + suggestedPageSize = 0; + numberOfExternalPSFiles = 0; + numberOfExternalNONPSFiles = 0; + sourceSpecialMarker = old->sourceSpecialMarker; + + dviData = old->dviData.copy(); + + filename = old->filename; + size_of_file = old->size_of_file; + end_pointer = dvi_Data()+size_of_file; + if (dvi_Data() == 0) { + kdError(4300) << "Not enough memory to copy the DVI-file." << endl; + return; + } + + font_pool = fp; + filename = old->filename; + generatorString = old->generatorString; + total_pages = old->total_pages; + + + tn_table.clear(); + process_preamble(); + find_postamble(); + read_postamble(); + prepare_pages(); +} + + +void dvifile::process_preamble() +{ + command_pointer = dvi_Data(); + + Q_UINT8 magic_number = readUINT8(); + if (magic_number != PRE) { + errorMsg = i18n("The DVI file does not start with the preamble."); + return; + } + magic_number = readUINT8(); + if (magic_number != 2) { + errorMsg = i18n("The DVI file contains the wrong version of DVI output for this program. " + "Hint: If you use the typesetting system Omega, you have to use a special " + "program, such as oxdvi."); + return; + } + + /** numerator, denominator and the magnification value that describe + how many centimeters there are in one TeX unit, as explained in + section A.3 of the DVI driver standard, Level 0, published by + the TUG DVI driver standards committee. */ + Q_UINT32 numerator = readUINT32(); + Q_UINT32 denominator = readUINT32(); + _magnification = readUINT32(); + + cmPerDVIunit = (double(numerator) / double(denominator)) * (double(_magnification) / 1000.0) * (1.0 / 1e5); + + + // Read the generatorString (such as "TeX output ..." from the + // DVI-File). The variable "magic_number" holds the length of the + // string. + char job_id[300]; + magic_number = readUINT8(); + strncpy(job_id, (char *)command_pointer, magic_number); + job_id[magic_number] = '\0'; + generatorString = job_id; +} + + +/** find_postamble locates the beginning of the postamble and leaves + the file ready to start reading at that location. */ + +void dvifile::find_postamble() +{ + // Move backwards through the TRAILER bytes + command_pointer = dvi_Data() + size_of_file - 1; + while((*command_pointer == TRAILER) && (command_pointer > dvi_Data())) + command_pointer--; + if (command_pointer == dvi_Data()) { + errorMsg = i18n("The DVI file is badly corrupted. KDVI was not able to find the postamble."); + return; + } + + // And this is finally the pointer to the beginning of the postamble + command_pointer -= 4; + beginning_of_postamble = readUINT32(); + command_pointer = dvi_Data() + beginning_of_postamble; +} + + +void dvifile::read_postamble() +{ + Q_UINT8 magic_byte = readUINT8(); + if (magic_byte != POST) { + errorMsg = i18n("The postamble does not begin with the POST command."); + return; + } + last_page_offset = readUINT32(); + + // Skip the numerator, denominator and magnification, the largest + // box height and width and the maximal depth of the stack. These + // are not used at the moment. + command_pointer += 4 + 4 + 4 + 4 + 4 + 2; + + // The number of pages is more interesting for us. + total_pages = readUINT16(); + + // As a next step, read the font definitions. + Q_UINT8 cmnd = readUINT8(); + while (cmnd >= FNTDEF1 && cmnd <= FNTDEF4) { + Q_UINT32 TeXnumber = readUINT(cmnd-FNTDEF1+1); + Q_UINT32 checksum = readUINT32(); // Checksum of the font, as found by TeX in the TFM file + + // Read scale and design factor, and the name of the font. All + // these are explained in section A.4 of the DVI driver standard, + // Level 0, published by the TUG DVI driver standards committee + Q_UINT32 scale = readUINT32(); + Q_UINT32 design = readUINT32(); + Q_UINT16 len = readUINT8() + readUINT8(); // Length of the font name, including the directory name + char *fontname = new char[len + 1]; + strncpy(fontname, (char *)command_pointer, len ); + fontname[len] = '\0'; + command_pointer += len; + +#ifdef DEBUG_FONTS + kdDebug(4300) << "Postamble: define font \"" << fontname << "\" scale=" << scale << " design=" << design << endl; +#endif + + // According to section A.4 of the DVI driver standard, this font + // shall be enlarged by the following factor before it is used. + double enlargement_factor = (double(scale) * double(_magnification))/(double(design) * 1000.0); + + if (font_pool != 0) { + TeXFontDefinition *fontp = font_pool->appendx(fontname, checksum, scale, enlargement_factor); + + // Insert font in dictionary and make sure the dictionary is big + // enough. + if (tn_table.size()-2 <= tn_table.count()) + // Not quite optimal. The size of the dictionary should be a + // prime for optimal performance. I don't care. + tn_table.resize(tn_table.size()*2); + tn_table.insert(TeXnumber, fontp); + } + + // Read the next command + cmnd = readUINT8(); + } + + if (cmnd != POSTPOST) { + errorMsg = i18n("The postamble contained a command other than FNTDEF."); + return; + } + + // Now we remove all those fonts from the memory which are no longer + // in use. + if (font_pool != 0) + font_pool->release_fonts(); +} + + +void dvifile::prepare_pages() +{ +#ifdef DEBUG_DVIFILE + kdDebug(4300) << "prepare_pages" << endl; +#endif + + if (page_offset.resize(total_pages+1) == false) { + kdError(4300) << "No memory for page list!" << endl; + return; + } + for(int i=0; i<=total_pages; i++) + page_offset[i] = 0; + + + page_offset[total_pages] = beginning_of_postamble; + Q_UINT16 i = total_pages-1; + page_offset[i] = last_page_offset; + + // Follow back pointers through pages in the DVI file, storing the + // offsets in the page_offset table. + while (i > 0) { + command_pointer = dvi_Data() + page_offset[i--]; + if (readUINT8() != BOP) { + errorMsg = i18n("The page %1 does not start with the BOP command.").arg(i+1); + return; + } + command_pointer += 10 * 4; + page_offset[i] = readUINT32(); + if ((dvi_Data()+page_offset[i] < dvi_Data())||(dvi_Data()+page_offset[i] > dvi_Data()+size_of_file)) + break; + } +} + + +dvifile::dvifile(const QString& fname, fontPool* pool) +{ +#ifdef DEBUG_DVIFILE + kdDebug(4300) << "init_dvi_file: " << fname << endl; +#endif + + errorMsg = QString::null; + errorCounter = 0; + page_offset = 0; + suggestedPageSize = 0; + numberOfExternalPSFiles = 0; + numberOfExternalNONPSFiles = 0; + font_pool = pool; + sourceSpecialMarker = true; + + QFile file(fname); + filename = file.name(); + file.open( IO_ReadOnly ); + size_of_file = file.size(); + dviData.resize(size_of_file); + // Sets the end pointer for the bigEndianByteReader so that the + // whole memory buffer is readable + end_pointer = dvi_Data()+size_of_file; + if (dvi_Data() == 0) { + kdError() << i18n("Not enough memory to load the DVI-file."); + return; + } + file.readBlock((char *)dvi_Data(), size_of_file); + file.close(); + if (file.status() != IO_Ok) { + kdError() << i18n("Could not load the DVI-file."); + return; + } + + tn_table.clear(); + + process_preamble(); + find_postamble(); + read_postamble(); + prepare_pages(); + + return; +} + + +dvifile::~dvifile() +{ +#ifdef DEBUG_DVIFILE + kdDebug(4300) << "destroy dvi-file" << endl; +#endif + + // Delete converted PDF files + QMap<QString, QString>::Iterator it; + for ( it = convertedFiles.begin(); it != convertedFiles.end(); ++it ) + QFile::remove(it.data()); + + if (suggestedPageSize != 0) + delete suggestedPageSize; + if (font_pool != 0) + font_pool->mark_fonts_as_unused(); +} + + +void dvifile::renumber() +{ + dviData.detach(); + + // Write the page number to the file, taking good care of byte + // orderings. + int wordSize; + bool bigEndian; + qSysInfo (&wordSize, &bigEndian); + + for(Q_UINT32 i=1; i<=total_pages; i++) { + Q_UINT8 *ptr = dviData.data() + page_offset[i-1]+1; + Q_UINT8 *num = (Q_UINT8 *)&i; + for(Q_UINT8 j=0; j<4; j++) + if (bigEndian) { + *(ptr++) = num[0]; + *(ptr++) = num[1]; + *(ptr++) = num[2]; + *(ptr++) = num[3]; + } else { + *(ptr++) = num[3]; + *(ptr++) = num[2]; + *(ptr++) = num[1]; + *(ptr++) = num[0]; + } + } +} + + +QString dvifile::convertPDFtoPS(const QString &PDFFilename) +{ + // Check if the PDFFile is known + QMap<QString, QString>::Iterator it = convertedFiles.find(PDFFilename); + if (it != convertedFiles.end()) { + // PDF-File is known. Good. + return it.data(); + } + + // Get the name of a temporary file + KTempFile tmpfile(QString::null, ".ps"); + QString convertedFileName = tmpfile.name(); + tmpfile.close(); + tmpfile.unlink(); + + // Use pdf2ps to do the conversion + KProcIO proc; + proc << "pdf2ps" << PDFFilename << convertedFileName; + if (proc.start(KProcess::Block) == false) + convertedFileName = QString::null; // Indicates that conversion failed, won't try again. + if (!QFile::exists(convertedFileName)) + convertedFileName = QString::null; // Indicates that conversion failed, won't try again. + + // Save name of converted file to buffer, so PDF file won't be + // converted again, and files can be deleted when *this is + // deconstructed. + convertedFiles[PDFFilename] = convertedFileName; + + return convertedFileName; +} + + +bool dvifile::saveAs(const QString &filename) +{ + if (dvi_Data() == 0) + return false; + + QFile out(filename); + if (out.open( IO_Raw|IO_WriteOnly ) == false) + return false; + if (out.writeBlock ( (char *)(dvi_Data()), size_of_file ) == -1) + return false; + out.close(); + return true; +} diff --git a/kdvi/dviFile.h b/kdvi/dviFile.h new file mode 100644 index 00000000..069ae6d9 --- /dev/null +++ b/kdvi/dviFile.h @@ -0,0 +1,150 @@ +// -*- C++ -*- +// +// Class: dviFile +// +// Class that represents a DVI file. Part of KDVI - A DVI previewing +// plugin for kviewshell. +// +// (C) 2004--2005 Stefan Kebekus. Distributed under the GPL. +// + +#ifndef _DVIFILE_H +#define _DVIFILE_H + +#include "bigEndianByteReader.h" + +#include <qintdict.h> +#include <qiodevice.h> +#include <qmemarray.h> +#include <qstring.h> + +class fontPool; +class pageSize; +class TeXFontDefinition; + + +class dvifile : public bigEndianByteReader +{ + public: + /** Makes a deep copy of the old DVI file. */ + dvifile(const dvifile *old, fontPool *fp ); + dvifile(const QString& fname, class fontPool* pool); + + ~dvifile(); + + fontPool * font_pool; + QString filename; + QString generatorString; + Q_UINT16 total_pages; + QMemArray<Q_UINT32> page_offset; + + /** Saves the DVI file. Returns true on success. */ + bool saveAs(const QString &filename); + + // Returns a pointer to the DVI file's data, or 0 if no data has yet + // been allocated. + Q_UINT8 * dvi_Data() {return dviData.data();} + + QIODevice::Offset size_of_file; + QString errorMsg; + + /** This field is set to zero when the DVI file is constructed, and + will be modified during the prescan phase (at this time the + prescan code still resides in the dviRenderer class) */ + Q_UINT16 numberOfExternalPSFiles; + + /** This field is set to zero when the DVI file is constructed, and + will be modified during the prescan phase (at this time the + prescan code still resides in the dviRenderer class) */ + Q_UINT16 numberOfExternalNONPSFiles; + + Q_UINT32 beginning_of_postamble; + + /** This flag is set to "true" during the construction of the + dvifile, and is never changed afterwards by the dvifile + class. It is used in kdvi in conjuction with source-specials: + the first time a page with source specials is rendered, KDVI + shows an info dialog, and the flag is set to false. That way + KDVI ensures that the user is only informed once. */ + bool sourceSpecialMarker; + + QIntDict<TeXFontDefinition> tn_table; + + /** Returns the number of centimeters per DVI unit in this DVI + file. */ + double getCmPerDVIunit() const {return cmPerDVIunit;} + + /** Returns the magnification of the DVI file, as described in the + DVI Standard. */ + Q_UINT32 getMagnification() const {return _magnification;} + + /** This member is set to zero on construction and can be used by + other software to count error messages that were printed when + the DVI-file was processed. Suggested application: limit the + number of error messages to, say, 25. */ + Q_UINT8 errorCounter; + + /** Papersize information read from the dvi-File */ + pageSize *suggestedPageSize; + + /** Sets new DVI data; all old data is erased. EXPERIMENTAL, use + with care. */ + void setNewData(const QMemArray<Q_UINT8>& newData) {dviData = newData;} + + /** Page numbers that appear in a DVI document need not be + ordered. Worse, page numbers need not be unique. This method + renumbers the pages. */ + void renumber(); + + /** PDF to PS file conversion + + This utility method takes the name of a PDF-file, and attempts to + convert it to a PS file. The dvifile internally keeps a list of + converted files, to do two thigs: + + - convert files only once. + + - delete all converted files on destruction + + @warning The internal buffer can lead to difficulties if filenames + of PDF-files are not unique: if the content of a PDF file is + changed and this method is called a second time with the same file + name, the method will then NOT convert the file, but simply return + the name from the buffer + + @returns The name of the PS file, or QString::null on failure. + */ + QString convertPDFtoPS(const QString &PDFFilename); + + private: + /** process_preamble reads the information in the preamble and + stores it into global variables for later use. */ + void process_preamble(); + void find_postamble(); + /** read_postamble reads the information in the postamble, storing + it into global variables. It also takes care of reading in all + of the pixel files for the fonts used in the job. */ + void read_postamble(); + void prepare_pages(); + + + /** Offset in DVI file of last page, set in read_postamble(). */ + Q_UINT32 last_page_offset; + Q_UINT32 _magnification; + + double cmPerDVIunit; + + QMemArray<Q_UINT8> dviData; + + + /** Map of filenames for converted PDF files + + This map contains names of PDF files that were converted to + PostScript. The key is the name of the PDF file, the data the name + of the associated PS file, or QString::null, if the file could not + be converted. The PS files are deleted when the DVI-file is + destructed. */ + QMap<QString, QString> convertedFiles; +}; + +#endif //ifndef _DVIFILE_H diff --git a/kdvi/dviPageCache.cpp b/kdvi/dviPageCache.cpp new file mode 100644 index 00000000..2a2b8a30 --- /dev/null +++ b/kdvi/dviPageCache.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * 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 <config.h> + +#include <kdebug.h> + +#include "dviPageCache.h" +#include "renderedDviPagePixmap.h" + +DVIPageCache::DVIPageCache() + : DocumentPageCache() +{ +} + +DVIPageCache::~DVIPageCache() +{ +} + +RenderedDocumentPagePixmap* DVIPageCache::createDocumentPagePixmap() const +{ + return new RenderedDviPagePixmap(); +} + +#include "dviPageCache.moc" diff --git a/kdvi/dviPageCache.h b/kdvi/dviPageCache.h new file mode 100644 index 00000000..a28052c5 --- /dev/null +++ b/kdvi/dviPageCache.h @@ -0,0 +1,40 @@ +// -*- C++ -*- +/*************************************************************************** + * 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 _DVIPAGECACHE_H_ +#define _DVIPAGECACHE_H_ + +#include "documentPageCache.h" + +class DVIPageCache : public DocumentPageCache +{ + Q_OBJECT + + public: + DVIPageCache(); + + virtual ~DVIPageCache(); + + private: + virtual RenderedDocumentPagePixmap* createDocumentPagePixmap() const; +}; + +#endif diff --git a/kdvi/dviRenderer.cpp b/kdvi/dviRenderer.cpp new file mode 100644 index 00000000..b9bf6256 --- /dev/null +++ b/kdvi/dviRenderer.cpp @@ -0,0 +1,839 @@ +// +// Class: dviRenderer +// +// Class for rendering TeX DVI files. +// Part of KDVI- A previewer for TeX DVI files. +// +// (C) 2001-2005 Stefan Kebekus +// Distributed under the GPL +// + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <qcheckbox.h> +#include <qclipboard.h> +#include <qcursor.h> +#include <qlabel.h> +#include <qfileinfo.h> +#include <qlayout.h> +#include <qmessagebox.h> +#include <qpaintdevice.h> +#include <qpainter.h> +#include <qptrstack.h> +#include <qregexp.h> +#include <qurl.h> +#include <qvbox.h> + +#include <kapplication.h> +#include <kmessagebox.h> +#include <kmimemagic.h> +#include <kglobal.h> +#include <kdebug.h> +#include <keditcl.h> +#include <kfiledialog.h> +#include <kio/job.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kprinter.h> +#include <kprocess.h> +#include <kprogress.h> +#include <kstandarddirs.h> +#include <kstdguiitem.h> + +#include "documentWidget.h" +#include "dviFile.h" +#include "dviRenderer.h" +#include "fontpool.h" +#include "fontprogress.h" +#include "hyperlink.h" +#include "infodialog.h" +#include "kdvi_multipage.h" +#include "performanceMeasurement.h" +#include "prebookmark.h" +#include "psgs.h" +#include "xdvi.h" +#include "zoomlimits.h" +#include "dvisourcesplitter.h" +#include "renderedDviPagePixmap.h" + +//#define DEBUG_DVIRENDERER + +QPainter *foreGroundPainter; // QPainter used for text + + +//------ now comes the dviRenderer class implementation ---------- + +dviRenderer::dviRenderer(QWidget *par) + : DocumentRenderer(par), info(new infoDialog(par)) +{ +#ifdef DEBUG_DVIRENDERER + kdDebug(4300) << "dviRenderer( parent=" << par << " )" << endl; +#endif + + // initialize the dvi machinery + dviFile = 0; + + connect(&font_pool, SIGNAL( setStatusBarText( const QString& ) ), this, SIGNAL( setStatusBarText( const QString& ) ) ); + + parentWidget = par; + shrinkfactor = 3; + current_page = 0; + resolutionInDPI = 0.0; + + connect( &clearStatusBarTimer, SIGNAL(timeout()), this, SLOT(clearStatusBar()) ); + + currentlyDrawnPage = 0; + editorCommand = ""; + + PostScriptOutPutString = NULL; + HTML_href = NULL; + _postscript = 0; + + // Storage used for dvips and friends, i.e. for the "export" functions. + proc = 0; + progress = 0; + export_printer = 0; + export_fileName = ""; + export_tmpFileName = ""; + export_errorString = ""; + + PS_interface = new ghostscript_interface(); + // pass status bar messages through + connect(PS_interface, SIGNAL( setStatusBarText( const QString& ) ), this, SIGNAL( setStatusBarText( const QString& ) ) ); +} + + +dviRenderer::~dviRenderer() +{ +#ifdef DEBUG_DVIRENDERER + kdDebug(4300) << "~dviRenderer" << endl; +#endif + + mutex.lock(); + mutex.unlock(); + + delete PS_interface; + delete proc; + delete dviFile; + // Don't delete the export printer. This is owned by the + // kdvi_multipage. + export_printer = 0; +} + + +void dviRenderer::setPrefs(bool flag_showPS, const QString &str_editorCommand, bool useFontHints ) +{ + QMutexLocker locker(&mutex); + _postscript = flag_showPS; + editorCommand = str_editorCommand; + font_pool.setParameters( useFontHints ); + emit(documentIsChanged()); +} + + +void dviRenderer::showInfo() +{ + mutex.lock(); + info->setDVIData(dviFile); + info->show(); + mutex.unlock(); +} + + +//------ this function calls the dvi interpreter ---------- + + +void dviRenderer::drawPage(double resolution, RenderedDocumentPage *page) +{ +#ifdef DEBUG_DVIRENDERER + kdDebug(4300) << "dviRenderer::drawPage(documentPage *) called, page number " << page->getPageNumber() << endl; +#endif + + // Paranoid safety checks + if (page == 0) { + kdError(4300) << "dviRenderer::drawPage(documentPage *) called with argument == 0" << endl; + return; + } + if (page->getPageNumber() == 0) { + kdError(4300) << "dviRenderer::drawPage(documentPage *) called for a documentPage with page number 0" << endl; + return; + } + + mutex.lock(); + if ( dviFile == 0 ) { + kdError(4300) << "dviRenderer::drawPage(documentPage *) called, but no dviFile class allocated." << endl; + page->clear(); + mutex.unlock(); + return; + } + if (page->getPageNumber() > dviFile->total_pages) { + kdError(4300) << "dviRenderer::drawPage(documentPage *) called for a documentPage with page number " << page->getPageNumber() + << " but the current dviFile has only " << dviFile->total_pages << " pages." << endl; + mutex.unlock(); + return; + } + if ( dviFile->dvi_Data() == 0 ) { + kdError(4300) << "dviRenderer::drawPage(documentPage *) called, but no dviFile is loaded yet." << endl; + page->clear(); + mutex.unlock(); + return; + } + + if (resolution != resolutionInDPI) + setResolution(resolution); + + currentlyDrawnPage = page; + shrinkfactor = 1200/resolutionInDPI; + current_page = page->getPageNumber()-1; + + + // Reset colors + colorStack.clear(); + globalColor = Qt::black; + + QApplication::setOverrideCursor( waitCursor ); + foreGroundPainter = page->getPainter(); + if (foreGroundPainter != 0) { + errorMsg = QString::null; + draw_page(); + page->returnPainter(foreGroundPainter); + } + QApplication::restoreOverrideCursor(); + + page->isEmpty = false; + if (errorMsg.isEmpty() != true) { + KMessageBox::detailedError(parentWidget, + i18n("<qt><strong>File corruption!</strong> KDVI had trouble interpreting your DVI file. Most " + "likely this means that the DVI file is broken.</qt>"), + errorMsg, i18n("DVI File Error")); + errorMsg = QString::null; + currentlyDrawnPage = 0; + mutex.unlock(); + return; + } + + // Tell the user (once) if the DVI file contains source specials + // ... we don't want our great feature to go unnoticed. + RenderedDviPagePixmap* currentDVIPage = dynamic_cast<RenderedDviPagePixmap*>(currentlyDrawnPage); + if (currentDVIPage) + { + if ((dviFile->sourceSpecialMarker == true) && (currentDVIPage->sourceHyperLinkList.size() > 0)) { + dviFile->sourceSpecialMarker = false; + // Show the dialog as soon as event processing is finished, and + // the program is idle + QTimer::singleShot( 0, this, SLOT(showThatSourceInformationIsPresent()) ); + } + } + + currentlyDrawnPage = 0; + mutex.unlock(); +} + + +void dviRenderer::getText(RenderedDocumentPage* page) +{ + bool postscriptBackup = _postscript; + // Disable postscript-specials temporarely to speed up text extraction. + _postscript = false; + + drawPage(100.0, page); + + _postscript = postscriptBackup; +} + + +void dviRenderer::showThatSourceInformationIsPresent() +{ + // In principle, we should use a KMessagebox here, but we want to + // add a button "Explain in more detail..." which opens the + // Helpcenter. Thus, we practically re-implement the KMessagebox + // here. Most of the code is stolen from there. + + // Check if the 'Don't show again' feature was used + KConfig *config = kapp->config(); + KConfigGroupSaver saver( config, "Notification Messages" ); + bool showMsg = config->readBoolEntry( "KDVI-info_on_source_specials", true); + + if (showMsg) { + KDialogBase *dialog= new KDialogBase(i18n("KDVI: Information"), KDialogBase::Yes, KDialogBase::Yes, KDialogBase::Yes, + parentWidget, "information", true, true,KStdGuiItem::ok() ); + + QVBox *topcontents = new QVBox (dialog); + topcontents->setSpacing(KDialog::spacingHint()*2); + topcontents->setMargin(KDialog::marginHint()*2); + + QWidget *contents = new QWidget(topcontents); + QHBoxLayout * lay = new QHBoxLayout(contents); + lay->setSpacing(KDialog::spacingHint()*2); + + lay->addStretch(1); + QLabel *label1 = new QLabel( contents); + label1->setPixmap(QMessageBox::standardIcon(QMessageBox::Information)); + lay->add( label1 ); + QLabel *label2 = new QLabel( i18n("<qt>This DVI file contains source file information. You may click into the text with the " + "middle mouse button, and an editor will open the TeX-source file immediately.</qt>"), + contents); + label2->setMinimumSize(label2->sizeHint()); + lay->add( label2 ); + lay->addStretch(1); + QSize extraSize = QSize(50,30); + QCheckBox *checkbox = new QCheckBox(i18n("Do not show this message again"), topcontents); + extraSize = QSize(50,0); + dialog->setHelpLinkText(i18n("Explain in more detail...")); + dialog->setHelp("inverse-search", "kdvi"); + dialog->enableLinkedHelp(true); + dialog->setMainWidget(topcontents); + dialog->enableButtonSeparator(false); + dialog->incInitialSize( extraSize ); + dialog->exec(); + delete dialog; + + showMsg = !checkbox->isChecked(); + if (!showMsg) { + KConfigGroupSaver saver( config, "Notification Messages" ); + config->writeEntry( "KDVI-info_on_source_specials", showMsg); + } + config->sync(); + } +} + + +void dviRenderer::embedPostScript() +{ +#ifdef DEBUG_DVIRENDERER + kdDebug(4300) << "dviRenderer::embedPostScript()" << endl; +#endif + + if (!dviFile) + return; + + embedPS_progress = new KProgressDialog(parentWidget, "embedPSProgressDialog", + i18n("Embedding PostScript Files"), QString::null, true); + if (!embedPS_progress) + return; + embedPS_progress->setAllowCancel(false); + embedPS_progress->showCancelButton(false); + embedPS_progress->setMinimumDuration(400); + embedPS_progress->progressBar()->setTotalSteps(dviFile->numberOfExternalPSFiles); + embedPS_progress->progressBar()->setProgress(0); + embedPS_numOfProgressedFiles = 0; + + + Q_UINT16 currPageSav = current_page; + errorMsg = QString::null; + for(current_page=0; current_page < dviFile->total_pages; current_page++) { + if (current_page < dviFile->total_pages) { + command_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page]; + end_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page+1]; + } else + command_pointer = end_pointer = 0; + + memset((char *) &currinf.data, 0, sizeof(currinf.data)); + currinf.fonttable = &(dviFile->tn_table); + currinf._virtual = NULL; + prescan(&dviRenderer::prescan_embedPS); + } + + delete embedPS_progress; + + if (!errorMsg.isEmpty()) { + errorMsg = "<qt>" + errorMsg + "</qt>"; + KMessageBox::detailedError(parentWidget, "<qt>" + i18n("Not all PostScript files could be embedded into your document.") + "</qt>", errorMsg); + errorMsg = QString::null; + } else + KMessageBox::information(parentWidget, "<qt>" + i18n("All external PostScript files were embedded into your document. You " + "will probably want to save the DVI file now.") + "</qt>", + QString::null, "embeddingDone"); + + // Prescan phase starts here +#ifdef PERFORMANCE_MEASUREMENT + kdDebug(4300) << "Time elapsed till prescan phase starts " << performanceTimer.elapsed() << "ms" << endl; + QTime preScanTimer; + preScanTimer.start(); +#endif + dviFile->numberOfExternalPSFiles = 0; + prebookmarks.clear(); + for(current_page=0; current_page < dviFile->total_pages; current_page++) { + PostScriptOutPutString = new QString(); + + if (current_page < dviFile->total_pages) { + command_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page]; + end_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page+1]; + } else + command_pointer = end_pointer = 0; + + memset((char *) &currinf.data, 0, sizeof(currinf.data)); + currinf.fonttable = &(dviFile->tn_table); + currinf._virtual = NULL; + + prescan(&dviRenderer::prescan_parseSpecials); + + if (!PostScriptOutPutString->isEmpty()) + PS_interface->setPostScript(current_page, *PostScriptOutPutString); + delete PostScriptOutPutString; + } + PostScriptOutPutString = NULL; + + +#ifdef PERFORMANCE_MEASUREMENT + kdDebug(4300) << "Time required for prescan phase: " << preScanTimer.restart() << "ms" << endl; +#endif + current_page = currPageSav; + _isModified = true; +} + + +bool dviRenderer::isValidFile(const QString& filename) const +{ + QFile f(filename); + if (!f.open(IO_ReadOnly)) + return false; + + unsigned char test[4]; + if ( f.readBlock( (char *)test,2)<2 || test[0] != 247 || test[1] != 2 ) + return false; + + int n = f.size(); + if ( n < 134 ) // Too short for a dvi file + return false; + f.at( n-4 ); + + unsigned char trailer[4] = { 0xdf,0xdf,0xdf,0xdf }; + + if ( f.readBlock( (char *)test, 4 )<4 || strncmp( (char *)test, (char *) trailer, 4 ) ) + return false; + // We suppose now that the dvi file is complete and OK + return true; +} + + +bool dviRenderer::setFile(const QString &fname, const KURL &base) +{ +#ifdef DEBUG_DVIRENDERER + kdDebug(4300) << "dviRenderer::setFile( fname='" << fname << "', ref='" << ref << "', sourceMarker=" << sourceMarker << " )" << endl; +#endif + + QMutexLocker lock(&mutex); + + QFileInfo fi(fname); + QString filename = fi.absFilePath(); + + // If fname is the empty string, then this means: "close". Delete + // the dvifile and the pixmap. + if (fname.isEmpty()) { + // Delete DVI file + info->setDVIData(0); + delete dviFile; + dviFile = 0; + return true; + } + + + // Make sure the file actually exists. + if (!fi.exists() || fi.isDir()) { + KMessageBox::error( parentWidget, + i18n("<qt><strong>File error.</strong> The specified file '%1' does not exist. " + "KDVI already tried to add the ending '.dvi'.</qt>").arg(filename), + i18n("File Error!")); + return false; + } + + // Check if we are really loading a DVI file, and complain about the + // mime type, if the file is not DVI. Perhaps we should move the + // procedure later to the kviewpart, instead of the implementaton in + // the multipage. + QString mimetype( KMimeMagic::self()->findFileType( fname )->mimeType() ); + if (mimetype != "application/x-dvi") { + KMessageBox::sorry( parentWidget, + i18n( "<qt>Could not open file <nobr><strong>%1</strong></nobr> which has " + "type <strong>%2</strong>. KDVI can only load DVI (.dvi) files.</qt>" ) + .arg( fname ) + .arg( mimetype ) ); + return false; + } + + // Check if the file is a valid DVI file. + if (!isValidFile(filename)) + { + KMessageBox::sorry( parentWidget, + i18n("<qt>File corruption! KDVI had trouble interpreting your DVI file. Most " + "likely this means that the DVI file is broken.</qt>") + .arg( fname ) ); + return false; + } + + QApplication::setOverrideCursor( waitCursor ); + dvifile *dviFile_new = new dvifile(filename, &font_pool); + + if ((dviFile == 0) || (dviFile->filename != filename)) + dviFile_new->sourceSpecialMarker = true; + else + dviFile_new->sourceSpecialMarker = false; + + if ((dviFile_new->dvi_Data() == NULL)||(dviFile_new->errorMsg.isEmpty() != true)) { + QApplication::restoreOverrideCursor(); + if (dviFile_new->errorMsg.isEmpty() != true) + KMessageBox::detailedError(parentWidget, + i18n("<qt>File corruption! KDVI had trouble interpreting your DVI file. Most " + "likely this means that the DVI file is broken.</qt>"), + dviFile_new->errorMsg, i18n("DVI File Error")); + delete dviFile_new; + return false; + } + + delete dviFile; + dviFile = dviFile_new; + numPages = dviFile->total_pages; + info->setDVIData(dviFile); + _isModified = false; + baseURL = base; + + font_pool.setExtraSearchPath( fi.dirPath(true) ); + font_pool.setCMperDVIunit( dviFile->getCmPerDVIunit() ); + + // Extract PostScript from the DVI file, and store the PostScript + // specials in PostScriptDirectory, and the headers in the + // PostScriptHeaderString. + PS_interface->clear(); + + // If the DVI file comes from a remote URL (e.g. downloaded from a + // web server), we limit the PostScript files that can be accessed + // by this file to the download directory, in order to limit the + // possibilities of a denial of service attack. + QString includePath; + if (!baseURL.isLocalFile()) { + includePath = filename; + includePath.truncate(includePath.findRev('/')); + } + PS_interface->setIncludePath(includePath); + + // We will also generate a list of hyperlink-anchors and source-file + // anchors in the document. So declare the existing lists empty. + anchorList.clear(); + sourceHyperLinkAnchors.clear(); + bookmarks.clear(); + prebookmarks.clear(); + + if (dviFile->page_offset.isEmpty() == true) + return false; + + // Locate fonts. + font_pool.locateFonts(); + + // Update the list of fonts in the info window + if (info != 0) + info->setFontInfo(&font_pool); + + // We should pre-scan the document now (to extract embedded, + // PostScript, Hyperlinks, ets). + + // PRESCAN STARTS HERE +#ifdef PERFORMANCE_MEASUREMENT + kdDebug(4300) << "Time elapsed till prescan phase starts " << performanceTimer.elapsed() << "ms" << endl; + QTime preScanTimer; + preScanTimer.start(); +#endif + dviFile->numberOfExternalPSFiles = 0; + Q_UINT16 currPageSav = current_page; + prebookmarks.clear(); + + for(current_page=0; current_page < dviFile->total_pages; current_page++) { + PostScriptOutPutString = new QString(); + + if (current_page < dviFile->total_pages) { + command_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page]; + end_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page+1]; + } else + command_pointer = end_pointer = 0; + + memset((char *) &currinf.data, 0, sizeof(currinf.data)); + currinf.fonttable = &(dviFile->tn_table); + currinf._virtual = NULL; + prescan(&dviRenderer::prescan_parseSpecials); + + if (!PostScriptOutPutString->isEmpty()) + PS_interface->setPostScript(current_page, *PostScriptOutPutString); + delete PostScriptOutPutString; + } + PostScriptOutPutString = NULL; + + // Generate the list of bookmarks + bookmarks.clear(); + QPtrStack<Bookmark> stack; + stack.setAutoDelete (false); + QValueVector<PreBookmark>::iterator it; + for( it = prebookmarks.begin(); it != prebookmarks.end(); ++it ) { + Bookmark *bmk = new Bookmark((*it).title, findAnchor((*it).anchorName)); + if (stack.isEmpty()) + bookmarks.append(bmk); + else { + stack.top()->subordinateBookmarks.append(bmk); + stack.remove(); + } + for(int i=0; i<(*it).noOfChildren; i++) + stack.push(bmk); + } + prebookmarks.clear(); + + +#ifdef PERFORMANCE_MEASUREMENT + kdDebug(4300) << "Time required for prescan phase: " << preScanTimer.restart() << "ms" << endl; +#endif + current_page = currPageSav; + // PRESCAN ENDS HERE + + + pageSizes.resize(0); + if (dviFile->suggestedPageSize != 0) { + // Fill the vector pageSizes with total_pages identical entries + pageSizes.resize(dviFile->total_pages, *(dviFile->suggestedPageSize)); + } + + QApplication::restoreOverrideCursor(); + return true; +} + + +Anchor dviRenderer::parseReference(const QString &reference) +{ + mutex.lock(); + +#ifdef DEBUG_DVIRENDERER + kdError(4300) << "dviRenderer::parseReference( " << reference << " ) called" << endl; +#endif + + if (dviFile == 0) { + mutex.unlock(); + return Anchor(); + } + + // case 1: The reference is a number, which we'll interpret as a + // page number. + bool ok; + int page = reference.toInt ( &ok ); + if (ok == true) { + if (page < 0) + page = 0; + if (page > dviFile->total_pages) + page = dviFile->total_pages; + + mutex.unlock(); + return Anchor(page, Length() ); + } + + // case 2: The reference is of form "src:1111Filename", where "1111" + // points to line number 1111 in the file "Filename". KDVI then + // looks for source specials of the form "src:xxxxFilename", and + // tries to find the special with the biggest xxxx + if (reference.find("src:",0,false) == 0) { + + // Extract the file name and the numeral part from the reference string + DVI_SourceFileSplitter splitter(reference, dviFile->filename); + Q_UINT32 refLineNumber = splitter.line(); + QString refFileName = splitter.filePath(); + + if (sourceHyperLinkAnchors.isEmpty()) { + KMessageBox::sorry(parentWidget, i18n("<qt>You have asked KDVI to locate the place in the DVI file which corresponds to " + "line %1 in the TeX-file <strong>%2</strong>. It seems, however, that the DVI file " + "does not contain the necessary source file information. " + "We refer to the manual of KDVI for a detailed explanation on how to include this " + "information. Press the F1 key to open the manual.</qt>").arg(refLineNumber).arg(refFileName), + i18n("Could Not Find Reference")); + mutex.unlock(); + return Anchor(); + } + + // Go through the list of source file anchors, and find the anchor + // whose line number is the biggest among those that are smaller + // than the refLineNumber. That way, the position in the DVI file + // which is highlighted is always a little further up than the + // position in the editor, e.g. if the DVI file contains + // positional information at the beginning of every paragraph, + // KDVI jumps to the beginning of the paragraph that the cursor is + // in, and never to the next paragraph. If source file anchors for + // the refFileName can be found, but none of their line numbers is + // smaller than the refLineNumber, the reason is most likely, that + // the cursor in the editor stands somewhere in the preamble of + // the LaTeX file. In that case, we jump to the beginning of the + // document. + bool anchorForRefFileFound = false; // Flag that is set if source file anchors for the refFileName could be found at all + + QValueVector<DVI_SourceFileAnchor>::iterator bestMatch = sourceHyperLinkAnchors.end(); + QValueVector<DVI_SourceFileAnchor>::iterator it; + for( it = sourceHyperLinkAnchors.begin(); it != sourceHyperLinkAnchors.end(); ++it ) + if (refFileName.stripWhiteSpace() == it->fileName.stripWhiteSpace() + || refFileName.stripWhiteSpace() == it->fileName.stripWhiteSpace() + ".tex" + ) { + anchorForRefFileFound = true; + + if ( (it->line <= refLineNumber) && + ( (bestMatch == sourceHyperLinkAnchors.end()) || (it->line > bestMatch->line) ) ) + bestMatch = it; + } + + if (bestMatch != sourceHyperLinkAnchors.end()) { + mutex.unlock(); + return Anchor(bestMatch->page, bestMatch->distance_from_top); + } else + if (anchorForRefFileFound == false) + KMessageBox::sorry(parentWidget, i18n("<qt>KDVI was not able to locate the place in the DVI file which corresponds to " + "line %1 in the TeX-file <strong>%2</strong>.</qt>").arg(refLineNumber).arg(refFileName), + i18n( "Could Not Find Reference" )); + else { + mutex.unlock(); + return Anchor(); + } + mutex.unlock(); + return Anchor(); + } + mutex.unlock(); + return Anchor(); +} + + +void dviRenderer::setResolution(double resolution_in_DPI) +{ + // Ignore minute changes. The difference to the current value would + // hardly be visible anyway. That saves a lot of re-painting, + // e.g. when the user resizes the window, and a flickery mouse + // changes the window size by 1 pixel all the time. + if (fabs(resolutionInDPI-resolution_in_DPI) < 1) + return; + + resolutionInDPI = resolution_in_DPI; + + // Pass the information on to the font pool. + font_pool.setDisplayResolution( resolutionInDPI ); + shrinkfactor = 1200/resolutionInDPI; + return; +} + + +void dviRenderer::clearStatusBar() +{ + emit setStatusBarText( QString::null ); +} + + +void dviRenderer::handleSRCLink(const QString &linkText, QMouseEvent *e, DocumentWidget *win) +{ +#ifdef DEBUG_SPECIAL + RenderedDviPagePixmap* currentDVIPage = dynamic_cast<RenderedDviPagePixmap*> currentlyDrawnPage; + if (currentDVIPage) + { + kdDebug(4300) << "Source hyperlink to " << currentDVIPage->sourceHyperLinkList[i].linkText << endl; + } +#endif + + DVI_SourceFileSplitter splitter(linkText, dviFile->filename); + QString TeXfile = splitter.filePath(); + if ( ! splitter.fileExists() ) + { + KMessageBox::sorry(parentWidget, QString("<qt>") + + i18n("The DVI-file refers to the TeX-file " + "<strong>%1</strong> which could not be found.").arg(KShellProcess::quote(TeXfile)) + + QString("</qt>"), + i18n( "Could Not Find File" )); + return; + } + + QString command = editorCommand; + if (command.isEmpty() == true) { + int r = KMessageBox::warningContinueCancel(parentWidget, QString("<qt>") + + i18n("You have not yet specified an editor for inverse search. " + "Please choose your favorite editor in the " + "<strong>DVI options dialog</strong> " + "which you will find in the <strong>Settings</strong>-menu.") + + QString("</qt>"), + i18n("Need to Specify Editor"), + i18n("Use KDE's Editor Kate for Now")); + if (r == KMessageBox::Continue) + command = "kate %f"; + else + return; + } + command = command.replace( "%l", QString::number(splitter.line()) ).replace( "%f", KShellProcess::quote(TeXfile) ); + +#ifdef DEBUG_SPECIAL + kdDebug(4300) << "Calling program: " << command << endl; +#endif + + // There may still be another program running. Since we don't + // want to mix the output of several programs, we will + // henceforth dimiss the output of the older programm. "If it + // hasn't failed until now, we don't care." + if (proc != 0) { + qApp->disconnect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)), 0, 0); + qApp->disconnect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), 0, 0); + proc = 0; + } + + // Set up a shell process with the editor command. + proc = new KShellProcess(); + if (proc == 0) { + kdError(4300) << "Could not allocate ShellProcess for the editor command." << endl; + return; + } + qApp->connect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)), this, SLOT(dvips_output_receiver(KProcess *, char *, int))); + qApp->connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(dvips_output_receiver(KProcess *, char *, int))); + qApp->connect(proc, SIGNAL(processExited(KProcess *)), this, SLOT(editorCommand_terminated(KProcess *))); + // Merge the editor-specific editor message here. + export_errorString = i18n("<qt>The external program<br><br><tt><strong>%1</strong></tt><br/><br/>which was used to call the editor " + "for inverse search, reported an error. You might wish to look at the <strong>document info " + "dialog</strong> which you will find in the File-Menu for a precise error report. The " + "manual for KDVI contains a detailed explanation how to set up your editor for use with KDVI, " + "and a list of common problems.</qt>").arg(command); + + info->clear(i18n("Starting the editor...")); + + int flashOffset = e->y(); // Heuristic correction. Looks better. + win->flash(flashOffset); + + + proc->clearArguments(); + *proc << command; + proc->closeStdin(); + if (proc->start(KProcess::NotifyOnExit, KProcess::AllOutput) == false) { + kdError(4300) << "Editor failed to start" << endl; + return; + } +} + + +QString dviRenderer::PDFencodingToQString(const QString& _pdfstring) +{ + // This method locates special PDF characters in a string and + // replaces them by UTF8. See Section 3.2.3 of the PDF reference + // guide for information. + QString pdfstring = _pdfstring; + pdfstring = pdfstring.replace("\\n", "\n"); + pdfstring = pdfstring.replace("\\r", "\n"); + pdfstring = pdfstring.replace("\\t", "\t"); + pdfstring = pdfstring.replace("\\f", "\f"); + pdfstring = pdfstring.replace("\\(", "("); + pdfstring = pdfstring.replace("\\)", ")"); + pdfstring = pdfstring.replace("\\\\", "\\"); + + // Now replace octal character codes with the characters they encode + int pos; + QRegExp rx( "(\\\\)(\\d\\d\\d)" ); // matches "\xyz" where x,y,z are numbers + while((pos = rx.search( pdfstring )) != -1) { + pdfstring = pdfstring.replace(pos, 4, QChar(rx.cap(2).toInt(0,8))); + } + rx.setPattern( "(\\\\)(\\d\\d)" ); // matches "\xy" where x,y are numbers + while((pos = rx.search( pdfstring )) != -1) { + pdfstring = pdfstring.replace(pos, 3, QChar(rx.cap(2).toInt(0,8))); + } + rx.setPattern( "(\\\\)(\\d)" ); // matches "\x" where x is a number + while((pos = rx.search( pdfstring )) != -1) { + pdfstring = pdfstring.replace(pos, 4, QChar(rx.cap(2).toInt(0,8))); + } + return pdfstring; +} + + +#include "dviRenderer.moc" diff --git a/kdvi/dviRenderer.h b/kdvi/dviRenderer.h new file mode 100644 index 00000000..98f0f4bd --- /dev/null +++ b/kdvi/dviRenderer.h @@ -0,0 +1,300 @@ +// -*- C++ -*- +// +// Class: dviRenderer +// +// Class for rendering TeX DVI files. +// Part of KDVI- A previewer for TeX DVI files. +// +// (C) 2001-2005 Stefan Kebekus. Distributed under the GPL. + +#ifndef _dvirenderer_h_ +#define _dvirenderer_h_ + +#include "bigEndianByteReader.h" +#include "documentRenderer.h" +#include "fontpool.h" + +#include <kurl.h> +#include <qintdict.h> +#include <qpointarray.h> +#include <qtimer.h> +#include <qvaluestack.h> +#include <qvaluevector.h> + +class Anchor; +class DocumentWidget; +class dviRenderer; +class fontProgressDialog; +class ghostscript_interface; +class infoDialog; +class KAction; +class KDVIMultiPage; +class KPrinter; +class KProcess; +class KProgressDialog; +class KShellProcess; +class PreBookmark; +class TeXFontDefinition; + +extern const int MFResolutions[]; + +class DVI_SourceFileAnchor { + public: + DVI_SourceFileAnchor() {} + DVI_SourceFileAnchor(const QString& name, Q_UINT32 ln, Q_UINT32 pg, const Length& _distance_from_top) + : fileName(name), line(ln), page(pg), + distance_from_top(_distance_from_top) {} + + QString fileName; + Q_UINT32 line; + Q_UINT32 page; + Length distance_from_top; +}; + +/** Compound of registers, as defined in section 2.6.2 of the DVI + driver standard, Level 0, published by the TUG DVI driver + standards committee. */ + +struct framedata { + long dvi_h; + long dvi_v; + long w; + long x; + long y; + long z; + int pxl_v; +}; + + +/* this information is saved when using virtual fonts */ + +typedef void (dviRenderer::*set_char_proc)(unsigned int, unsigned int); +typedef void (dviRenderer::*parseSpecials)(char *, Q_UINT8 *); + +struct drawinf { + struct framedata data; + TeXFontDefinition *fontp; + set_char_proc set_char_p; + + QIntDict<TeXFontDefinition> *fonttable; + TeXFontDefinition *_virtual; +}; + + + +class dviRenderer : public DocumentRenderer, bigEndianByteReader +{ + Q_OBJECT + +public: + dviRenderer(QWidget *parent); + ~dviRenderer(); + + virtual bool setFile(const QString &fname, const KURL &base); + + class dvifile *dviFile; + + void setPrefs(bool flag_showPS, const QString &editorCommand, bool useFontHints ); + + virtual bool supportsTextSearch() const {return true;} + + bool showPS() { return _postscript; } + int curr_page() { return current_page+1; } + virtual bool isValidFile(const QString& fileName) const; + + + /** This method will try to parse the reference part of the DVI + file's URL, (either a number, which is supposed to be a page + number, or src:<line><filename>) and see if a corresponding + section of the DVI file can be found. If so, it returns an + anchor to that section. If not, it returns an invalid anchor. */ + virtual Anchor parseReference(const QString &reference); + + // These should not be public... only for the moment + void read_postamble(); + void draw_part(double current_dimconv, bool is_vfmacro); + void set_vf_char(unsigned int cmd, unsigned int ch); + void set_char(unsigned int cmd, unsigned int ch); + void set_empty_char(unsigned int cmd, unsigned int ch); + void set_no_char(unsigned int cmd, unsigned int ch); + void applicationDoSpecial(char * cp); + + void special(long nbytes); + void printErrorMsgForSpecials(const QString& msg); + void color_special(const QString& cp); + void html_href_special(const QString& cp); + void html_anchor_end(); + void draw_page(); + +public slots: + void exportPS(const QString& fname = QString::null, const QString& options = QString::null, KPrinter* printer = 0); + void exportPDF(); + + void showInfo(); + void handleSRCLink(const QString &linkText, QMouseEvent *e, DocumentWidget *widget); + + void embedPostScript(); + void abortExternalProgramm(); + + /** simply emits "setStatusBarText( QString::null )". This is used + in dviRenderer::mouseMoveEvent(), see the explanation there. */ + void clearStatusBar(); + + virtual void drawPage(double res, RenderedDocumentPage *page); + virtual void getText(RenderedDocumentPage* page); + + /** Slots used in conjunction with external programs */ + void dvips_output_receiver(KProcess *, char *buffer, int buflen); + void dvips_terminated(KProcess *); + void editorCommand_terminated(KProcess *); + +signals: + /** Passed through to the top-level kpart. */ + // void setStatusBarText( const QString& ); + +private slots: + /** This method shows a dialog that tells the user that source + information is present, and gives the opportunity to open the + manual and learn more about forward and inverse search */ + void showThatSourceInformationIsPresent(); + +private: + /** URL to the DVI file + + This field is initialized by the setFile() method. See the + explanation there. */ + KURL baseURL; + + + /** This method parses a color specification of type "gray 0.5", "rgb + 0.5 0.7 1.0", "hsb ...", "cmyk .." or "PineGreen". See the source + code for details. */ + QColor parseColorSpecification(const QString& colorSpec); + + /** This map contains the colors which are known by name. This field + is initialized in the method parseColorSpecification() as soon as + it is needed. */ + QMap<QString, QColor> namedColors; + + /* This method locates special PDF characters in a string and + replaces them by UTF8. See Section 3.2.3 of the PDF reference + guide for information */ + QString PDFencodingToQString(const QString& pdfstring); + + void setResolution(double resolution_in_DPI); + + fontPool font_pool; + infoDialog *info; + + double resolutionInDPI; + + // @@@ explanation + void prescan(parseSpecials specialParser); + void prescan_embedPS(char *cp, Q_UINT8 *); + void prescan_removePageSizeInfo(char *cp, Q_UINT8 *); + void prescan_parseSpecials(char *cp, Q_UINT8 *); + void prescan_ParsePapersizeSpecial(const QString& cp); + void prescan_ParseBackgroundSpecial(const QString& cp); + void prescan_ParseHTMLAnchorSpecial(const QString& cp); + void prescan_ParsePSHeaderSpecial(const QString& cp); + void prescan_ParsePSBangSpecial(const QString& cp); + void prescan_ParsePSQuoteSpecial(const QString& cp); + void prescan_ParsePSSpecial(const QString& cp); + void prescan_ParsePSFileSpecial(const QString& cp); + void prescan_ParseSourceSpecial(const QString& cp); + void prescan_setChar(unsigned int ch); + + /* */ + QValueVector<PreBookmark> prebookmarks; + + + + /** Utility fields used by the embedPostScript method*/ + KProgressDialog *embedPS_progress; + Q_UINT16 embedPS_numOfProgressedFiles; + + /** Shrink factor. Units are not quite clear */ + double shrinkfactor; + + QString errorMsg; + + /** Methods which handle certain special commands. */ + void epsf_special(const QString& cp); + void source_special(const QString& cp); + + /** TPIC specials */ + void TPIC_setPen_special(const QString& cp); + void TPIC_addPath_special(const QString& cp); + void TPIC_flushPath_special(); + + /** This timer is used to delay clearing of the statusbar. Clearing + the statusbar is delayed to avoid awful flickering when the + mouse moves over a block of text that contains source + hyperlinks. The signal timeout() is connected to the method + clearStatusBar() of *this. */ + QTimer clearStatusBarTimer; + + // List of source-hyperlinks on all pages. This vector is generated + // when the DVI-file is first loaded, i.e. when draw_part is called + // with PostScriptOutPutString != NULL + QValueVector<DVI_SourceFileAnchor> sourceHyperLinkAnchors; + + // If not NULL, the text currently drawn represents a source + // hyperlink to the (relative) URL given in the string; + QString *source_href; + + // If not NULL, the text currently drawn represents a hyperlink to + // the (relative) URL given in the string; + QString *HTML_href; + + QString editorCommand; + + /** Stack for register compounds, used for the DVI-commands PUSH/POP + as explained in section 2.5 and 2.6.2 of the DVI driver standard, + Level 0, published by the TUG DVI driver standards committee. */ + QValueStack<struct framedata> stack; + + /** A stack where color are stored, according to the documentation of + DVIPS */ + QValueStack<QColor> colorStack; + + /** The global color is to be used when the color stack is empty */ + QColor globalColor; + + /** If PostScriptOutPutFile is non-zero, then no rendering takes + place. Instead, the PostScript code which is generated by the + \special-commands is written to the PostScriptString */ + QString *PostScriptOutPutString; + + ghostscript_interface *PS_interface; + + /** true, if gs should be used, otherwise, only bounding boxes are + drawn. */ + bool _postscript; + + /** This flag is used when rendering a dvi-page. It is set to "true" + when any dvi-command other than "set" or "put" series of commands + is encountered. This is considered to mark the end of a word. */ + bool line_boundary_encountered; + bool word_boundary_encountered; + + unsigned int current_page; + + /** Used to run and to show the progress of dvips and friends. */ + fontProgressDialog *progress; + KShellProcess *proc; + KPrinter *export_printer; + QString export_fileName; + QString export_tmpFileName; + QString export_errorString; + + /** Data required for handling TPIC specials */ + float penWidth_in_mInch; + QPointArray TPIC_path; + Q_UINT16 number_of_elements_in_path; + + struct drawinf currinf; + RenderedDocumentPage* currentlyDrawnPage; +}; + +#endif diff --git a/kdvi/dviRenderer_draw.cpp b/kdvi/dviRenderer_draw.cpp new file mode 100644 index 00000000..d09caf6d --- /dev/null +++ b/kdvi/dviRenderer_draw.cpp @@ -0,0 +1,660 @@ +/* + * Copyright (c) 1994 Paul Vojta. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * NOTE: + * xdvi is based on prior work as noted in the modification history, below. + */ + +/* + * DVI previewer for X. + * + * Eric Cooper, CMU, September 1985. + * + * Code derived from dvi-imagen.c. + * + * Modification history: + * 1/1986 Modified for X.10 --Bob Scheifler, MIT LCS. + * 7/1988 Modified for X.11 --Mark Eichin, MIT + * 12/1988 Added 'R' option, toolkit, magnifying glass + * --Paul Vojta, UC Berkeley. + * 2/1989 Added tpic support --Jeffrey Lee, U of Toronto + * 4/1989 Modified for System V --Donald Richardson, Clarkson Univ. + * 3/1990 Added VMS support --Scott Allendorf, U of Iowa + * 7/1990 Added reflection mode --Michael Pak, Hebrew U of Jerusalem + * 1/1992 Added greyscale code --Till Brychcy, Techn. Univ. Muenchen + * and Lee Hetherington, MIT + * 4/1994 Added DPS support, bounding box + * --Ricardo Telichevesky + * and Luis Miguel Silveira, MIT RLE. + */ + +//#define DEBUG_RENDER 0 + +#include <config.h> + +#include "dviRenderer.h" +#include "dvi.h" +#include "dviFile.h" +#include "fontpool.h" +#include "hyperlink.h" +#include "kdvi_multipage.h" +#include "performanceMeasurement.h" +#include "psgs.h" +#include "renderedDviPagePixmap.h" +#include "TeXFont.h" +#include "textBox.h" +#include "xdvi.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <qpainter.h> +#include <qbitmap.h> +#include <qimage.h> +#include <qpainter.h> +#include <qfileinfo.h> + +extern QPainter *foreGroundPainter; + + +/** Routine to print characters. */ + +void dviRenderer::set_char(unsigned int cmd, unsigned int ch) +{ +#ifdef DEBUG_RENDER + kdDebug(4300) << "set_char #" << ch << endl; +#endif + + glyph *g; + if (colorStack.isEmpty()) + g = ((TeXFont *)(currinf.fontp->font))->getGlyph(ch, true, globalColor); + else + g = ((TeXFont *)(currinf.fontp->font))->getGlyph(ch, true, colorStack.top()); + if (g == NULL) + return; + + long dvi_h_sav = currinf.data.dvi_h; + + QPixmap pix = g->shrunkenCharacter; + int x = ((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))) - g->x2; + int y = currinf.data.pxl_v - g->y2; + + // Draw the character. + foreGroundPainter->drawPixmap(x, y, pix); + + // Are we drawing text for a hyperlink? And are hyperlinks + // enabled? + if (HTML_href != NULL) { + // Now set up a rectangle which is checked against every mouse + // event. + if (line_boundary_encountered == true) { + // Set up hyperlink + Hyperlink dhl; + dhl.baseline = currinf.data.pxl_v; + dhl.box.setRect(x, y, pix.width(), pix.height()); + dhl.linkText = *HTML_href; + currentlyDrawnPage->hyperLinkList.push_back(dhl); + } else { + QRect dshunion = currentlyDrawnPage->hyperLinkList[currentlyDrawnPage->hyperLinkList.size()-1].box.unite(QRect(x, y, pix.width(), pix.height())) ; + currentlyDrawnPage->hyperLinkList[currentlyDrawnPage->hyperLinkList.size()-1].box = dshunion; + } + } + + // Are we drawing text for a source hyperlink? And are source + // hyperlinks enabled? + // If we are printing source hyperlinks are irrelevant, otherwise we + // actually got a pointer to a RenderedDviPagePixmap. + RenderedDviPagePixmap* currentDVIPage = dynamic_cast<RenderedDviPagePixmap*>(currentlyDrawnPage); + if (source_href != 0 && currentDVIPage) { + // Now set up a rectangle which is checked against every mouse + // event. + if (line_boundary_encountered == true) { + // Set up source hyperlinks + Hyperlink dhl; + dhl.baseline = currinf.data.pxl_v; + dhl.box.setRect(x, y, pix.width(), pix.height()); + if (source_href != NULL) + dhl.linkText = *source_href; + else + dhl.linkText = ""; + currentDVIPage->sourceHyperLinkList.push_back(dhl); + } else { + QRect dshunion = currentDVIPage->sourceHyperLinkList[currentDVIPage->sourceHyperLinkList.size()-1].box.unite(QRect(x, y, pix.width(), pix.height())) ; + currentDVIPage->sourceHyperLinkList[currentDVIPage->sourceHyperLinkList.size()-1].box = dshunion; + } + } + + // Code for DVI -> text functions (e.g. marking of text, full text + // search, etc.). Set up the currentlyDrawnPage->textBoxList. + TextBox link; + link.box.setRect(x, y, pix.width(), pix.height()); + link.text = ""; + currentlyDrawnPage->textBoxList.push_back(link); + + switch(ch) { + case 0x0b: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "ff"; + break; + case 0x0c: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "fi"; + break; + case 0x0d: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "fl"; + break; + case 0x0e: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "ffi"; + break; + case 0x0f: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "ffl"; + break; + + case 0x7b: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "-"; + break; + case 0x7c: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "---"; + break; + case 0x7d: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "\""; + break; + case 0x7e: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "~"; + break; + case 0x7f: + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "@@"; // @@@ check! + break; + + default: + if ((ch >= 0x21) && (ch <= 0x7a)) + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += QChar(ch); + else + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += "?"; + break; + } + + + if (cmd == PUT1) + currinf.data.dvi_h = dvi_h_sav; + else + currinf.data.dvi_h += (int)(currinf.fontp->scaled_size_in_DVI_units * dviFile->getCmPerDVIunit() * + (1200.0 / 2.54)/16.0 * g->dvi_advance_in_units_of_design_size_by_2e20 + 0.5); + + word_boundary_encountered = false; + line_boundary_encountered = false; +} + +void dviRenderer::set_empty_char(unsigned int, unsigned int) +{ + return; +} + +void dviRenderer::set_vf_char(unsigned int cmd, unsigned int ch) +{ +#ifdef DEBUG_RENDER + kdDebug(4300) << "dviRenderer::set_vf_char( cmd=" << cmd << ", ch=" << ch << " )" << endl; +#endif + + static unsigned char c; + macro *m = &currinf.fontp->macrotable[ch]; + if (m->pos == NULL) { + kdError(4300) << "Character " << ch << " not defined in font " << currinf.fontp->fontname << endl; + m->pos = m->end = &c; + return; + } + + long dvi_h_sav = currinf.data.dvi_h; + + struct drawinf oldinfo = currinf; + currinf.data.w = 0; + currinf.data.x = 0; + currinf.data.y = 0; + currinf.data.z = 0; + + currinf.fonttable = &(currinf.fontp->vf_table); + currinf._virtual = currinf.fontp; + Q_UINT8 *command_ptr_sav = command_pointer; + Q_UINT8 *end_ptr_sav = end_pointer; + command_pointer = m->pos; + end_pointer = m->end; + draw_part(currinf.fontp->scaled_size_in_DVI_units*(dviFile->getCmPerDVIunit() * 1200.0 / 2.54)/16.0, true); + command_pointer = command_ptr_sav; + end_pointer = end_ptr_sav; + currinf = oldinfo; + + if (cmd == PUT1) + currinf.data.dvi_h = dvi_h_sav; + else + currinf.data.dvi_h += (int)(currinf.fontp->scaled_size_in_DVI_units * dviFile->getCmPerDVIunit() * + (1200.0 / 2.54)/16.0 * m->dvi_advance_in_units_of_design_size_by_2e20 + 0.5); +} + + +void dviRenderer::set_no_char(unsigned int cmd, unsigned int ch) +{ +#ifdef DEBUG_RENDER + kdDebug(4300) << "dviRenderer::set_no_char( cmd=" << cmd << ", ch =" << ch << " )" << endl; +#endif + + if (currinf._virtual) { + currinf.fontp = currinf._virtual->first_font; + if (currinf.fontp != NULL) { + currinf.set_char_p = currinf.fontp->set_char_p; + (this->*currinf.set_char_p)(cmd, ch); + return; + } + } + + errorMsg = i18n("The DVI code set a character of an unknown font."); + return; +} + + +void dviRenderer::draw_part(double current_dimconv, bool is_vfmacro) +{ +#ifdef DEBUG_RENDER + kdDebug(4300) << "draw_part" << endl; +#endif + + Q_INT32 RRtmp=0, WWtmp=0, XXtmp=0, YYtmp=0, ZZtmp=0; + Q_UINT8 ch; + + currinf.fontp = NULL; + currinf.set_char_p = &dviRenderer::set_no_char; + + for (;;) { + ch = readUINT8(); + if (ch <= (unsigned char) (SETCHAR0 + 127)) { + (this->*currinf.set_char_p)(ch, ch); + } else + if (FNTNUM0 <= ch && ch <= (unsigned char) (FNTNUM0 + 63)) { + currinf.fontp = currinf.fonttable->find(ch - FNTNUM0); + if (currinf.fontp == NULL) { + errorMsg = i18n("The DVI code referred to font #%1, which was not previously defined.").arg(ch - FNTNUM0); + return; + } + currinf.set_char_p = currinf.fontp->set_char_p; + } else { + Q_INT32 a, b; + + switch (ch) { + case SET1: + case PUT1: + (this->*currinf.set_char_p)(ch, readUINT8()); + break; + + case SETRULE: + if (is_vfmacro == false) { + word_boundary_encountered = true; + line_boundary_encountered = true; + } + /* Be careful, dvicopy outputs rules with height = + 0x80000000. We don't want any SIGFPE here. */ + a = readUINT32(); + b = readUINT32(); + b = ((long) (b * current_dimconv)); + if (a > 0 && b > 0) { + int h = ((int) ROUNDUP(((long) (a * current_dimconv)), shrinkfactor * 65536)); + int w = ((int) ROUNDUP(b, shrinkfactor * 65536)); + + if (colorStack.isEmpty()) + foreGroundPainter->fillRect( ((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))), + currinf.data.pxl_v - h + 1, w?w:1, h?h:1, globalColor ); + else + foreGroundPainter->fillRect( ((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))), + currinf.data.pxl_v - h + 1, w?w:1, h?h:1, colorStack.top() ); + } + currinf.data.dvi_h += b; + break; + + case PUTRULE: + if (is_vfmacro == false) { + word_boundary_encountered = true; + line_boundary_encountered = true; + } + a = readUINT32(); + b = readUINT32(); + a = ((long) (a * current_dimconv)); + b = ((long) (b * current_dimconv)); + if (a > 0 && b > 0) { + int h = ((int) ROUNDUP(a, shrinkfactor * 65536)); + int w = ((int) ROUNDUP(b, shrinkfactor * 65536)); + if (colorStack.isEmpty()) + foreGroundPainter->fillRect( ((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))), + currinf.data.pxl_v - h + 1, w?w:1, h?h:1, globalColor ); + else + foreGroundPainter->fillRect( ((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))), + currinf.data.pxl_v - h + 1, w?w:1, h?h:1, colorStack.top() ); + } + break; + + case NOP: + break; + + case BOP: + if (is_vfmacro == false) { + word_boundary_encountered = true; + line_boundary_encountered = true; + } + command_pointer += 11 * 4; + currinf.data.dvi_h = 1200 << 16; // Reminder: DVI-coordinates start at (1",1") from top of page + currinf.data.dvi_v = 1200; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + currinf.data.w = currinf.data.x = currinf.data.y = currinf.data.z = 0; + break; + + case EOP: + // Check if we are just at the end of a virtual font macro. + if (is_vfmacro == false) { + // This is really the end of a page, and not just the end + // of a macro. Mark the end of the current word. + word_boundary_encountered = true; + line_boundary_encountered = true; + // Sanity check for the dvi-file: The DVI-standard asserts + // that at the end of a page, the stack should always be + // empty. + if (!stack.isEmpty()) { + kdDebug(4300) << "DRAW: The stack was not empty when the EOP command was encountered." << endl; + errorMsg = i18n("The stack was not empty when the EOP command was encountered."); + return; + } + } + return; + + case PUSH: + stack.push(currinf.data); + break; + + case POP: + if (stack.isEmpty()) { + errorMsg = i18n("The stack was empty when a POP command was encountered."); + return; + } else + currinf.data = stack.pop(); + word_boundary_encountered = true; + line_boundary_encountered = true; + break; + + case RIGHT1: + case RIGHT2: + case RIGHT3: + case RIGHT4: + RRtmp = readINT(ch - RIGHT1 + 1); + + // A horizontal motion in the range 4 * font space [f] < h < + // font space [f] will be treated as a kern that is not + // indicated in the printouts that DVItype produces between + // brackets. We allow a larger space in the negative + // direction than in the positive one, because TEX makes + // comparatively large backspaces when it positions + // accents. (comments stolen from the source of dvitype) + if ((is_vfmacro == false) && + (currinf.fontp != 0) && + ((RRtmp >= currinf.fontp->scaled_size_in_DVI_units/6) || (RRtmp <= -4*(currinf.fontp->scaled_size_in_DVI_units/6))) && + (currentlyDrawnPage->textBoxList.size() > 0)) + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += ' '; + currinf.data.dvi_h += ((long) (RRtmp * current_dimconv)); + break; + + case W1: + case W2: + case W3: + case W4: + WWtmp = readINT(ch - W0); + currinf.data.w = ((long) (WWtmp * current_dimconv)); + case W0: + if ((is_vfmacro == false) && + (currinf.fontp != 0) && + ((WWtmp >= currinf.fontp->scaled_size_in_DVI_units/6) || (WWtmp <= -4*(currinf.fontp->scaled_size_in_DVI_units/6))) && + (currentlyDrawnPage->textBoxList.size() > 0) ) + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += ' '; + currinf.data.dvi_h += currinf.data.w; + break; + + case X1: + case X2: + case X3: + case X4: + XXtmp = readINT(ch - X0); + currinf.data.x = ((long) (XXtmp * current_dimconv)); + case X0: + if ((is_vfmacro == false) && + (currinf.fontp != 0) && + ((XXtmp >= currinf.fontp->scaled_size_in_DVI_units/6) || (XXtmp <= -4*(currinf.fontp->scaled_size_in_DVI_units/6))) && + (currentlyDrawnPage->textBoxList.size() > 0)) + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += ' '; + currinf.data.dvi_h += currinf.data.x; + break; + + case DOWN1: + case DOWN2: + case DOWN3: + case DOWN4: + { + Q_INT32 DDtmp = readINT(ch - DOWN1 + 1); + if ((is_vfmacro == false) && + (currinf.fontp != 0) && + (abs(DDtmp) >= 5*(currinf.fontp->scaled_size_in_DVI_units/6)) && + (currentlyDrawnPage->textBoxList.size() > 0)) { + word_boundary_encountered = true; + line_boundary_encountered = true; + if (abs(DDtmp) >= 10*(currinf.fontp->scaled_size_in_DVI_units/6)) + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += '\n'; + } + currinf.data.dvi_v += ((long) (DDtmp * current_dimconv))/65536; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + } + break; + + case Y1: + case Y2: + case Y3: + case Y4: + YYtmp = readINT(ch - Y0); + currinf.data.y = ((long) (YYtmp * current_dimconv)); + case Y0: + if ((is_vfmacro == false) && + (currinf.fontp != 0) && + (abs(YYtmp) >= 5*(currinf.fontp->scaled_size_in_DVI_units/6)) && + (currentlyDrawnPage->textBoxList.size() > 0)) { + word_boundary_encountered = true; + line_boundary_encountered = true; + if (abs(YYtmp) >= 10*(currinf.fontp->scaled_size_in_DVI_units/6)) + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += '\n'; + } + currinf.data.dvi_v += currinf.data.y/65536; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + break; + + case Z1: + case Z2: + case Z3: + case Z4: + ZZtmp = readINT(ch - Z0); + currinf.data.z = ((long) (ZZtmp * current_dimconv)); + case Z0: + if ((is_vfmacro == false) && + (currinf.fontp != 0) && + (abs(ZZtmp) >= 5*(currinf.fontp->scaled_size_in_DVI_units/6)) && + (currentlyDrawnPage->textBoxList.size() > 0)) { + word_boundary_encountered = true; + line_boundary_encountered = true; + if (abs(ZZtmp) >= 10*(currinf.fontp->scaled_size_in_DVI_units/6)) + currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += '\n'; + } + currinf.data.dvi_v += currinf.data.z/65536; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + break; + + case FNT1: + case FNT2: + case FNT3: + currinf.fontp = currinf.fonttable->find(readUINT(ch - FNT1 + 1)); + if (currinf.fontp == NULL) { + errorMsg = i18n("The DVI code referred to a font which was not previously defined."); + return; + } + currinf.set_char_p = currinf.fontp->set_char_p; + break; + + case FNT4: + currinf.fontp = currinf.fonttable->find(readINT(ch - FNT1 + 1)); + if (currinf.fontp == NULL) { + errorMsg = i18n("The DVI code referred to a font which was not previously defined."); + return; + } + currinf.set_char_p = currinf.fontp->set_char_p; + break; + + case XXX1: + case XXX2: + case XXX3: + case XXX4: + if (is_vfmacro == false) { + word_boundary_encountered = true; + line_boundary_encountered = true; + } + a = readUINT(ch - XXX1 + 1); + if (a > 0) { + char *cmd = new char[a+1]; + strncpy(cmd, (char *)command_pointer, a); + command_pointer += a; + cmd[a] = '\0'; + applicationDoSpecial(cmd); + delete [] cmd; + } + break; + + case FNTDEF1: + case FNTDEF2: + case FNTDEF3: + case FNTDEF4: + command_pointer += 12 + ch - FNTDEF1 + 1; + { + Q_UINT8 tempa = readUINT8(); + Q_UINT8 tempb = readUINT8(); + command_pointer += tempa + tempb; + } + break; + + case PRE: + case POST: + case POSTPOST: + errorMsg = i18n("An illegal command was encountered."); + return; + break; + + default: + errorMsg = i18n("The unknown op-code %1 was encountered.").arg(ch); + return; + } /* end switch*/ + } /* end else (ch not a SETCHAR or FNTNUM) */ + } /* end for */ +} + + +void dviRenderer::draw_page() +{ + // Reset a couple of variables + HTML_href = 0; + source_href = 0; + penWidth_in_mInch = 0.0; + + // Calling resize() here rather than clear() means that the memory + // taken up by the vector is not freed. This is faster than + // constantly allocating/freeing memory. + currentlyDrawnPage->textBoxList.resize(0); + + RenderedDviPagePixmap* currentDVIPage = dynamic_cast<RenderedDviPagePixmap*>(currentlyDrawnPage); + if (currentDVIPage) + { + currentDVIPage->sourceHyperLinkList.resize(0); + } + +#ifdef PERFORMANCE_MEASUREMENT + // If this is the first time a page is drawn, take the time that is + // elapsed till the kdvi_multipage was constructed, and print + // it. Set the flag so that is message will not be printed again. + if (performanceFlag == 0) { + kdDebug(4300) << "Time elapsed till the first page is drawn: " << performanceTimer.restart() << "ms" << endl; + performanceFlag = 1; + } +#endif + + +#ifdef DEBUG_RENDER + kdDebug(4300) <<"draw_page" << endl; +#endif + + if (!accessibilityBackground) + { + foreGroundPainter->fillRect( foreGroundPainter->viewport(), PS_interface->getBackgroundColor(current_page) ); + } + else + { + // In accessiblity mode use the custom background color + foreGroundPainter->fillRect( foreGroundPainter->viewport(), accessibilityBackgroundColor ); + } + + // Render the PostScript background, if there is one. + if (_postscript) + { + // In accessiblity mode use the custom background color + if (accessibilityBackground) + { + // Flag permanent is set to false because otherwise we would not be able to restore + // the original background color. + PS_interface->setBackgroundColor(current_page, accessibilityBackgroundColor, false); + } + else + PS_interface->restoreBackgroundColor(current_page); + + PS_interface->graphics(current_page, resolutionInDPI, dviFile->getMagnification(), foreGroundPainter); + } + + // Now really write the text + if (dviFile->page_offset.isEmpty() == true) + return; + if (current_page < dviFile->total_pages) { + command_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page]; + end_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page+1]; + } else + command_pointer = end_pointer = 0; + + memset((char *) &currinf.data, 0, sizeof(currinf.data)); + currinf.fonttable = &(dviFile->tn_table); + currinf._virtual = 0; + + double fontPixelPerDVIunit = dviFile->getCmPerDVIunit() * 1200.0/2.54; + + draw_part(65536.0*fontPixelPerDVIunit, false); + if (HTML_href != 0) { + delete HTML_href; + HTML_href = 0; + } + if (source_href != 0) { + delete source_href; + source_href = 0; + } +} diff --git a/kdvi/dviRenderer_export.cpp b/kdvi/dviRenderer_export.cpp new file mode 100644 index 00000000..4745bf18 --- /dev/null +++ b/kdvi/dviRenderer_export.cpp @@ -0,0 +1,391 @@ +// +// Class: dviRenderer +// Author: Stefan Kebekus +// +// (C) 2001-2004, Stefan Kebekus. +// +// Previewer for TeX DVI files. +// +// 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. +// +// Please report bugs or improvements, etc. via the "Report bug"-Menu +// of kdvi. + +#include <config.h> + +#include <kapplication.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <kio/job.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kprinter.h> +#include <kprocess.h> +#include <ktempfile.h> +#include <qlabel.h> +#include <qpainter.h> + +#include "dviRenderer.h" +#include "dviFile.h" +#include "fontprogress.h" +#include "infodialog.h" +#include "kdvi_multipage.h" + +extern QPainter foreGroundPaint; // QPainter used for text + + + +void dviRenderer::exportPDF() +{ + // It could perhaps happen that a kShellProcess, which runs an + // editor for inverse search, is still running. In that case, we + // ingore any further output of the editor by detaching the + // appropriate slots. The sigal "processExited", however, remains + // attached to the slow "exportCommand_terminated", which is smart + // enough to ignore the exit status of the editor if another command + // has been called meanwhile. See also the exportPS method. + if (proc != 0) { + // Make sure all further output of the programm is ignored + qApp->disconnect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)), 0, 0); + qApp->disconnect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), 0, 0); + proc = 0; + } + + // That sould also not happen. + if (dviFile == NULL) + return; + + // Is the dvipdfm-Programm available ?? + QStringList texList = QStringList::split(":", QString::fromLocal8Bit(getenv("PATH"))); + bool found = false; + for (QStringList::Iterator it=texList.begin(); it!=texList.end(); ++it) { + QString temp = (*it) + "/" + "dvipdfm"; + if (QFile::exists(temp)) { + found = true; + break; + } + } + if (found == false) { + KMessageBox::sorry(0, i18n("KDVI could not locate the program 'dvipdfm' on your computer. That program is " + "essential for the export function to work. You can, however, convert " + "the DVI-file to PDF using the print function of KDVI, but that will often " + "produce documents which print ok, but are of inferior quality if viewed in the " + "Acrobat Reader. It may be wise to upgrade to a more recent version of your " + "TeX distribution which includes the 'dvipdfm' program.\n" + "Hint to the perplexed system administrator: KDVI uses the shell's PATH variable " + "when looking for programs.")); + return; + } + + // Generate a suggestion for a reasonable file name + QString suggestedName = dviFile->filename; + suggestedName = suggestedName.left(suggestedName.find(".")) + ".pdf"; + + QString fileName = KFileDialog::getSaveFileName(suggestedName, i18n("*.pdf|Portable Document Format (*.pdf)"), parentWidget, i18n("Export File As")); + if (fileName.isEmpty()) + return; + QFileInfo finfo(fileName); + if (finfo.exists()) { + int r = KMessageBox::warningContinueCancel (parentWidget, i18n("The file %1\nexists. Do you want to overwrite that file?").arg(fileName), + i18n("Overwrite File"), i18n("Overwrite")); + if (r == KMessageBox::Cancel) + return; + } + + // Initialize the progress dialog + progress = new fontProgressDialog( QString::null, + i18n("Using dvipdfm to export the file to PDF"), + QString::null, + i18n("KDVI is currently using the external program 'dvipdfm' to " + "convert your DVI-file to PDF. Sometimes that can take " + "a while because dvipdfm needs to generate its own bitmap fonts " + "Please be patient."), + i18n("Waiting for dvipdfm to finish..."), + parentWidget, i18n("dvipdfm progress dialog"), false ); + if (progress != 0) { + progress->TextLabel2->setText( i18n("Please be patient") ); + progress->setTotalSteps( dviFile->total_pages ); + qApp->connect(progress, SIGNAL(finished()), this, SLOT(abortExternalProgramm())); + } + + proc = new KShellProcess(); + if (proc == 0) { + kdError(4300) << "Could not allocate ShellProcess for the dvipdfm command." << endl; + return; + } + qApp->disconnect( this, SIGNAL(mySignal()), 0, 0 ); + + qApp->connect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)), this, SLOT(dvips_output_receiver(KProcess *, char *, int))); + qApp->connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(dvips_output_receiver(KProcess *, char *, int))); + qApp->connect(proc, SIGNAL(processExited(KProcess *)), this, SLOT(dvips_terminated(KProcess *))); + + export_errorString = i18n("<qt>The external program 'dvipdf', which was used to export the file, reported an error. " + "You might wish to look at the <strong>document info dialog</strong> which you will " + "find in the File-Menu for a precise error report.</qt>") ; + info->clear(i18n("Export: %1 to PDF").arg(KShellProcess::quote(dviFile->filename))); + + proc->clearArguments(); + finfo.setFile(dviFile->filename); + *proc << QString("cd %1; dvipdfm").arg(KShellProcess::quote(finfo.dirPath(true))); + *proc << QString("-o %1").arg(KShellProcess::quote(fileName)); + *proc << KShellProcess::quote(dviFile->filename); + proc->closeStdin(); + if (proc->start(KProcess::NotifyOnExit, KProcess::AllOutput) == false) { + kdError(4300) << "dvipdfm failed to start" << endl; + return; + } + return; +} + + +void dviRenderer::exportPS(const QString& fname, const QString& options, KPrinter* printer) +{ + // Safety check. + if (dviFile->page_offset.isEmpty() == true) + return; + + // It could perhaps happen that a kShellProcess, which runs an + // editor for inverse search, is still running. In that case, we + // ingore any further output of the editor by detaching the + // appropriate slots. The sigal "processExited", however, remains + // attached to the slow "exportCommand_terminated", which is smart + // enough to ignore the exit status of the editor if another command + // has been called meanwhile. See also the exportPDF method. + if (proc != 0) { + qApp->disconnect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)), 0, 0); + qApp->disconnect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), 0, 0); + proc = 0; + } + + // That sould also not happen. + if (dviFile == NULL) + return; + + if (dviFile->numberOfExternalNONPSFiles != 0) { + KMessageBox::sorry( parentWidget, + i18n("<qt><P>This DVI file refers to external graphic files which are not in PostScript format, and cannot be handled by the " + "<strong>dvips</strong> program that KDVI uses interally to print or to export to PostScript. The functionality that " + "you require is therefore unavailable in this version of KDVI.</p>" + "<p>As a workaround, you can use the <strong>File/Export As</strong>-Menu to save this file in PDF format, and then use " + "a PDF viewer.</p>" + "<p>The author of KDVI apologizes for the inconvenience. If enough users complain, the missing functionality might later " + "be added.</p></qt>") , + i18n("Functionality Unavailable")); + return; + } + + QString fileName; + if (fname.isEmpty()) { + // Generate a suggestion for a reasonable file name + QString suggestedName = dviFile->filename; + suggestedName = suggestedName.left(suggestedName.find(".")) + ".ps"; + + fileName = KFileDialog::getSaveFileName(suggestedName, i18n("*.ps|PostScript (*.ps)"), parentWidget, i18n("Export File As")); + if (fileName.isEmpty()) + return; + QFileInfo finfo(fileName); + if (finfo.exists()) { + int r = KMessageBox::warningYesNo (parentWidget, i18n("The file %1\nexists. Do you want to overwrite that file?").arg(fileName), + i18n("Overwrite File")); + if (r == KMessageBox::No) + return; + } + } else + fileName = fname; + export_fileName = fileName; + export_printer = printer; + + // Initialize the progress dialog + progress = new fontProgressDialog( QString::null, + i18n("Using dvips to export the file to PostScript"), + QString::null, + i18n("KDVI is currently using the external program 'dvips' to " + "convert your DVI-file to PostScript. Sometimes that can take " + "a while because dvips needs to generate its own bitmap fonts " + "Please be patient."), + i18n("Waiting for dvips to finish..."), + parentWidget, i18n("dvips progress dialog"), false ); + if (progress != 0) { + progress->TextLabel2->setText( i18n("Please be patient") ); + progress->setTotalSteps( dviFile->total_pages ); + qApp->connect(progress, SIGNAL(finished()), this, SLOT(abortExternalProgramm())); + } + + // There is a major problem with dvips, at least 5.86 and lower: the + // arguments of the option "-pp" refer to TeX-pages, not to + // sequentially numbered pages. For instance "-pp 7" may refer to 3 + // or more pages: one page "VII" in the table of contents, a page + // "7" in the text body, and any number of pages "7" in various + // appendices, indices, bibliographies, and so forth. KDVI currently + // uses the following disgusting workaround: if the "options" + // variable is used, the DVI-file is copied to a temporary file, and + // all the page numbers are changed into a sequential ordering + // (using UNIX files, and taking manually care of CPU byte + // ordering). Finally, dvips is then called with the new file, and + // the file is afterwards deleted. Isn't that great? + + // A similar problem occurs with DVI files that contain page size + // information. On these files, dvips pointblank refuses to change + // the page orientation or set another page size. Thus, if the + // DVI-file does contain page size information, we remove that + // information first. + + // Sourcefile is the name of the DVI which is used by dvips, either + // the original file, or a temporary file with a new numbering. + QString sourceFileName = dviFile->filename; + if ((options.isEmpty() == false) || (dviFile->suggestedPageSize != 0) ) { + // Get a name for a temporary file. + KTempFile export_tmpFile; + export_tmpFileName = export_tmpFile.name(); + export_tmpFile.unlink(); + + sourceFileName = export_tmpFileName; + + fontPool fp; + dvifile newFile(dviFile, &fp); + + // Renumber pages + newFile.renumber(); + + // Remove any page size information from the file + Q_UINT16 currPageSav = current_page; + dvifile *dvsav = dviFile; + dviFile = &newFile; + errorMsg = QString::null; + + + for(current_page=0; current_page < newFile.total_pages; current_page++) { + if (current_page < newFile.total_pages) { + command_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page]; + end_pointer = dviFile->dvi_Data() + dviFile->page_offset[current_page+1]; + } else + command_pointer = end_pointer = 0; + + memset((char *) &currinf.data, 0, sizeof(currinf.data)); + currinf.fonttable = &(dviFile->tn_table); + currinf._virtual = NULL; + prescan(&dviRenderer::prescan_removePageSizeInfo); + } + + current_page = currPageSav; + dviFile = dvsav; + newFile.saveAs(sourceFileName); + } + + // Allocate and initialize the shell process. + proc = new KShellProcess(); + if (proc == 0) { + kdError(4300) << "Could not allocate ShellProcess for the dvips command." << endl; + return; + } + + qApp->connect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)), this, SLOT(dvips_output_receiver(KProcess *, char *, int))); + qApp->connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(dvips_output_receiver(KProcess *, char *, int))); + qApp->connect(proc, SIGNAL(processExited(KProcess *)), this, SLOT(dvips_terminated(KProcess *))); + export_errorString = i18n("<qt>The external program 'dvips', which was used to export the file, reported an error. " + "You might wish to look at the <strong>document info dialog</strong> which you will " + "find in the File-Menu for a precise error report.</qt>") ; + info->clear(i18n("Export: %1 to PostScript").arg(KShellProcess::quote(dviFile->filename))); + + proc->clearArguments(); + QFileInfo finfo(dviFile->filename); + *proc << QString("cd %1; dvips").arg(KShellProcess::quote(finfo.dirPath(true))); + if (printer == 0) + *proc << "-z"; // export Hyperlinks + if (options.isEmpty() == false) + *proc << options; + *proc << QString("%1").arg(KShellProcess::quote(sourceFileName)); + *proc << QString("-o %1").arg(KShellProcess::quote(fileName)); + proc->closeStdin(); + if (proc->start(KProcess::NotifyOnExit, KProcess::Stderr) == false) { + kdError(4300) << "dvips failed to start" << endl; + return; + } + return; +} + + +void dviRenderer::dvips_output_receiver(KProcess *, char *buffer, int buflen) +{ + // Paranoia. + if (buflen < 0) + return; + QString op = QString::fromLocal8Bit(buffer, buflen); + + info->outputReceiver(op); + if (progress != 0) + progress->show(); +} + + +void dviRenderer::dvips_terminated(KProcess *sproc) +{ + // Give an error message from the message string. However, if the + // sproc is not the "current external process of interest", i.e. not + // the LAST external program that was started by the user, then the + // export_errorString, does not correspond to sproc. In that case, + // we ingore the return status silently. + if ((proc == sproc) && (sproc->normalExit() == true) && (sproc->exitStatus() != 0)) + KMessageBox::error( parentWidget, export_errorString ); + + if (export_printer != 0) + export_printer->printFiles( QStringList(export_fileName), true ); + + // Kill and delete the remaining process, delete the printer, etc. + abortExternalProgramm(); +} + + +void dviRenderer::editorCommand_terminated(KProcess *sproc) +{ + // Give an error message from the message string. However, if the + // sproc is not the "current external process of interest", i.e. not + // the LAST external program that was started by the user, then the + // export_errorString, does not correspond to sproc. In that case, + // we ingore the return status silently. + if ((proc == sproc) && (sproc->normalExit() == true) && (sproc->exitStatus() != 0)) + KMessageBox::error( parentWidget, export_errorString ); + + // Let's hope that this is not all too nasty... killing a + // KShellProcess from a slot that was called from the KShellProcess + // itself. Until now, there weren't any problems. + + // Perhaps it was a bad idea, after all. + //@@@@ delete sproc; +} + + +void dviRenderer::abortExternalProgramm() +{ + delete proc; // Deleting the KProcess kills the child. + proc = 0; + + if (export_tmpFileName.isEmpty() != true) { + unlink(QFile::encodeName(export_tmpFileName)); // That should delete the file. + export_tmpFileName = ""; + } + + if (progress != 0) { + progress->hide(); + delete progress; + progress = 0; + } + + delete export_printer; + export_printer = 0; + export_fileName = ""; +} diff --git a/kdvi/dviRenderer_prescan.cpp b/kdvi/dviRenderer_prescan.cpp new file mode 100644 index 00000000..59bdeb25 --- /dev/null +++ b/kdvi/dviRenderer_prescan.cpp @@ -0,0 +1,789 @@ +// dviRenderer_prescan.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003--2004 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> + +#include "dviRenderer.h" +#include "dvi.h" +#include "dviFile.h" +#include "fontpool.h" +#include "kdvi_multipage.h" +#include "performanceMeasurement.h" +#include "prebookmark.h" +#include "psgs.h" +#include "TeXFont.h" +#include "xdvi.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kmimetype.h> +#include <kprocess.h> +#include <kprocio.h> +#include <kprogress.h> +#include <qapplication.h> +#include <qbitmap.h> +#include <qdir.h> +#include <qfileinfo.h> +#include <qimage.h> +#include <qpainter.h> +#include <qpaintdevice.h> + + +extern QPainter foreGroundPaint; +extern void parse_special_argument(const QString& strg, const char* argument_name, int* variable); + + +//#define DEBUG_PRESCAN + + +void dviRenderer::prescan_embedPS(char *cp, Q_UINT8 *beginningOfSpecialCommand) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "dviRenderer::prescan_embedPS( cp = " << cp << " ) " << endl; +#endif + + // Encapsulated Postscript File + if (strncasecmp(cp, "PSfile=", 7) != 0) + return; + + QString command(cp+7); + + QString include_command = command.simplifyWhiteSpace(); + + // The line is supposed to start with "..ile=", and then comes the + // filename. Figure out what the filename is and stow it away. Of + // course, this does not work if the filename contains spaces + // (already the simplifyWhiteSpace() above is wrong). If you have + // files like this, go away. + QString EPSfilename = include_command; + EPSfilename.truncate(EPSfilename.find(' ')); + + // Strip enclosing quotation marks which are included by some LaTeX + // macro packages (but not by others). This probably means that + // graphic files are no longer found if the filename really does + // contain quotes, but we don't really care that much. + if ((EPSfilename.at(0) == '\"') && (EPSfilename.at(EPSfilename.length()-1) == '\"')) + EPSfilename = EPSfilename.mid(1,EPSfilename.length()-2); + + // If the file is neither a PostScript not a PDF file, we exit here. + // The graphic file is later read when the page is rendered. + KMimeType::Ptr const mime_type = + KMimeType::findByFileContent(EPSfilename); + QString const & mime_type_name = mime_type->name(); + + bool const is_ps_file = (mime_type_name == "application/postscript" || + mime_type_name == "image/x-eps"); + bool const is_pdf_file = (!is_ps_file && + mime_type_name == "application/pdf"); + if (!(is_ps_file || is_pdf_file)) + return; + + QString originalFName = EPSfilename; + + embedPS_progress->setLabel(i18n("Embedding %1").arg(EPSfilename)); + qApp->processEvents(); + + + // Now locate the Gfx file on the hard disk... + EPSfilename = ghostscript_interface::locateEPSfile(EPSfilename, baseURL); + + // If the EPSfilename really points to a PDF file, convert that file now. + if (is_pdf_file) + EPSfilename = dviFile->convertPDFtoPS(EPSfilename); + + if (!QFile::exists(EPSfilename)) { + // Find the number of the page + Q_UINT32 currentOffset = beginningOfSpecialCommand - dviFile->dvi_Data(); + Q_UINT16 page; + for(page=0; page < dviFile->total_pages; page++) + if ((dviFile->page_offset[page] <= currentOffset) && (currentOffset <= dviFile->page_offset[page+1])) + break; + errorMsg += i18n("Page %1: The PostScript file <strong>%2</strong> could not be found.<br>").arg(page+1).arg(originalFName); + embedPS_progress->progressBar()->advance(1); + qApp->processEvents(); + return; + } + + // Now parse the arguments. + int llx = 0; + int lly = 0; + int urx = 0; + int ury = 0; + int rwi = 0; + int rhi = 0; + int angle = 0; + + // just to avoid ambiguities; the filename could contain keywords + include_command = include_command.mid(include_command.find(' ')); + + parse_special_argument(include_command, "llx=", &llx); + parse_special_argument(include_command, "lly=", &lly); + parse_special_argument(include_command, "urx=", &urx); + parse_special_argument(include_command, "ury=", &ury); + parse_special_argument(include_command, "rwi=", &rwi); + parse_special_argument(include_command, "rhi=", &rhi); + parse_special_argument(include_command, "angle=", &angle); + + int clip=include_command.find(" clip"); // -1 if clip keyword is not present, >= 0 otherwise + + // Generate the PostScript commands to be included + QString PS = QString("ps: @beginspecial %1 @llx %2 @lly %3 @urx %4 @ury").arg(llx).arg(lly).arg(urx).arg(ury); + if (rwi != 0) + PS.append( QString(" %1 @rwi").arg(rwi) ); + if (rhi != 0) + PS.append( QString(" %1 @rhi").arg(rhi) ); + if (angle != 0) + PS.append( QString(" %1 @angle").arg(angle) ); + if (clip != -1) + PS.append(" @clip"); + PS.append( " @setspecial\n" ); + + QFile file( EPSfilename ); + if ( file.open( IO_ReadOnly ) ) { + QTextStream stream( &file ); + while ( !stream.atEnd() ) { + PS += stream.readLine().section( '%', 0, 0); + PS += "\n"; + } + file.close(); + } + PS.append( "@endspecial" ); + PS = PS.simplifyWhiteSpace(); + + + _isModified = true; + Q_UINT32 lengthOfOldSpecial = command_pointer - beginningOfSpecialCommand; + Q_UINT32 lengthOfNewSpecial = PS.length()+5; + + QMemArray<Q_UINT8> newDVI(dviFile->size_of_file + lengthOfNewSpecial-lengthOfOldSpecial); + + Q_UINT8 *commandPtrSav = command_pointer; + Q_UINT8 *endPtrSav = end_pointer; + end_pointer = newDVI.data() + dviFile->size_of_file + lengthOfNewSpecial-lengthOfOldSpecial; + memcpy(newDVI.data(), dviFile->dvi_Data(), beginningOfSpecialCommand-dviFile->dvi_Data()); + command_pointer = newDVI.data()+(beginningOfSpecialCommand-dviFile->dvi_Data()); + command_pointer[0] = XXX4; + command_pointer++; + writeUINT32(PS.length()); + memcpy(newDVI.data()+(beginningOfSpecialCommand-dviFile->dvi_Data())+5, PS.latin1(), PS.length() ); + memcpy(newDVI.data()+(beginningOfSpecialCommand-dviFile->dvi_Data())+lengthOfNewSpecial, beginningOfSpecialCommand+lengthOfOldSpecial, + dviFile->size_of_file-(beginningOfSpecialCommand-dviFile->dvi_Data())-lengthOfOldSpecial ); + + // Adjust page pointers in the DVI file + dviFile->size_of_file = dviFile->size_of_file + lengthOfNewSpecial-lengthOfOldSpecial; + end_pointer = newDVI.data() + dviFile->size_of_file; + Q_UINT32 currentOffset = beginningOfSpecialCommand-dviFile->dvi_Data(); + for(Q_UINT16 i=0; i < dviFile->total_pages; i++) { + if (dviFile->page_offset[i] > currentOffset) { + dviFile->page_offset[i] = dviFile->page_offset[i] + lengthOfNewSpecial-lengthOfOldSpecial; + command_pointer = dviFile->page_offset[i] + newDVI.data() + 4*10 + 1; + Q_UINT32 a = readUINT32(); + if (a > currentOffset) { + a = a + lengthOfNewSpecial-lengthOfOldSpecial; + command_pointer = dviFile->page_offset[i] + newDVI.data() + 4*10 + 1; + writeUINT32(a); + } + } + } + + + dviFile->beginning_of_postamble = dviFile->beginning_of_postamble + lengthOfNewSpecial - lengthOfOldSpecial; + dviFile->page_offset[dviFile->total_pages] = dviFile->beginning_of_postamble; + + command_pointer = newDVI.data() + dviFile->beginning_of_postamble + 1; + Q_UINT32 a = readUINT32(); + if (a > currentOffset) { + a = a + lengthOfNewSpecial - lengthOfOldSpecial; + command_pointer = newDVI.data() + dviFile->beginning_of_postamble + 1; + writeUINT32(a); + } + + command_pointer = newDVI.data() + dviFile->size_of_file - 1; + while((*command_pointer == TRAILER) && (command_pointer > newDVI.data())) + command_pointer--; + command_pointer -= 4; + writeUINT32(dviFile->beginning_of_postamble); + command_pointer -= 4; + + command_pointer = commandPtrSav; + end_pointer = endPtrSav; + + // Modify all pointers to point to the newly allocated memory + command_pointer = newDVI.data() + (command_pointer - dviFile->dvi_Data()) + lengthOfNewSpecial-lengthOfOldSpecial; + end_pointer = newDVI.data() + (end_pointer - dviFile->dvi_Data()) + lengthOfNewSpecial-lengthOfOldSpecial; + + dviFile->setNewData(newDVI); + + embedPS_progress->progressBar()->advance(1); + qApp->processEvents(); + return; +} + + +void dviRenderer::prescan_removePageSizeInfo(char *cp, Q_UINT8 *beginningOfSpecialCommand) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "dviRenderer::prescan_embedPS( cp = " << cp << " ) " << endl; +#endif + + // Encapsulated Postscript File + if (strncasecmp(cp, "papersize=", 10) != 0) + return; + + for (Q_UINT8 *ptr=beginningOfSpecialCommand; ptr<command_pointer; ptr++) + *ptr = NOP; +} + + +void dviRenderer::prescan_ParsePapersizeSpecial(const QString& _cp) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "Papersize-Special : papersize" << cp << endl; +#endif + + QString cp = _cp.simplifyWhiteSpace(); + + if (cp[0] == '=') { + cp = cp.mid(1); + dviFile->suggestedPageSize = new pageSize; + dviFile->suggestedPageSize->setPageSize(cp); + } else + printErrorMsgForSpecials(i18n("The papersize data '%1' could not be parsed.").arg(cp)); + + return; +} + + +void dviRenderer::prescan_ParseBackgroundSpecial(const QString& cp) +{ + QColor col = parseColorSpecification(cp.stripWhiteSpace()); + if (col.isValid()) + for(Q_UINT16 page=current_page; page < dviFile->total_pages; page++) + PS_interface->setBackgroundColor(page, col); + return; +} + + +void dviRenderer::prescan_ParseHTMLAnchorSpecial(const QString& _cp) +{ + QString cp = _cp; + cp.truncate(cp.find('"')); + Length l; + l.setLength_in_inch(currinf.data.dvi_v/(resolutionInDPI*shrinkfactor)); + anchorList[cp] = Anchor(current_page+1, l); +} + + +void dviRenderer::prescan_ParsePSHeaderSpecial(const QString& cp) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "PostScript-special, header " << cp.latin1() << endl; +#endif + + QString _file = cp; + + // If the file is not found in the current directory, use kpsewhich + // to find it. + if (!QFile::exists(_file)) { + // Otherwise, use kpsewhich to find the eps file. + KProcIO proc; + proc << "kpsewhich" << cp; + proc.start(KProcess::Block); + proc.readln(_file); + } + + if (QFile::exists(_file)) + PS_interface->PostScriptHeaderString->append( QString(" (%1) run\n").arg(_file) ); +} + + +void dviRenderer::prescan_ParsePSBangSpecial(const QString& cp) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "PostScript-special, literal header " << cp.latin1() << endl; +#endif + + PS_interface->PostScriptHeaderString->append( " @defspecial \n" ); + PS_interface->PostScriptHeaderString->append( cp ); + PS_interface->PostScriptHeaderString->append( " @fedspecial \n" ); +} + + +void dviRenderer::prescan_ParsePSQuoteSpecial(const QString& cp) +{ +#ifdef DEBUG_PRESCAN + kdError(4300) << "PostScript-special, literal PostScript " << cp.latin1() << endl; +#endif + + double PS_H = (currinf.data.dvi_h*300.0)/(65536*1200)-300; + double PS_V = (currinf.data.dvi_v*300.0)/1200 - 300; + PostScriptOutPutString->append( QString(" %1 %2 moveto\n").arg(PS_H).arg(PS_V) ); + PostScriptOutPutString->append( " @beginspecial @setspecial \n" ); + PostScriptOutPutString->append( cp ); + PostScriptOutPutString->append( " @endspecial \n" ); +} + + +void dviRenderer::prescan_ParsePSSpecial(const QString& cp) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "PostScript-special, direct PostScript " << cp << endl; +#endif + + // Unfortunately, in some TeX distribution the hyperref package uses + // the dvips driver by default, rather than the hypertex driver. As + // a result, the DVI files produced are full of PostScript that + // specifies links and anchors, and KDVI would call the ghostscript + // interpreter for every page which makes it really slow. This is a + // major nuisance, so that we try to filter and interpret the + // hypertex generated PostScript here. + if (cp.startsWith("ps:SDict begin")) { + // We suspect this may be hyperref generated nonsense. Let's check + // for some known code that hyperref generates. + if (cp == "ps:SDict begin H.S end") + return; // start of hyperref rectangle + if (cp == "ps:SDict begin H.R end") + return; // end of hyperref rectangle + if (cp.endsWith("H.A end")) + return; // end of hyperref anchor + if (cp.endsWith("H.L end")) + return; // end of hyperref link + if (cp.startsWith("ps:SDict begin /product where{pop product(Distiller)")) + return; // hyperref tries to work around Distiller bug + if (cp.startsWith("ps:SDict begin [") && cp.endsWith(" pdfmark end")) { // hyperref definition of link/anchor/bookmark/etc + if (cp.contains("/DEST")) { // The PostScript code defines an anchor + QString anchorName = cp.section('(', 1, 1).section(')', 0, 0); + Length l; + l.setLength_in_inch(currinf.data.dvi_v/(resolutionInDPI*shrinkfactor)); + anchorList[anchorName] = Anchor(current_page+1, l); + } + // The PostScript code defines a bookmark + if (cp.contains("/Dest") && cp.contains("/Title")) + prebookmarks.append(PreBookmark(PDFencodingToQString(cp.section('(', 2, 2).section(')', 0, 0)), + cp.section('(', 1, 1).section(')', 0, 0), + cp.section('-', 1, 1).section(' ', 0, 0).toUInt() + )); + return; + } + } + + double PS_H = (currinf.data.dvi_h*300.0)/(65536*1200)-300; + double PS_V = (currinf.data.dvi_v*300.0)/1200 - 300; + + if (cp.find("ps::[begin]", 0, false) == 0) { + PostScriptOutPutString->append( QString(" %1 %2 moveto\n").arg(PS_H).arg(PS_V) ); + PostScriptOutPutString->append( QString(" %1\n").arg(cp.mid(11)) ); + } else { + if (cp.find("ps::[end]", 0, false) == 0) { + PostScriptOutPutString->append( QString(" %1\n").arg(cp.mid(9)) ); + } else { + if (cp.find("ps::", 0, false) == 0) { + PostScriptOutPutString->append( QString(" %1\n").arg(cp.mid(4)) ); + } else { + PostScriptOutPutString->append( QString(" %1 %2 moveto\n").arg(PS_H).arg(PS_V) ); + PostScriptOutPutString->append( QString(" %1\n").arg(cp.mid(3)) ); + } + } + } +} + + +void dviRenderer::prescan_ParsePSFileSpecial(const QString& cp) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "epsf-special: psfile=" << cp <<endl; +#endif + + QString include_command = cp.simplifyWhiteSpace(); + + // The line is supposed to start with "..ile=", and then comes the + // filename. Figure out what the filename is and stow it away. Of + // course, this does not work if the filename contains spaces + // (already the simplifyWhiteSpace() above is wrong). If you have + // files like this, go away. + QString EPSfilename = include_command; + EPSfilename.truncate(EPSfilename.find(' ')); + + // Strip enclosing quotation marks which are included by some LaTeX + // macro packages (but not by others). This probably means that + // graphic files are no longer found if the filename really does + // contain quotes, but we don't really care that much. + if ((EPSfilename.at(0) == '\"') && (EPSfilename.at(EPSfilename.length()-1) == '\"')) { + EPSfilename = EPSfilename.mid(1,EPSfilename.length()-2); + } + + // If the file name ends in 'png', 'gif', 'jpg' or 'jpeg', we assume + // that this is NOT a PostScript file, and we exit here. + QString ending = EPSfilename.section('.', -1).lower(); + if ((ending == "png") || (ending == "gif") || (ending == "jpg") || (ending == "jpeg")) { + dviFile->numberOfExternalNONPSFiles++; + return; + } + + // Now assume that the graphics file *is* a PostScript file + dviFile->numberOfExternalPSFiles++; + + // Now locate the Gfx file on the hard disk... + EPSfilename = ghostscript_interface::locateEPSfile(EPSfilename, baseURL); + + // If the EPSfilename really points to a PDF file, convert that file now. + if (ending == "pdf") + EPSfilename = dviFile->convertPDFtoPS(EPSfilename); + + // Now parse the arguments. + int llx = 0; + int lly = 0; + int urx = 0; + int ury = 0; + int rwi = 0; + int rhi = 0; + int angle = 0; + + // just to avoid ambiguities; the filename could contain keywords + include_command = include_command.mid(include_command.find(' ')); + + parse_special_argument(include_command, "llx=", &llx); + parse_special_argument(include_command, "lly=", &lly); + parse_special_argument(include_command, "urx=", &urx); + parse_special_argument(include_command, "ury=", &ury); + parse_special_argument(include_command, "rwi=", &rwi); + parse_special_argument(include_command, "rhi=", &rhi); + parse_special_argument(include_command, "angle=", &angle); + + int clip=include_command.find(" clip"); // -1 if clip keyword is not present, >= 0 otherwise + + if (QFile::exists(EPSfilename)) { + double PS_H = (currinf.data.dvi_h*300.0)/(65536*1200)-300; + double PS_V = (currinf.data.dvi_v*300.0)/1200 - 300; + PostScriptOutPutString->append( QString(" %1 %2 moveto\n").arg(PS_H).arg(PS_V) ); + PostScriptOutPutString->append( "@beginspecial " ); + PostScriptOutPutString->append( QString(" %1 @llx").arg(llx) ); + PostScriptOutPutString->append( QString(" %1 @lly").arg(lly) ); + PostScriptOutPutString->append( QString(" %1 @urx").arg(urx) ); + PostScriptOutPutString->append( QString(" %1 @ury").arg(ury) ); + if (rwi != 0) + PostScriptOutPutString->append( QString(" %1 @rwi").arg(rwi) ); + if (rhi != 0) + PostScriptOutPutString->append( QString(" %1 @rhi").arg(rhi) ); + if (angle != 0) + PostScriptOutPutString->append( QString(" %1 @angle").arg(angle) ); + if (clip != -1) + PostScriptOutPutString->append(" @clip"); + PostScriptOutPutString->append( " @setspecial \n" ); + PostScriptOutPutString->append( QString(" (%1) run\n").arg(EPSfilename) ); + PostScriptOutPutString->append( "@endspecial \n" ); + } + + return; +} + + +void dviRenderer::prescan_ParseSourceSpecial(const QString& cp) +{ + // if no rendering takes place, i.e. when the DVI file is first + // loaded, generate a DVI_SourceFileAnchor. These anchors are used + // in forward search, i.e. to relate references line + // "src:123file.tex" to positions in the DVI file + + // extract the file name and the numeral part from the string + Q_UINT32 j; + for(j=0;j<cp.length();j++) + if (!cp.at(j).isNumber()) + break; + Q_UINT32 sourceLineNumber = cp.left(j).toUInt(); + QFileInfo fi1(dviFile->filename); + QString sourceFileName = QFileInfo(fi1.dir(), cp.mid(j).stripWhiteSpace()).absFilePath(); + Length l; + l.setLength_in_inch(currinf.data.dvi_v/(resolutionInDPI*shrinkfactor)); + DVI_SourceFileAnchor sfa(sourceFileName, sourceLineNumber, current_page+1, l); + sourceHyperLinkAnchors.push_back(sfa); +} + + +void dviRenderer::prescan_parseSpecials(char *cp, Q_UINT8 *) +{ + QString special_command(cp); + + // Now to those specials which are only interpreted during the + // prescan phase, and NOT during rendering. + + // PaperSize special + if (strncasecmp(cp, "papersize", 9) == 0) { + prescan_ParsePapersizeSpecial(special_command.mid(9)); + return; + } + + // color special for background color + if (strncasecmp(cp, "background", 10) == 0) { + prescan_ParseBackgroundSpecial(special_command.mid(10)); + return; + } + + // HTML anchor special + if (strncasecmp(cp, "html:<A name=", 13) == 0) { + prescan_ParseHTMLAnchorSpecial(special_command.mid(14)); + return; + } + + // Postscript Header File + if (strncasecmp(cp, "header=", 7) == 0) { + prescan_ParsePSHeaderSpecial(special_command.mid(7)); + return; + } + + // Literal Postscript Header + if (cp[0] == '!') { + prescan_ParsePSBangSpecial(special_command.mid(1)); + return; + } + + // Literal Postscript inclusion + if (cp[0] == '"') { + prescan_ParsePSQuoteSpecial(special_command.mid(1)); + return; + } + + // PS-Postscript inclusion + if (strncasecmp(cp, "ps:", 3) == 0) { + prescan_ParsePSSpecial(special_command); + return; + } + + // Encapsulated Postscript File + if (strncasecmp(cp, "PSfile=", 7) == 0) { + prescan_ParsePSFileSpecial(special_command.mid(7)); + return; + } + + // source special + if (strncasecmp(cp, "src:", 4) == 0) { + prescan_ParseSourceSpecial(special_command.mid(4)); + return; + } + + // Finally there are those special commands which must be considered + // both during rendering and during the pre-scan phase + + // HTML anchor end + if (strncasecmp(cp, "html:</A>", 9) == 0) { + html_anchor_end(); + return; + } + + return; +} + + +void dviRenderer::prescan_setChar(unsigned int ch) +{ + TeXFontDefinition *fontp = currinf.fontp; + if (fontp == NULL) + return; + + if (currinf.set_char_p == &dviRenderer::set_char) { + glyph *g = ((TeXFont *)(currinf.fontp->font))->getGlyph(ch, true, globalColor); + if (g == NULL) + return; + currinf.data.dvi_h += (int)(currinf.fontp->scaled_size_in_DVI_units * dviFile->getCmPerDVIunit() * + (1200.0 / 2.54)/16.0 * g->dvi_advance_in_units_of_design_size_by_2e20 + 0.5); + return; + } + + if (currinf.set_char_p == &dviRenderer::set_vf_char) { + macro *m = &currinf.fontp->macrotable[ch]; + if (m->pos == NULL) + return; + currinf.data.dvi_h += (int)(currinf.fontp->scaled_size_in_DVI_units * dviFile->getCmPerDVIunit() * + (1200.0 / 2.54)/16.0 * m->dvi_advance_in_units_of_design_size_by_2e20 + 0.5); + return; + } +} + + +void dviRenderer::prescan(parseSpecials specialParser) +{ +#ifdef DEBUG_PRESCAN + kdDebug(4300) << "dviRenderer::prescan( ... )" << endl; +#endif + + if (resolutionInDPI == 0.0) + setResolution(100); + + Q_INT32 RRtmp=0, WWtmp=0, XXtmp=0, YYtmp=0, ZZtmp=0; + Q_UINT8 ch; + double fontPixelPerDVIunit = dviFile->getCmPerDVIunit() * 1200.0/2.54; + + stack.clear(); + + currinf.fontp = NULL; + currinf.set_char_p = &dviRenderer::set_no_char; + + for (;;) { + ch = readUINT8(); + + if (ch <= (unsigned char) (SETCHAR0 + 127)) { + prescan_setChar(ch); + continue; + } + + if (FNTNUM0 <= ch && ch <= (unsigned char) (FNTNUM0 + 63)) { + currinf.fontp = currinf.fonttable->find(ch - FNTNUM0); + if (currinf.fontp == NULL) { + errorMsg = i18n("The DVI code referred to font #%1, which was not previously defined.").arg(ch - FNTNUM0); + return; + } + currinf.set_char_p = currinf.fontp->set_char_p; + continue; + } + + + Q_INT32 a, b; + + switch (ch) { + case SET1: + prescan_setChar(readUINT8()); + break; + + case SETRULE: + /* Be careful, dvicopy outputs rules with height = + 0x80000000. We don't want any SIGFPE here. */ + a = readUINT32(); + b = readUINT32(); + b = ((long) (b * 65536.0*fontPixelPerDVIunit)); + currinf.data.dvi_h += b; + break; + + case PUTRULE: + a = readUINT32(); + b = readUINT32(); + break; + + case PUT1: + case NOP: + break; + + case BOP: + command_pointer += 11 * 4; + currinf.data.dvi_h = 1200 << 16; // Reminder: DVI-coordinates start at (1",1") from top of page + currinf.data.dvi_v = 1200; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + currinf.data.w = currinf.data.x = currinf.data.y = currinf.data.z = 0; + break; + + case PUSH: + stack.push(currinf.data); + break; + + case POP: + if (stack.isEmpty()) + return; + else + currinf.data = stack.pop(); + break; + + case RIGHT1: + case RIGHT2: + case RIGHT3: + case RIGHT4: + RRtmp = readINT(ch - RIGHT1 + 1); + currinf.data.dvi_h += ((long) (RRtmp * 65536.0*fontPixelPerDVIunit)); + break; + + case W1: + case W2: + case W3: + case W4: + WWtmp = readINT(ch - W0); + currinf.data.w = ((long) (WWtmp * 65536.0*fontPixelPerDVIunit)); + case W0: + currinf.data.dvi_h += currinf.data.w; + break; + + case X1: + case X2: + case X3: + case X4: + XXtmp = readINT(ch - X0); + currinf.data.x = ((long) (XXtmp * 65536.0*fontPixelPerDVIunit)); + case X0: + currinf.data.dvi_h += currinf.data.x; + break; + + case DOWN1: + case DOWN2: + case DOWN3: + case DOWN4: + { + Q_INT32 DDtmp = readINT(ch - DOWN1 + 1); + currinf.data.dvi_v += ((long) (DDtmp * 65536.0*fontPixelPerDVIunit))/65536; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + } + break; + + case Y1: + case Y2: + case Y3: + case Y4: + YYtmp = readINT(ch - Y0); + currinf.data.y = ((long) (YYtmp * 65536.0*fontPixelPerDVIunit)); + case Y0: + currinf.data.dvi_v += currinf.data.y/65536; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + break; + + case Z1: + case Z2: + case Z3: + case Z4: + ZZtmp = readINT(ch - Z0); + currinf.data.z = ((long) (ZZtmp * 65536.0*fontPixelPerDVIunit)); + case Z0: + currinf.data.dvi_v += currinf.data.z/65536; + currinf.data.pxl_v = int(currinf.data.dvi_v/shrinkfactor); + break; + + case FNT1: + case FNT2: + case FNT3: + case FNT4: + currinf.fontp = currinf.fonttable->find(readUINT(ch - FNT1 + 1)); + if (currinf.fontp == NULL) + return; + currinf.set_char_p = currinf.fontp->set_char_p; + break; + + case XXX1: + case XXX2: + case XXX3: + case XXX4: + { + Q_UINT8 *beginningOfSpecialCommand = command_pointer-1; + a = readUINT(ch - XXX1 + 1); + if (a > 0) { + char *cmd = new char[a+1]; + strncpy(cmd, (char *)command_pointer, a); + command_pointer += a; + cmd[a] = '\0'; + (this->*specialParser)(cmd, beginningOfSpecialCommand); + delete [] cmd; + } + } + break; + + case FNTDEF1: + case FNTDEF2: + case FNTDEF3: + case FNTDEF4: + command_pointer += 12 + ch - FNTDEF1 + 1; + command_pointer += readUINT8() + readUINT8(); + break; + + default: + return; + } /* end switch */ + } /* end for */ +} diff --git a/kdvi/dviWidget.cpp b/kdvi/dviWidget.cpp new file mode 100644 index 00000000..8475af92 --- /dev/null +++ b/kdvi/dviWidget.cpp @@ -0,0 +1,123 @@ +// +// Class: DVIWidget +// +// Widget for displaying TeX DVI files. +// Part of KDVI- A previewer for TeX DVI files. +// +// (C) 2004 Wilfried Huss, Stefan Kebekus +// Distributed under the GPL +// + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "dviWidget.h" + +#include "documentPageCache.h" +#include "documentWidget.h" +#include "hyperlink.h" +#include "pageView.h" +#include "renderedDviPagePixmap.h" +#include "selection.h" + +DVIWidget::DVIWidget(QWidget* parent, PageView* sv, DocumentPageCache* cache, const char* name) + : DocumentWidget(parent, sv, cache, name) +{ +} + + +void DVIWidget::mousePressEvent(QMouseEvent* e) +{ + // pageNr == 0 indicated an invalid page (e.g. page number not yet set) + if (pageNr == 0) + return; + + // Get a pointer to the page contents + RenderedDviPagePixmap* pageData = dynamic_cast<RenderedDviPagePixmap*>(documentCache->getPage(pageNr)); + if (pageData == 0) + { + kdDebug(4300) << "DVIWidget::mousePressEvent(...) pageData for page #" << pageNr << " is empty" << endl; + return; + } + + // Check if the mouse is pressed on a source-hyperlink + // source hyperlinks can be invoked with the Middle Mousebutton or alternatively + // with Control+Left Mousebutton + if ((e->button() == MidButton || (e->button() == LeftButton && (e->state() & ControlButton))) + && (pageData->sourceHyperLinkList.size() > 0)) + { + int minIndex = 0; + int minimum = 0; + + for(unsigned int i=0; i<pageData->sourceHyperLinkList.size(); i++) + { + if (pageData->sourceHyperLinkList[i].box.contains(e->pos())) + { + emit(SRCLink(pageData->sourceHyperLinkList[i].linkText, e, this)); + e->accept(); + return; + } + // Remember the closest source link + QPoint center = pageData->sourceHyperLinkList[i].box.center(); + int dx = center.x() - e->pos().x(); + int dy = center.y() - e->pos().y(); + if (dx*dx + dy*dy < minimum || i == 0) + { + minIndex = i; + minimum = dx*dx + dy*dy; + } + } + // If the mouse pointer is not exactly inside a source link, jump to the closest target. + emit(SRCLink(pageData->sourceHyperLinkList[minIndex].linkText, e, this)); + e->accept(); + } + + // Call implementation from parent + DocumentWidget::mousePressEvent(e); +} + + +void DVIWidget::mouseMoveEvent(QMouseEvent* e) +{ + // pageNr == 0 indicated an invalid page (e.g. page number not yet set) + if (pageNr == 0) + return; + + // Call the standard implementation + DocumentWidget::mouseMoveEvent(e); + + // Analyze the mouse movement only if no mouse button was pressed + if ( e->state() == 0 ) { + // Get a pointer to the page contents + RenderedDviPagePixmap* pageData = dynamic_cast<RenderedDviPagePixmap*>(documentCache->getPage(pageNr)); + if (pageData == 0) { + kdDebug(4300) << "DVIWidget::mouseMoveEvent(...) pageData for page #" << pageNr << " is empty" << endl; + return; + } + + // Check if the cursor hovers over a sourceHyperlink. + for(unsigned int i=0; i<pageData->sourceHyperLinkList.size(); i++) { + if (pageData->sourceHyperLinkList[i].box.contains(e->pos())) { + clearStatusBarTimer.stop(); + + // The macro-package srcltx gives a special like "src:99 test.tex" + // while MikTeX gives "src:99test.tex". KDVI tries + // to understand both. + QString cp = pageData->sourceHyperLinkList[i].linkText; + int max = cp.length(); + int i; + for(i=0; i<max; i++) + if (cp[i].isDigit() == false) + break; + + emit setStatusBarText( i18n("line %1 of %2").arg(cp.left(i)).arg(cp.mid(i).simplifyWhiteSpace()) ); + return; + } + } + } +} + + +#include "dviWidget.moc" diff --git a/kdvi/dviWidget.h b/kdvi/dviWidget.h new file mode 100644 index 00000000..7c008d5a --- /dev/null +++ b/kdvi/dviWidget.h @@ -0,0 +1,38 @@ +// -*- C++ -*- +// +// Class: DVIWidget +// +// Widget for displaying TeX DVI files. +// Part of KDVI- A previewer for TeX DVI files. +// +// (C) 2004 Wilfried Huss. Distributed under the GPL. + +#ifndef _DVIWIDGET_H_ +#define _DVIWIDGET_H_ + +#include "documentWidget.h" + +class PageView; +class DocumentPageCache; +class QPaintEvent; +class QMouseEvent; +class QWidget; +class textSelection; + + +class DVIWidget : public DocumentWidget +{ + Q_OBJECT + +public: + DVIWidget(QWidget* parent, PageView* sv, DocumentPageCache* cache, const char* name); + +signals: + void SRCLink(const QString&, QMouseEvent* e, DocumentWidget*); + +private: + virtual void mousePressEvent(QMouseEvent* e); + virtual void mouseMoveEvent(QMouseEvent* e); +}; + +#endif diff --git a/kdvi/dvisourcesplitter.cpp b/kdvi/dvisourcesplitter.cpp new file mode 100644 index 00000000..a052a10a --- /dev/null +++ b/kdvi/dvisourcesplitter.cpp @@ -0,0 +1,95 @@ +// +// C++ Implementation: dvisourcesplitter +// +// Author: Jeroen Wijnhout <Jeroen.Wijnhout@kdemail.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// + +#include <config.h> + +#include <qdir.h> +#include <kdebug.h> + +#include "dvisourcesplitter.h" + +//#define DEBUG_SOURCESPLITTER + + +DVI_SourceFileSplitter::DVI_SourceFileSplitter(const QString &srclink, const QString &dviFile) +{ + QString filepart = srclink, linepart; + bool possibleNumberMixUp = false; //if sourcefilename starts with a number + //then there could be a mix up, i.e. src:123file.tex + //line 123 and file.tex or line 12 and 3file.tex? + +#ifdef DEBUG_SOURCESPLITTER + kdDebug(4300) << "DVI_SourceSplitter: srclink " << srclink << endl; +#endif + + //remove src: if necessary + if ( filepart.left(4) == "src:" ) filepart = srclink.mid(4); + + //split first + Q_UINT32 max = filepart.length(), i = 0; + for(i=0; i<max; ++i) if ( !filepart[i].isDigit()) break; + linepart = filepart.left(i); + filepart = filepart.mid(i); + + //check for number mix up + if ( filepart[0] != ' ' && (linepart.length() != 1) ) possibleNumberMixUp = true; + + //remove a spaces + filepart = filepart.stripWhiteSpace(); + linepart = linepart.stripWhiteSpace(); + +#ifdef DEBUG_SOURCESPLITTER + kdDebug() << "DVI_SourceSplitter: filepart " << filepart << " linepart " << linepart << endl; +#endif + + //test if the file exists + m_fileInfo.setFile(QFileInfo(dviFile).dir(true), filepart); + bool fiExists = m_fileInfo.exists(); + + //if it doesn't exist, but adding ".tex" + if ( !fiExists && QFileInfo(m_fileInfo.absFilePath() + ".tex").exists() ) + m_fileInfo.setFile(m_fileInfo.absFilePath() + ".tex"); + + //if that doesn't help either, perhaps the file started with a + //number: move the numbers from the sourceline to the filename + //one by one (also try to add .tex repeatedly) + if ( possibleNumberMixUp && !fiExists ) + { + QFileInfo tempInfo(m_fileInfo); + QString tempFileName = tempInfo.fileName(); + Q_UINT32 index, maxindex = linepart.length(); + bool found = false; + for ( index = 1; index < maxindex; ++index) + { + tempInfo.setFile(linepart.right(index) + tempFileName); +#ifdef DEBUG_SOURCESPLITTER + kdDebug() << "DVI_SourceSplitter: trying " << tempInfo.fileName() << endl; +#endif + if ( tempInfo.exists() ) { found = true; break;} + tempInfo.setFile(linepart.right(index) + tempFileName + ".tex"); +#ifdef DEBUG_SOURCESPLITTER + kdDebug() << "DVI_SourceSplitter: trying " << tempInfo.fileName() << endl; +#endif + if ( tempInfo.exists() ) { found = true; break;} + } + + if (found) + { + m_fileInfo = tempInfo; + linepart = linepart.left(maxindex - index); + } + } + + bool ok; + m_line = linepart.toInt(&ok); + if (!ok) m_line = 0; + +#ifdef DEBUG_SOURCESPLITTER + kdDebug() << "DVI_SourceSplitter: result: file " << m_fileInfo.absFilePath() << " line " << m_line << endl; +#endif +} diff --git a/kdvi/dvisourcesplitter.h b/kdvi/dvisourcesplitter.h new file mode 100644 index 00000000..f0028bf2 --- /dev/null +++ b/kdvi/dvisourcesplitter.h @@ -0,0 +1,34 @@ +// -*- C++ -*- +// +// C++ Interface: dvisourcesplitter +// +// Author: Jeroen Wijnhout <Jeroen.Wijnhout@kdemail.net>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// + +#ifndef DVI_SOURCEFILESPLITTER_H +#define DVI_SOURCEFILESPLITTER_H + +#include <qfileinfo.h> + +class QString; + + +class DVI_SourceFileSplitter +{ +public: + DVI_SourceFileSplitter(const QString & scrlink, const QString & dviFile); + + QString fileName() { return m_fileInfo.fileName(); } + QString filePath() { return m_fileInfo.absFilePath(); } + bool fileExists() { return m_fileInfo.exists(); } + + Q_UINT32 line() { return m_line; } + +private: + QFileInfo m_fileInfo; + Q_UINT32 m_line; + bool m_exists; +}; +#endif diff --git a/kdvi/examples/BrokenDVI1.dvi b/kdvi/examples/BrokenDVI1.dvi Binary files differnew file mode 100644 index 00000000..4f189853 --- /dev/null +++ b/kdvi/examples/BrokenDVI1.dvi diff --git a/kdvi/examples/Font_not_found.dvi b/kdvi/examples/Font_not_found.dvi Binary files differnew file mode 100644 index 00000000..4ba6383f --- /dev/null +++ b/kdvi/examples/Font_not_found.dvi diff --git a/kdvi/examples/dvistd0.dvi b/kdvi/examples/dvistd0.dvi Binary files differnew file mode 100644 index 00000000..ef73e604 --- /dev/null +++ b/kdvi/examples/dvistd0.dvi diff --git a/kdvi/fontEncoding.cpp b/kdvi/fontEncoding.cpp new file mode 100644 index 00000000..0ca83372 --- /dev/null +++ b/kdvi/fontEncoding.cpp @@ -0,0 +1,87 @@ +// fontEncoding.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> +#ifdef HAVE_FREETYPE + +#include <kdebug.h> +#include <kprocio.h> +#include <qfile.h> +#include <qstringlist.h> + +#include "fontEncoding.h" + +//#define DEBUG_FONTENC + +fontEncoding::fontEncoding(const QString &encName) +{ +#ifdef DEBUG_FONTENC + kdDebug(4300) << "fontEncoding( " << encName << " )" << endl; +#endif + + _isValid = false; + // Use kpsewhich to find the encoding file. + KProcIO proc; + QString encFileName; + proc << "kpsewhich" << encName; + if (proc.start(KProcess::Block) == false) { + kdError(4300) << "fontEncoding::fontEncoding(...): kpsewhich could not be started." << endl; + return; + } + proc.readln(encFileName); + encFileName = encFileName.stripWhiteSpace(); + + if (encFileName.isEmpty()) { + kdError(4300) << QString("fontEncoding::fontEncoding(...): The file '%1' could not be found by kpsewhich.").arg(encName) << endl; + return; + } + +#ifdef DEBUG_FONTENC + kdDebug(4300) << "FileName of the encoding: " << encFileName << endl; +#endif + + QFile file( encFileName ); + if ( file.open( IO_ReadOnly ) ) { + // Read the file (excluding comments) into the QString variable + // 'fileContent' + QTextStream stream( &file ); + QString fileContent; + while ( !stream.atEnd() ) + fileContent += stream.readLine().section('%', 0, 0); // line of text excluding '\n' until first '%'-sign + file.close(); + + fileContent = fileContent.stripWhiteSpace(); + + // Find the name of the encoding + encodingFullName = fileContent.section('[', 0, 0).simplifyWhiteSpace().mid(1); +#ifdef DEBUG_FONTENC + kdDebug(4300) << "encodingFullName: " << encodingFullName << endl; +#endif + + fileContent = fileContent.section('[', 1, 1).section(']',0,0).simplifyWhiteSpace(); + QStringList glyphNameList = QStringList::split( '/', fileContent ); + + int i = 0; + for ( QStringList::Iterator it = glyphNameList.begin(); (it != glyphNameList.end())&&(i<256); ++it ) { + glyphNameVector[i] = (*it).simplifyWhiteSpace(); +#ifdef DEBUG_FONTENC + kdDebug(4300) << i << ": " << glyphNameVector[i] << endl; +#endif + i++; + } + for(; i<256; i++) + glyphNameVector[i] = ".notdef"; + } else { + kdError(4300) << QString("fontEncoding::fontEncoding(...): The file '%1' could not be opened.").arg(encFileName) << endl; + return; + } + + _isValid = true; +} + + +#endif // HAVE_FREETYPE diff --git a/kdvi/fontEncoding.h b/kdvi/fontEncoding.h new file mode 100644 index 00000000..b5ca1344 --- /dev/null +++ b/kdvi/fontEncoding.h @@ -0,0 +1,86 @@ +// -*- C++ -*- +// fontEncoding.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#ifndef _FONTENCODING_H +#define _FONTENCODING_H + +#include <qstring.h> + + +/** + * This class represents the contents of a font encoding file, + * e.g. "8r.enc" + * + * Explanation of font encodings: TeX was designed to only use + * MetaFont fonts. A DVI file referres to a MetaFont font by giving an + * at-most-8-character name, such as 'cmr10'. The DVI previewer would + * then locate the associated PK font file (e.g. cmr10.600pk), load + * it, and retrieve the character shaped. + * + * Today TeX is also used to access Type1 and TrueType fonts, which it + * was never designed to do. As in the case of MetaFont font, the DVI + * file specifies the name of a font, e.g. 'rpbkd', and the DVI + * previewer finds the associated font file 'ubkd8a.pfb' by means of a + * map file (see fontMap.h). The font map file also specifies an + * encoding (e.g. '8r', to be found in a file '8r.enc'). Font + * encodings are necessary because TeX can only use the first 256 + * characters of a font, while modern PostScript fonts often contain + * more. + * + * In a PostScript font, glyphs can often be accessed in two ways: + * + * (a) by an integer, the 'glyph index', which need not be + * positive. Glyph indices can be found in every font. + * + * (b) by the name of the glyph, such as 'A', 'plusminus' or + * 'ogonek'. Note: Not all fonts contain glyph names, and if a font + * contains glyph names, they are not always reliable. + * + * An encoding file is essentially a list of 256 names of glyphs that + * TeX wishes to use from a certain font. If the font contains more + * than 256 glyphs, TeX is still limited to use at most 256 glyphs. If + * more glyphs are required, TeX can probably use the same font under + * a different name and with a different encoding ---the map file + * (fontMap.h) can probably see to that. + * + * Summing up: this class contains 256 glyph names read from an + * encoding file during the construction of this class. + * + * @author Stefan Kebekus <kebekus@kde.org> + * + **/ + +class fontEncoding { + public: + // The constructor takes the name of an encoding file, such as + // '8r.enc', locate the file on the hard disk using the 'kpsewhich' + // command, reads it in and parses it. If the file cannot be + // located, opened or parsed, errors are printed using the kdError() + // channel, and the array glyphNameVector will contain empty + // strings. + fontEncoding(const QString &encName); + + // Full name of the encoding, as read from the encoding file + QString encodingFullName; + + // List of 256 glyph names. The name can be '.notdef' to indicate + // that a certain position is left open, or empty, if the encoding + // file did not contain 256 characters or could not be properly read + QString glyphNameVector[256]; + + // Returns 'true' if the encoding file was found and could + // successfully be loaded. + bool isValid() {return _isValid;} + + private: + // Set by the constructor to 'true', if the encoding file was found + // and could be loaded successfully. + bool _isValid; +}; + +#endif diff --git a/kdvi/fontEncodingPool.cpp b/kdvi/fontEncodingPool.cpp new file mode 100644 index 00000000..0100ee90 --- /dev/null +++ b/kdvi/fontEncodingPool.cpp @@ -0,0 +1,37 @@ +// fontEncodingPool.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> +#ifdef HAVE_FREETYPE + + +#include "fontEncodingPool.h" + +fontEncodingPool::fontEncodingPool() +{ +} + + +fontEncoding *fontEncodingPool::findByName(const QString &name) +{ + fontEncoding *ptr = dictionary.find( name ); + + if (ptr == 0) { + ptr = new fontEncoding(name); + if (ptr->isValid()) + dictionary.insert(name, ptr ); + else { + delete ptr; + ptr = 0; + } + } + + return ptr; +} + + +#endif // HAVE_FREETYPE diff --git a/kdvi/fontEncodingPool.h b/kdvi/fontEncodingPool.h new file mode 100644 index 00000000..b875bf52 --- /dev/null +++ b/kdvi/fontEncodingPool.h @@ -0,0 +1,29 @@ +// -*- C++ -*- +// fontEncodingPool.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#ifndef _FONTENCODINGPOOL_H +#define _FONTENCODINGPOOL_H + +#include "fontEncoding.h" + +#include <qdict.h> + +class QString; + + +class fontEncodingPool { + public: + fontEncodingPool(); + + fontEncoding *findByName(const QString &name); + + private: + QDict<fontEncoding> dictionary; +}; + +#endif diff --git a/kdvi/fontMap.cpp b/kdvi/fontMap.cpp new file mode 100644 index 00000000..cf5fa841 --- /dev/null +++ b/kdvi/fontMap.cpp @@ -0,0 +1,159 @@ +// fontMap.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> +#ifdef HAVE_FREETYPE + +#include <kdebug.h> +#include <kprocio.h> +#include <qfile.h> + +#include "fontMap.h" + +//#define DEBUG_FONTMAP + +fontMap::fontMap() +{ + // Read the map file of ps2pk which will provide us with a + // dictionary "TeX Font names" <-> "Name of font files, Font Names + // and Encodings" (example: the font "Times-Roman" is called + // "ptmr8y" in the DVI file, but the Type1 font file name is + // "utmr8a.pfb". We use the map file of "ps2pk" because that progam + // has, like kdvi (and unlike dvips), no built-in fonts. + + // Finding ps2pk.map is not easy. In teTeX < 3.0, the kpsewhich + // program REQUIRES the option "--format=dvips config". In teTeX = + // 3.0, the option "--format=map" MUST be used. Since there is no + // way to give both options at the same time, there is seemingly no + // other way than to try both options one after another. We use the + // teTeX 3.0 format first. + KProcIO proc; + proc << "kpsewhich" << "--format=map" << "ps2pk.map"; + if (proc.start(KProcess::Block) == false) { + kdError(4700) << "fontMap::fontMap(): kpsewhich could not be started." << endl; + return; + } + + QString map_fileName; + proc.readln(map_fileName); + map_fileName = map_fileName.stripWhiteSpace(); + if (map_fileName.isEmpty()) { + // Map file not found? Then we try the teTeX < 3.0 way of finding + // the file. + proc << "kpsewhich" << "--format=dvips config" << "ps2pk.map"; + if (proc.start(KProcess::Block) == false) { + kdError(4700) << "fontMap::fontMap(): kpsewhich could not be started." << endl; + return; + } + proc.readln(map_fileName); + map_fileName = map_fileName.stripWhiteSpace(); + + // If both versions fail, then there is nothing left to do. + if (map_fileName.isEmpty()) { + kdError(4700) << "fontMap::fontMap(): The file 'ps2pk.map' could not be found by kpsewhich." << endl; + return; + } + } + + QFile file( map_fileName ); + if ( file.open( IO_ReadOnly ) ) { + QTextStream stream( &file ); + QString line; + while ( !stream.atEnd() ) { + line = stream.readLine().simplifyWhiteSpace(); + if (line.at(0) == '%') + continue; + + QString TeXName = line.section(' ', 0, 0); + QString FullName = line.section(' ', 1, 1); + QString fontFileName = line.section('<', -1).stripWhiteSpace().section(' ', 0, 0); + QString encodingName = line.section('<', -2, -2).stripWhiteSpace().section(' ', 0, 0); + // It seems that sometimes the encoding is prepended by the + // letter '[', which we ignore + if ((!encodingName.isEmpty()) && (encodingName[0] == '[')) + encodingName = encodingName.mid(1); + + double slant = 0.0; + int i = line.find("SlantFont"); + if (i >= 0) { + bool ok; + slant = line.left(i).section(' ', -1, -1 ,QString::SectionSkipEmpty).toDouble(&ok); + if (ok == false) + slant = 0.0; + } + + fontMapEntry &entry = fontMapEntries[TeXName]; + + entry.slant = slant; + entry.fontFileName = fontFileName; + entry.fullFontName = FullName; + if (encodingName.endsWith(".enc")) + entry.fontEncoding = encodingName; + else + entry.fontEncoding = QString::null; + } + file.close(); + } else + kdError(4300) << QString("fontMap::fontMap(): The file '%1' could not be opened.").arg(map_fileName) << endl; + +#ifdef DEBUG_FONTMAP + kdDebug(4300) << "FontMap file parsed. Results:" << endl; + QMap<QString, fontMapEntry>::Iterator it; + for ( it = fontMapEntries.begin(); it != fontMapEntries.end(); ++it ) + kdDebug(4300) << "TeXName: " << it.key() + << ", FontFileName=" << it.data().fontFileName + << ", FullName=" << it.data().fullFontName + << ", Encoding=" << it.data().fontEncoding + << "." << endl;; +#endif +} + + +const QString &fontMap::findFileName(const QString &TeXName) +{ + QMap<QString, fontMapEntry>::Iterator it = fontMapEntries.find(TeXName); + + if (it != fontMapEntries.end()) + return it.data().fontFileName; + else + return QString::null; +} + + +const QString &fontMap::findFontName(const QString &TeXName) +{ + QMap<QString, fontMapEntry>::Iterator it = fontMapEntries.find(TeXName); + + if (it != fontMapEntries.end()) + return it.data().fullFontName; + else + return QString::null; +} + + +const QString &fontMap::findEncoding(const QString &TeXName) +{ + QMap<QString, fontMapEntry>::Iterator it = fontMapEntries.find(TeXName); + + if (it != fontMapEntries.end()) + return it.data().fontEncoding; + else + return QString::null; +} + + +double fontMap::findSlant(const QString &TeXName) +{ + QMap<QString, fontMapEntry>::Iterator it = fontMapEntries.find(TeXName); + + if (it != fontMapEntries.end()) + return it.data().slant; + else + return 0.0; +} + +#endif // HAVE_FREETYPE diff --git a/kdvi/fontMap.h b/kdvi/fontMap.h new file mode 100644 index 00000000..55e44082 --- /dev/null +++ b/kdvi/fontMap.h @@ -0,0 +1,118 @@ +// -*- C++ -*- +// fontMap.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#ifndef _FONTMAP_H +#define _FONTMAP_H + +#include <qmap.h> +#include <qstring.h> + +/** + * This class represents one line of a font map file, and contains + * three pieces of information about a font: its file name, the full + * name of the font, and the encoding. + * + * @author Stefan Kebekus <kebekus@kde.org> + **/ + +class fontMapEntry { + public: + // File name of the font WITHOUT the path. The full path name must + // be looked by using the kpathsea library, e.g. by means of the + // kpsewhich command. A valid entry would be 'ubkd8a.pfb' + QString fontFileName; + + // This string contains the full name of the font, + // e.g. 'URWBookmanL-DemiBold' + QString fullFontName; + + // If the font requires an encoding (see fontEncoding.h for an + // explanation), this string is not empty and contains the name of + // the encoding, e.g. '8r'. The path of the associated encoding file + // (on the author's machine: /usr/share/texmf/dvips/psnfss/8r.enc) + // must be looked up using the kpsewhich comman. + QString fontEncoding; + + // Some fonts need to be slanted, and the font map file defines by + // how much. This field is set to 0.0 if no slanting is specified in + // the map file. + double slant; +}; + + +/** + * This class represents the contents of the font map file "ps2pk.map" + * + * A font map file is part of the machinery that make it possible to + * access PostScript (and possibly also TrueType and OpenType) fonts + * from a DVI file. + * + * Long time ago, when TeX was only used with MetaFont fonts, the DVI + * file would specify a font by giving an 8-character name, such as + * 'cmr10'. The DVI previewer would then locate the associated PK font + * file, load it, and retrieve the character shaped. Happy times, they + * were. + * + * Today TeX is also used to access Type1 and TrueType fonts, which do + * not fit well into the TeX naming scheme. Like in earlier times, the + * DVI file specifies the name of a font, e.g. 'rpbkd', but nowadays + * the DVI previewer cannot just go and find a file 'rpbkd.pk'. No, + * no. Instead, the DVI previewr needs to look up the meaning of + * 'rpbkd' in a map-file. There it finds that 'rpbkd' refers to a font + * called 'URWBookmanL-DemiBold', to be found under the file name + * 'ubkd8a.pfb' whose glyphs are to be encoded using the '8a' encoding + * file (see the header file 'fontEncoding.h' for more information + * about encodings) + * + * Such map files exists for all dvi output drivers that are part of + * the TeX distribution that is installed on your + * computer. Unfortunately, KDVI is not part of a TeX distribution, + * and therefore does not have its own map file. As a workaround, KDVI + * uses the map file of the program ps2pk which is similar to KDVI in + * that the ps2pk driver does not have built-in fonts, unlike the + * PostScript printers for which dvips is used. + * + * @author Stefan Kebekus <kebekus@kde.org> + * + **/ + +class fontMap { + public: + /** The default constructor will try to locate the file 'ps2pk.map', + and read its contents. If the file 'ps2pk.map' cannot be found + using the kpsewhich command, or if it cannot be read, or is + (partially) in an improper format, an error message is printed + to stderr using the kdDebug() stream. */ + fontMap( void ); + + /** find the name of a font file (e.g. 'ubkd8a.pfb') from a TeX font + name (e.g. 'rpbkd'). This method return a reference to + QString::null if the font could not be found. */ + const QString &findFileName(const QString &TeXName); + + /** find the name of a font (e.g. 'URWBookmanL-DemiBold') from a TeX + font name (e.g. 'rpbkd'). This method return a reference to + QString::null if the font could not be found. */ + const QString &findFontName(const QString &TeXName); + + /** find the name of an encoding file for a font (e.g. '8r') from a + TeX font name (e.g. 'rpbkd'). This method return a reference to + QString::null if the font could not be found. */ + const QString &findEncoding(const QString &TeXName); + + /** This method finds the slant of a font. Returns 0.0 if no slant + was defined. */ + double findSlant(const QString &TeXName); + + private: + /** This member maps TeX font names mapEntry classes that contain + the font's filenames, full font names and encodings. */ + QMap<QString, fontMapEntry> fontMapEntries; +}; + +#endif // ifndef _FONTMAP_H diff --git a/kdvi/fontpool.cpp b/kdvi/fontpool.cpp new file mode 100644 index 00000000..adec497b --- /dev/null +++ b/kdvi/fontpool.cpp @@ -0,0 +1,597 @@ +// +// fontpool.cpp +// +// (C) 2001-2004 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> + +#include <kdebug.h> +#include <kinstance.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <kprocio.h> +#include <math.h> +#include <qapplication.h> +#include <qfile.h> +#include <qimage.h> +#include <qpainter.h> +#include <stdlib.h> + +#include "fontpool.h" +#include "performanceMeasurement.h" +#include "prefs.h" +#include "TeXFont.h" + +//#define DEBUG_FONTPOOL + + + +// List of permissible MetaFontModes which are supported by kdvi. + +//const char *MFModes[] = { "cx", "ljfour", "lexmarks" }; +//const char *MFModenames[] = { "Canon CX", "LaserJet 4", "Lexmark S" }; +//const int MFResolutions[] = { 300, 600, 1200 }; + +#ifdef PERFORMANCE_MEASUREMENT +QTime fontPoolTimer; +bool fontPoolTimerFlag; +#endif + +//#define DEBUG_FONTPOOL + +fontPool::fontPool() + : progress( "fontgen", // Chapter in the documentation for help. + i18n( "KDVI is currently generating bitmap fonts..." ), + i18n( "Aborts the font generation. Don't do this." ), + i18n( "KDVI is currently generating bitmap fonts which are needed to display your document. " + "For this, KDVI uses a number of external programs, such as MetaFont. You can find " + "the output of these programs later in the document info dialog." ), + i18n( "KDVI is generating fonts. Please wait." ), + 0 ) +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::fontPool() called" << endl; +#endif + + setName("Font Pool"); + + displayResolution_in_dpi = 100.0; // A not-too-bad-default + useFontHints = true; + CMperDVIunit = 0; + extraSearchPath = QString::null; + fontList.setAutoDelete(true); + + +#ifdef HAVE_FREETYPE + // Initialize the Freetype Library + if ( FT_Init_FreeType( &FreeType_library ) != 0 ) { + kdError(4300) << "Cannot load the FreeType library. KDVI proceeds without FreeType support." << endl; + FreeType_could_be_loaded = false; + } else + FreeType_could_be_loaded = true; +#endif + + // Check if the QT library supports the alpha channel of + // pixmaps. Experiments show that --depending of the configuration + // of QT at compile and runtime or the availability of the XFt + // extension, alpha channels are either supported, or silently + // converted to 1-bit masks. + QImage start(1, 1, 32); // Generate a 1x1 image, black with alpha=0x10 + start.setAlphaBuffer(true); + Q_UINT32 *destScanLine = (Q_UINT32 *)start.scanLine(0); + *destScanLine = 0x80000000; + QPixmap intermediate(start); + QPixmap dest(1,1); + dest.fill(Qt::white); + QPainter paint( &dest ); + paint.drawPixmap(0, 0, intermediate); + paint.end(); + start = dest.convertToImage().convertDepth(32); + Q_UINT8 result = *(start.scanLine(0)) & 0xff; + + if ((result == 0xff) || (result == 0x00)) { +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::fontPool(): QPixmap does not support the alpha channel" << endl; +#endif + QPixmapSupportsAlpha = false; + } else { +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::fontPool(): QPixmap supports the alpha channel" << endl; +#endif + QPixmapSupportsAlpha = true; + } +} + + +fontPool::~fontPool() +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::~fontPool() called" << endl; +#endif + + // need to manually clear the fonts _before_ freetype gets unloaded + fontList.clear(); + +#ifdef HAVE_FREETYPE + if (FreeType_could_be_loaded == true) + FT_Done_FreeType( FreeType_library ); +#endif +} + + +void fontPool::setParameters( bool _useFontHints ) +{ + // Check if glyphs need to be cleared + if (_useFontHints != useFontHints) { + double displayResolution = displayResolution_in_dpi; + TeXFontDefinition *fontp = fontList.first(); + while(fontp != 0 ) { + fontp->setDisplayResolution(displayResolution * fontp->enlargement); + fontp=fontList.next(); + } + } + + useFontHints = _useFontHints; +} + + +TeXFontDefinition* fontPool::appendx(const QString& fontname, Q_UINT32 checksum, Q_UINT32 scale, double enlargement) +{ + // Reuse font if possible: check if a font with that name and + // natural resolution is already in the fontpool, and use that, if + // possible. + TeXFontDefinition *fontp = fontList.first(); + while( fontp != 0 ) { + if ((fontname == fontp->fontname) && ( (int)(enlargement*1000.0+0.5)) == (int)(fontp->enlargement*1000.0+0.5)) { + // if font is already in the list + fontp->mark_as_used(); + return fontp; + } + fontp=fontList.next(); + } + + // If font doesn't exist yet, we have to generate a new font. + + double displayResolution = displayResolution_in_dpi; + + fontp = new TeXFontDefinition(fontname, displayResolution*enlargement, checksum, scale, this, enlargement); + if (fontp == 0) { + kdError(4300) << i18n("Could not allocate memory for a font structure!") << endl; + exit(0); + } + fontList.append(fontp); + +#ifdef PERFORMANCE_MEASUREMENT + fontPoolTimer.start(); + fontPoolTimerFlag = false; +#endif + + // Now start kpsewhich/MetaFont, etc. if necessary + return fontp; +} + + +QString fontPool::status() +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::status() called" << endl; +#endif + + QString text; + QStringList tmp; + + if (fontList.isEmpty()) + return i18n("The fontlist is currently empty."); + + text.append("<table WIDTH=\"100%\" NOSAVE >"); + text.append( QString("<tr><td><b>%1</b></td> <td><b>%2</b></td> <td><b>%3</b></td> <td><b>%4</b> <td><b>%5</b></td> <td><b>%6</b></td></tr>") + .arg(i18n("TeX Name")) + .arg(i18n("Family")) + .arg(i18n("Zoom")) + .arg(i18n("Type")) + .arg(i18n("Encoding")) + .arg(i18n("Comment")) ); + + TeXFontDefinition *fontp = fontList.first(); + while ( fontp != 0 ) { + QString errMsg, encoding; + + if (!(fontp->flags & TeXFontDefinition::FONT_VIRTUAL)) { +#ifdef HAVE_FREETYPE + encoding = fontp->getFullEncodingName(); +#endif + if (fontp->font != 0) + errMsg = fontp->font->errorMessage; + else + errMsg = i18n("Font file not found"); + } + +#ifdef HAVE_FREETYPE + tmp << QString ("<tr><td>%1</td> <td>%2</td> <td>%3%</td> <td>%4</td> <td>%5</td> <td>%6</td></tr>") + .arg(fontp->fontname) + .arg(fontp->getFullFontName()) + .arg((int)(fontp->enlargement*100 + 0.5)) + .arg(fontp->getFontTypeName()) + .arg(encoding) + .arg(errMsg); +#endif + + fontp=fontList.next(); + } + + tmp.sort(); + text.append(tmp.join("\n")); + text.append("</table>"); + + return text; +} + + +bool fontPool::areFontsLocated() +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::areFontsLocated() called" << endl; +#endif + + // Is there a font whose name we did not try to find out yet? + TeXFontDefinition *fontp = fontList.first(); + while( fontp != 0 ) { + if ( !fontp->isLocated() ) + return false; + fontp=fontList.next(); + } + +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "... yes, all fonts are located (but not necessarily loaded)." << endl; +#endif + return true; // That says that all fonts are located. +} + + +void fontPool::locateFonts() +{ + kpsewhichOutput = QString::null; + + // First, we try and find those fonts which exist on disk + // already. If virtual fonts are found, they will add new fonts to + // the list of fonts whose font files need to be located, so that we + // repeat the lookup. + bool vffound; + do { + vffound = false; + locateFonts(false, false, &vffound); + } while(vffound); + + // If still not all fonts are found, look again, this time with + // on-demand generation of PK fonts enabled. + if (!areFontsLocated()) + locateFonts(true, false); + + // If still not all fonts are found, we look for TFM files as a last + // resort, so that we can at least draw filled rectangles for + // characters. + if (!areFontsLocated()) + locateFonts(false, true); + + // If still not all fonts are found, we give up. We mark all fonts + // as 'located', so that wee won't look for them any more, and + // present an error message to the user. + if (!areFontsLocated()) { + markFontsAsLocated(); + QString details = QString("<qt><p><b>PATH:</b> %1</p>%2</qt>").arg(getenv("PATH")).arg(kpsewhichOutput); + KMessageBox::detailedError( 0, i18n("<qt><p>KDVI was not able to locate all the font files " + "which are necessary to display the current DVI file. " + "Your document might be unreadable.</p></qt>"), + details, + i18n("Not All Font Files Found") ); + } +} + + +void fontPool::locateFonts(bool makePK, bool locateTFMonly, bool *virtualFontsFound) +{ + // Set up the kpsewhich process. If pass == 0, look for vf-fonts and + // disable automatic font generation as vf-fonts can't be + // generated. If pass == 0, ennable font generation, if it was + // enabled globally. + emit setStatusBarText(i18n("Locating fonts...")); + + QStringList shellProcessCmdLine; + + KProcIO kpsewhichIO; + // If PK fonts are generated, the kpsewhich command will re-route + // the output of MetaFont into its stderr. Here we make sure this + // output is intercepted and parsed. + qApp->connect(&kpsewhichIO, SIGNAL(receivedStderr(KProcess *, char *, int)), + this, SLOT(mf_output_receiver(KProcess *, char *, int))); + + + kpsewhichIO.setUseShell(true); + + // Now generate the command line for the kpsewhich + // program. Unfortunately, this can be rather long and involved... + shellProcessCmdLine += "kpsewhich"; + shellProcessCmdLine += QString("--dpi 1200"); + shellProcessCmdLine += QString("--mode lexmarks"); + + // Disable automatic pk-font generation. + if (makePK == true) + shellProcessCmdLine += "--mktex pk"; + else + shellProcessCmdLine += "--no-mktex pk"; + + // Names of fonts that shall be located + Q_UINT16 numFontsInJob = 0; + TeXFontDefinition *fontp = fontList.first(); + while ( fontp != 0 ) { + if (!fontp->isLocated()) { + numFontsInJob++; + + if (locateTFMonly == true) + shellProcessCmdLine += KShellProcess::quote(QString("%1.tfm").arg(fontp->fontname)); + else { +#ifdef HAVE_FREETYPE + if (FreeType_could_be_loaded == true) { + const QString &filename = fontsByTeXName.findFileName(fontp->fontname); + if (!filename.isEmpty()) + shellProcessCmdLine += KShellProcess::quote(QString("%1").arg(filename)); + } +#endif + shellProcessCmdLine += KShellProcess::quote(QString("%1.vf").arg(fontp->fontname)); + shellProcessCmdLine += KShellProcess::quote(QString("%1.1200pk").arg(fontp->fontname)); + } + } + fontp=fontList.next(); + } + + if (numFontsInJob == 0) + return; + + progress.setTotalSteps(numFontsInJob, &kpsewhichIO); + + // Now run... kpsewhich. In case of error, kick up a fuss. + MetafontOutput = QString::null; + kpsewhichOutput += "<p><b>"+shellProcessCmdLine.join(" ")+"</b></p>"; + kpsewhichIO << shellProcessCmdLine; + QString importanceOfKPSEWHICH = i18n("<p>KDVI relies on the <b>kpsewhich</b> program to locate font files " + "on your hard disc and to generate PK fonts, if necessary.</p>"); + if (kpsewhichIO.start(KProcess::NotifyOnExit, false) == false) { + QString msg = i18n( "<p>The shell process for the kpsewhich program could not " + "be started. Consequently, some font files could not be found, " + "and your document might by unreadable. If this error is reproducable " + "please report the issue to the KDVI developers using the 'Help' menu.<p>" ); + QApplication::restoreOverrideCursor(); + KMessageBox::error( 0, QString("<qt>%1%2</qt>").arg(importanceOfKPSEWHICH).arg(msg), + i18n("Problem locating fonts - KDVI") ); + markFontsAsLocated(); + return; + } + + // We wait here while the kpsewhich program is concurrently + // running. Every second we call processEvents() to keep the GUI + // updated. This is important, e.g. for the progress dialog that is + // shown when PK fonts are generated by MetaFont. + while(kpsewhichIO.wait(1) == false) + qApp->processEvents(); + progress.hide(); + + // Handle fatal errors. + if (!kpsewhichIO.normalExit()) { + KMessageBox::sorry( 0, "<qt><p>The font generation was aborted. As a result, " + "some font files could not be located, and your document might be unreadable.</p></qt>", + i18n("Font generation aborted - KDVI") ); + + // This makes sure the we don't try to run kpsewhich again + if (makePK == false) + markFontsAsLocated(); + } else + if (kpsewhichIO.exitStatus() == 127) { + // An exit status of 127 means that the kpsewhich executable + // could not be found. We give extra explanation then. + QApplication::restoreOverrideCursor(); + QString msg = i18n( "<p>There were problems running kpsewhich. As a result, " + "some font files could not be located, and your document might be unreadable.</p>" + "<p><b>Possible reason:</b> The kpsewhich program is perhaps not installed on your system, or it " + "cannot be found in the current search path.</p>" + "<p><b>What you can do:</b> The kpsewhich program is normally contained in distributions of the TeX " + "typesetting system. If TeX is not installed on your system, you could install the TeTeX distribution (www.tetex.org). " + "If you are sure that TeX is installed, please try to use the kpsewhich program from the command line to check if it " + "really works.</p>"); + QString details = QString("<qt><p><b>PATH:</b> %1</p>%2</qt>").arg(getenv("PATH")).arg(kpsewhichOutput); + + KMessageBox::detailedError( 0, QString("<qt>%1%2</qt>").arg(importanceOfKPSEWHICH).arg(msg), details, + i18n("Problem locating fonts - KDVI") ); + // This makes sure the we don't try to run kpsewhich again + markFontsAsLocated(); + return; + } + + // Create a list with all filenames found by the kpsewhich program. + QStringList fileNameList; + QString line; + while(kpsewhichIO.readln(line) >= 0) + fileNameList += line; + + // Now associate the file names found with the fonts + fontp=fontList.first(); + while ( fontp != 0 ) { + if (fontp->filename.isEmpty() == true) { + QStringList matchingFiles; +#ifdef HAVE_FREETYPE + const QString &fn = fontsByTeXName.findFileName(fontp->fontname); + if (!fn.isEmpty()) + matchingFiles = fileNameList.grep(fn); +#endif + if (matchingFiles.isEmpty() == true) + matchingFiles += fileNameList.grep(fontp->fontname+"."); + + if (matchingFiles.isEmpty() != true) { +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "Associated " << fontp->fontname << " to " << matchingFiles.first() << endl; +#endif + QString fname = matchingFiles.first(); + fontp->fontNameReceiver(fname); + fontp->flags |= TeXFontDefinition::FONT_KPSE_NAME; + if (fname.endsWith(".vf")) { + if (virtualFontsFound != 0) + *virtualFontsFound = true; + // Constructing a virtual font will most likely insert other + // fonts into the fontList. After that, fontList.next() will + // no longer work. It is therefore safer to start over. + fontp=fontList.first(); + continue; + } + } + } // of if (fontp->filename.isEmpty() == true) + fontp = fontList.next(); + } +} + + +void fontPool::setCMperDVIunit( double _CMperDVI ) +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::setCMperDVIunit( " << _CMperDVI << " )" << endl; +#endif + + if (CMperDVIunit == _CMperDVI) + return; + + CMperDVIunit = _CMperDVI; + + TeXFontDefinition *fontp = fontList.first(); + while(fontp != 0 ) { + fontp->setDisplayResolution(displayResolution_in_dpi * fontp->enlargement); + fontp=fontList.next(); + } +} + + +void fontPool::setDisplayResolution( double _displayResolution_in_dpi ) +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::setDisplayResolution( displayResolution_in_dpi=" << _displayResolution_in_dpi << " ) called" << endl; +#endif + + // Ignore minute changes by less than 2 DPI. The difference would + // hardly be visible anyway. That saves a lot of re-painting, + // e.g. when the user resizes the window, and a flickery mouse + // changes the window size by 1 pixel all the time. + if ( fabs(displayResolution_in_dpi - _displayResolution_in_dpi) <= 2.0 ) { +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::setDisplayResolution(...): resolution wasn't changed. Aborting." << endl; +#endif + return; + } + + displayResolution_in_dpi = _displayResolution_in_dpi; + double displayResolution = displayResolution_in_dpi; + + TeXFontDefinition *fontp = fontList.first(); + while(fontp != 0 ) { + fontp->setDisplayResolution(displayResolution * fontp->enlargement); + fontp=fontList.next(); + } + + // Do something that causes re-rendering of the dvi-window + /*@@@@ + emit fonts_have_been_loaded(this); + */ +} + + +void fontPool::markFontsAsLocated() +{ + TeXFontDefinition *fontp=fontList.first(); + while ( fontp != 0 ) { + fontp->markAsLocated(); + fontp = fontList.next(); + } +} + + + +void fontPool::mark_fonts_as_unused() +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "fontPool::mark_fonts_as_unused() called" << endl; +#endif + + TeXFontDefinition *fontp = fontList.first(); + while ( fontp != 0 ) { + fontp->flags &= ~TeXFontDefinition::FONT_IN_USE; + fontp=fontList.next(); + } +} + + +void fontPool::release_fonts() +{ +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "Release_fonts" << endl; +#endif + + TeXFontDefinition *fontp = fontList.first(); + while(fontp != 0) { + if ((fontp->flags & TeXFontDefinition::FONT_IN_USE) != TeXFontDefinition::FONT_IN_USE) { + fontList.removeRef(fontp); + fontp = fontList.first(); + } else + fontp = fontList.next(); + } +} + + +void fontPool::mf_output_receiver(KProcess *, char *buffer, int buflen) +{ + // Paranoia. + if (buflen < 0) + return; + + QString op = QString::fromLocal8Bit(buffer, buflen); + + kpsewhichOutput.append(op); + MetafontOutput.append(op); + + // We'd like to print only full lines of text. + int numleft; + bool show_prog = false; + while( (numleft = MetafontOutput.find('\n')) != -1) { + QString line = MetafontOutput.left(numleft+1); +#ifdef DEBUG_FONTPOOL + kdDebug(4300) << "MF OUTPUT RECEIVED: " << line; +#endif + // Search for a line which marks the beginning of a MetaFont run + // and show the progress dialog at the end of this method. + if (line.find("kpathsea:") == 0) + show_prog = true; + + // If the Output of the kpsewhich program contains a line starting + // with "kpathsea:", this means that a new MetaFont-run has been + // started. We filter these lines out and update the display + // accordingly. + int startlineindex = line.find("kpathsea:"); + if (startlineindex != -1) { + int endstartline = line.find("\n",startlineindex); + QString startLine = line.mid(startlineindex,endstartline-startlineindex); + + // The last word in the startline is the name of the font which we + // are generating. The second-to-last word is the resolution in + // dots per inch. Display this info in the text label below the + // progress bar. + int lastblank = startLine.findRev(' '); + QString fontName = startLine.mid(lastblank+1); + int secondblank = startLine.findRev(' ',lastblank-1); + QString dpi = startLine.mid(secondblank+1,lastblank-secondblank-1); + + progress.show(); + progress.increaseNumSteps( i18n("Currently generating %1 at %2 dpi").arg(fontName).arg(dpi) ); + } + MetafontOutput = MetafontOutput.remove(0,numleft+1); + } +} + + +#include "fontpool.moc" diff --git a/kdvi/fontpool.h b/kdvi/fontpool.h new file mode 100644 index 00000000..2e9d25da --- /dev/null +++ b/kdvi/fontpool.h @@ -0,0 +1,215 @@ +// -*- C++ -*- +// fontpool.h +// +// (C) 2001-2004 Stefan Kebekus +// Distributed under the GPL + +#ifndef _FONTPOOL_H +#define _FONTPOOL_H + +#include "fontEncodingPool.h" +#include "fontMap.h" +#include "fontprogress.h" +#include "TeXFontDefinition.h" + +#include <qptrlist.h> +#include <qobject.h> + +#ifdef HAVE_FREETYPE +#include <ft2build.h> +#include FT_FREETYPE_H +#endif + +class KProcess; +class KShellProcess; + + +/** + * A list of fonts and a compilation of utility functions + * + * This class holds a list of fonts and is able to perform a number of + * functions on each of the fonts. The main use of this class is that + * it is able to control a concurrently running "kpsewhich" programm + * which is used to locate and load the fonts. + * + * @author Stefan Kebekus <kebekus@kde.org> + * + **/ + +class fontPool : public QObject { + Q_OBJECT + +public: + // Default constructor. + fontPool(); + + // Default destructor. + ~fontPool(); + + /** Method used to set the MetafontMode for the PK font files. This + data is used when loading fonts. Currently, a change here will be + applied only to those font which were not yet loaded ---expect + funny results when changing the data in the mid-work. */ + void setParameters( bool useFontHints ); + + /** Sets the DVI file's path. This information is used to set the + current working directory for the kpsewhich command, so that + kpsewhich will find fonts that are stored in the DVI file's + directory. */ + void setExtraSearchPath( const QString &path ) {extraSearchPath = path;} + + /** Returns the path that is set as the current working directory + for the kpsewhich command, so that kpsewhich will find fonts + that are stored in the DVI file's directory. */ + QString getExtraSearchPath( ) const {return extraSearchPath;} + + /** Sets the resolution of the output device. */ + void setDisplayResolution( double _displayResolution_in_dpi ); + + /** Sets the number of centimeters per DVI unit. */ + void setCMperDVIunit( double CMperDVI ); + double getCMperDVIunit() const {return CMperDVIunit;} + + // If return value is true, font hinting should be used if possible + bool getUseFontHints() const {return useFontHints;} + + // This method adds a font to the list. If the font is not currently + // loaded, it's file will be located and font::load_font will be + // called. Since this is done using a concurrently running process, + // there is no guarantee that the loading is already performed when + // the method returns. + TeXFontDefinition* appendx(const QString& fontname, Q_UINT32 checksum, Q_UINT32 scale, double enlargement); + + // Returns a string in a very basic HTML format which describes the + // fonts in the pool. + QString status(); + + // This is the list which actually holds pointers to the fonts + QPtrList<TeXFontDefinition> fontList; + + // This method marks all fonts in the fontpool as "not in use". The + // fonts are, however, not removed from memory until the method + // release_fonts is called. The method is called when the dvi-file + // is closed. Because the next dvi-file which will be loaded is + // likely to use most of the fonts again, this method implements a + // convenient way of re-using fonts without loading them repeatedly. + void mark_fonts_as_unused(); + + /** This methods removes all fonts from the fontpool (and thus from + memory) which are labeled "not in use". For explanation, see the + mark_fonts_as_unused method. */ + void release_fonts(); + +#ifdef HAVE_FREETYPE + /** A handle to the FreeType library, which is used by TeXFont_PFM + font objects, if KDVI is compiled with FreeType support. */ + FT_Library FreeType_library; + + /** Simple marker. Set to 'true', if the FreeType library was loaded + successfully */ + bool FreeType_could_be_loaded; + + /** This maps TeX font names to font file names, full font names and + encodings. See the file 'fontMap.h' for a detailed + description. */ + fontMap fontsByTeXName; + + /** This is a list of known font encodings which can be conveniently + acessed by name. */ + fontEncodingPool encodingPool; +#endif + + /** This flag is set during the construction of the fontPool + object. It indicates if the QT library supports the alpha + channel of pixmaps. Experiments show that --depending of the + configuration of QT at compile and runtime or the availability + of the XFt extension, alpha channels are either supported, or + silently converted to 1-bit masks. The redering routines in the + TeXFont implementation use this flag to choose the apropriate + drawing routines for the different setups. */ + bool QPixmapSupportsAlpha; + +signals: + /** Passed through to the top-level kpart. */ + void setStatusBarText( const QString& ); + +public slots: + // Locates font files on the disk using the kpsewhich program. If + // 'locateTFMonly' is true, the method does not look for PFB- or + // PK-fonts. Instead, only TFM-files are searched. This option can be + // used as a 'last resort': if a found cannot be found, one can at + // least use the TFM file to draw filled rectangles for the + // characters. If not null, the bool pointed at by virtualFontsFound + // is set to true if one of the fonts found is a virtual font. If no + // virtual font is found, the variable remains untouched. + void locateFonts(); + +private: + // This method goes through the list of fonts, and marks each of them + // as 'located'. Used, e.g. after a fatal error in the font lookup + // process to ensure that the problematic kpsewhich is not used again + void markFontsAsLocated(); + + // Checks if all the fonts file names have been located, and returns + // true if that is so. + bool areFontsLocated(); + + // This flag is used by PFB fonts to determine if the FREETYPE engine + // should use hinted fonts or not + bool useFontHints; + + // Resolution of the output device. + double displayResolution_in_dpi; + + // Number of centimeters per DVI unit + double CMperDVIunit; + + + /** Members used for font location */ + + // Locates font files on the disk using the kpsewhich program. If + // 'locateTFMonly' is true, the method does not look for PFB- or + // PK-fonts. Instead, only TFM-files are searched. This option can be + // used as a 'last resort': if a found cannot be found, one can at + // least use the TFM file to draw filled rectangles for the + // characters. If not null, the bool pointed at by virtualFontsFound + // is set to true if one of the fonts found is a virtual font. If no + // virtual font is found, the variable remains untouched. + void locateFonts(bool makePK, bool locateTFMonly, bool *virtualFontsFound=0); + + // This QString is used internally by the mf_output_receiver() + // method. This string is set to QString::null in locateFonts(bool, + // bool, bool *). Values are set and read by the + // mf_output_receiver(...) method + QString MetafontOutput; + + // This QString is used to collect the output of kpsewhich and + // MetaFont. The string is set to QString::null in the + // locateFonts()-method, and content is gathered by the + // mf_output_receiver(). This string is used by locateFonts() and + // locateFonts(bool, bool, bool *) to display error messages. + QString kpsewhichOutput; + + // This string is set to the DVI file's path. It is used to set the + // current working directory for the kpsewhich command, so that + // kpsewhich will find fonts that are stored in the DVI file's + // directory. Used by the locateFonts() and the locateFonts(bool, + // bool, bool *) method. Values are set by the + // setExtraSearchPath(...) method + QString extraSearchPath; + + // FontProgress; the progress dialog used when generating fonts. + fontProgressDialog progress; + +private slots: + /** Members used for font location */ + + // For internal purposess only. This slot is called when MetaFont is + // run via the kpsewhich programm. The MetaFont output is + // transmitted to the fontpool via this slot. This method calles + // suitable methods in the fontProgres Dialog, and collects the + // output of MetaFontt int the "MetafontOutput" member + void mf_output_receiver(KProcess *, char *, int); +}; + +#endif //ifndef _FONTPOOL_H diff --git a/kdvi/fontprogress.cpp b/kdvi/fontprogress.cpp new file mode 100644 index 00000000..3935ceba --- /dev/null +++ b/kdvi/fontprogress.cpp @@ -0,0 +1,104 @@ +// fontprogress.cpp +// +// (C) 2001--2004 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> + +#include "fontprogress.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kprocio.h> +#include <kprogress.h> +#include <qapplication.h> +#include <qframe.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qvariant.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +#include <qvbox.h> + +/* + * Constructs a fontProgressDialog which is a child of 'parent', with the + * name 'name' and widget flags set to 'f' + */ +fontProgressDialog::fontProgressDialog(const QString& helpIndex, const QString& label, const QString& abortTip, const QString& whatsThis, const QString& ttip, QWidget* parent, const QString& name, bool progressbar) + : KDialogBase( parent, "Font Generation Progress Dialog", true, name, Cancel, Cancel, true ) +{ + setCursor( QCursor( 3 ) ); + + setButtonCancel(KGuiItem(i18n("Abort"), "stop", abortTip)); + + if (helpIndex.isEmpty() == false) { + setHelp(helpIndex, "kdvi"); + setHelpLinkText( i18n( "What's going on here?") ); + enableLinkedHelp(true); + } else + enableLinkedHelp(false); + + QVBox *page = makeVBoxMainWidget(); + + TextLabel1 = new QLabel( label, page, "TextLabel2" ); + TextLabel1->setAlignment( int( QLabel::AlignCenter ) ); + QWhatsThis::add( TextLabel1, whatsThis ); + QToolTip::add( TextLabel1, ttip ); + + if (progressbar) { + ProgressBar1 = new KProgress( page, "ProgressBar1" ); + ProgressBar1->setFormat(i18n("%v of %m")); + QWhatsThis::add( ProgressBar1, whatsThis ); + QToolTip::add( ProgressBar1, ttip ); + } else + ProgressBar1 = NULL; + + TextLabel2 = new QLabel( "", page, "TextLabel2" ); + TextLabel2->setAlignment( int( QLabel::AlignCenter ) ); + QWhatsThis::add( TextLabel2, whatsThis ); + QToolTip::add( TextLabel2, ttip ); + + progress = 0; + procIO = 0; + qApp->connect(this, SIGNAL(finished()), this, SLOT(killProcIO())); +} + + +/* + * Destroys the object and frees any allocated resources + */ + +fontProgressDialog::~fontProgressDialog() +{ + // no need to delete child widgets, Qt does it all for us +} + + +void fontProgressDialog::increaseNumSteps(const QString& explanation) +{ + if (ProgressBar1 != 0) + ProgressBar1->setProgress(progress++); + TextLabel2->setText( explanation ); +} + + +void fontProgressDialog::setTotalSteps(int steps, KProcIO *proc) +{ + procIO = proc; + if (ProgressBar1 != 0) { + ProgressBar1->setTotalSteps(steps); + ProgressBar1->setProgress(0); + } + progress = 0; +} + + +void fontProgressDialog::killProcIO() +{ + if (!procIO.isNull()) + procIO->kill(); +} + + +#include "fontprogress.moc" diff --git a/kdvi/fontprogress.h b/kdvi/fontprogress.h new file mode 100644 index 00000000..f9c7232b --- /dev/null +++ b/kdvi/fontprogress.h @@ -0,0 +1,64 @@ +// -*- C++ -*- +// +// fontprogress.h +// +// (C) 2001-2004 Stefan Kebekus +// Distributed under the GPL + +#ifndef FONT_GENERATION_H +#define FONT_GENERATION_H + +#include <kdialogbase.h> +#include <qguardedptr.h> + +class KProcIO; +class KProgress; +class QLabel; + + +/** + * A dialog to give feedback to the user when kpsewhich is generating fonts. + * + * This class implements a dialog which pops up, shows a progress bar + * and displays the MetaFont output. It contains three slots, + * outputReceiver, setTotalSteps and hideDialog which can be connected + * with the appropriate signals emitted by the fontpool class. + * + * @author Stefan Kebekus <kebekus@kde.org> + * + * + **/ +class fontProgressDialog : public KDialogBase +{ + Q_OBJECT + +public: + fontProgressDialog( const QString& helpIndex, const QString& label, const QString& abortTip, const QString& whatsThis, const QString& ttip, + QWidget* parent = 0, const QString &name = 0, bool progressbar=true ); + ~fontProgressDialog(); + + /** The number of steps already done is increased, the text received + here is analyzed and presented to the user. */ + void increaseNumSteps(const QString& explanation); + + /** Used to initialize the progress bar. If the argument proc is + non-zero, the associated process will be killed when the "abort" + button is pressed. The FontProgress uses a QGuarderPtr + internally, so it is save to delete the KProcIO anytime. */ + void setTotalSteps(int, KProcIO *proc=0); + + QLabel* TextLabel2; + +private slots: + /** Calling this slot does nothing than to kill the process that is + pointed to be procIO, if procIO is not zero.*/ + void killProcIO(); + +private: + QLabel* TextLabel1; + KProgress* ProgressBar1; + int progress; + QGuardedPtr<KProcIO> procIO; +}; + +#endif // FONT_GENERATION_H diff --git a/kdvi/glyph.cpp b/kdvi/glyph.cpp new file mode 100644 index 00000000..d495d096 --- /dev/null +++ b/kdvi/glyph.cpp @@ -0,0 +1,30 @@ + +/* glyph.cpp + * + * part of kdvi, a dvi-previewer for the KDE desktop environement + * + * written by Stefan Kebekus, originally based on code by Paul Vojta + * and a large number of co-authors */ + +#include <config.h> + +#include <kdebug.h> + +#include "glyph.h" + +glyph::glyph() +{ +#ifdef DEBUG_GLYPH + kdDebug(4300) << "glyph::glyph()" << endl; +#endif + + addr = 0; + x = 0; + y = 0; + dvi_advance_in_units_of_design_size_by_2e20 = 0; +} + +glyph::~glyph() +{ + ; +} diff --git a/kdvi/glyph.h b/kdvi/glyph.h new file mode 100644 index 00000000..1cc41823 --- /dev/null +++ b/kdvi/glyph.h @@ -0,0 +1,37 @@ +// -*- C++ -*- + +#ifndef _GLYPH_H +#define _GLYPH_H + +#include <qcolor.h> +#include <qpixmap.h> + + +struct bitmap { + Q_UINT16 w, h; /* width and height in pixels */ + Q_UINT16 bytes_wide; /* scan-line width in bytes */ + char *bits; /* pointer to the bits */ +}; + +class glyph { + public: + glyph(); + ~glyph(); + + // address of bitmap in font file + long addr; + + QColor color; + + // DVI units to move reference point + Q_INT32 dvi_advance_in_units_of_design_size_by_2e20; + + // x and y offset in pixels + short x, y; + + QPixmap shrunkenCharacter; + + short x2, y2; /* x and y offset in pixels (shrunken bitmap) */ +}; + +#endif //ifndef _GLYPH_H diff --git a/kdvi/infodialog.cpp b/kdvi/infodialog.cpp new file mode 100644 index 00000000..f645def8 --- /dev/null +++ b/kdvi/infodialog.cpp @@ -0,0 +1,134 @@ +// infodialog.cpp +// +// (C) 2001-2003 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> + +#include <kdebug.h> +#include <kio/global.h> +#include <klocale.h> +#include <qfile.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qregexp.h> +#include <qtextview.h> +#include <qtooltip.h> +#include <qvariant.h> +#include <qwhatsthis.h> + +#include "dviFile.h" +#include "fontpool.h" +#include "infodialog.h" + +infoDialog::infoDialog( QWidget* parent ) + : KDialogBase( Tabbed, i18n("Document Info"), Ok, Ok, parent, "Document Info", false, false) +{ + QFrame *page1 = addPage( i18n("DVI File") ); + QVBoxLayout *topLayout1 = new QVBoxLayout( page1, 0, 6 ); + TextLabel1 = new QTextView( page1, "TextLabel1" ); + QToolTip::add( TextLabel1, i18n("Information on the currently loaded DVI-file.") ); + topLayout1->addWidget( TextLabel1 ); + + QFrame *page2 = addPage( i18n("Fonts") ); + QVBoxLayout *topLayout2 = new QVBoxLayout( page2, 0, 6 ); + TextLabel2 = new QTextView( page2, "TextLabel1" ); + TextLabel2->setMinimumWidth(fontMetrics().maxWidth()*40); + TextLabel2->setMinimumHeight(fontMetrics().height()*10); + QToolTip::add( TextLabel2, i18n("Information on currently loaded fonts.") ); + QWhatsThis::add( TextLabel2, i18n("This text field shows detailed information about the currently loaded fonts. " + "This is useful for experts who want to locate problems in the setup of TeX or KDVI.") ); + topLayout2->addWidget( TextLabel2 ); + + QFrame *page3 = addPage( i18n("External Programs") ); + QVBoxLayout *topLayout3 = new QVBoxLayout( page3, 0, 6 ); + TextLabel3 = new QTextView( page3, "TextLabel1" ); + TextLabel3->setText( i18n("No output from any external program received.") ); + QToolTip::add( TextLabel3, i18n("Output of external programs.") ); + QWhatsThis::add( TextLabel3, i18n("KDVI uses external programs, such as MetaFont, dvipdfm or dvips. " + "This text field shows the output of these programs. " + "That is useful for experts who want to find problems in the setup of TeX or KDVI.") ); + topLayout3->addWidget( TextLabel3 ); + + MFOutputReceived = false; + headline = QString::null; + pool = QString::null; +} + + +void infoDialog::setDVIData(dvifile *dviFile) +{ + QString text = ""; + + if (dviFile == NULL) + text = i18n("There is no DVI file loaded at the moment."); + else { + text.append("<table WIDTH=\"100%\" NOSAVE >"); + text.append(QString("<tr><td><b>%1</b></td> <td>%2</td></tr>").arg(i18n("Filename")).arg(dviFile->filename)); + + QFile file(dviFile->filename); + if (file.exists()) + text.append(QString("<tr><td><b>%1</b></td> <td>%2</td></tr>").arg(i18n("File Size")).arg(KIO::convertSize(file.size()))); + else + text.append(QString("<tr><td><b> </b></td> <td>%1</td></tr>").arg(i18n("The file does no longer exist."))); + + text.append(QString("<tr><td><b> </b></td> <td> </td></tr>")); + text.append(QString("<tr><td><b>%1</b></td> <td>%2</td></tr>").arg(i18n("#Pages")).arg(dviFile->total_pages)); + text.append(QString("<tr><td><b>%1</b></td> <td>%2</td></tr>").arg(i18n("Generator/Date")).arg(dviFile->generatorString)); + } // else (dviFile == NULL) + + TextLabel1->setText( text ); +} + + +void infoDialog::setFontInfo(fontPool *fp) +{ + TextLabel2->setText(fp->status()); +} + +void infoDialog::outputReceiver(const QString& _op) +{ + QString op = _op; + op = op.replace( QRegExp("<"), "<" ); + + if (MFOutputReceived == false) { + TextLabel3->setText("<b>"+headline+"</b><br>"); + headline = QString::null; + } + + // It seems that the QTextView wants that we append only full lines. + // We see to that. + pool = pool+op; + int idx = pool.findRev("\n"); + + while(idx != -1) { + QString line = pool.left(idx); + pool = pool.mid(idx+1); + + // If the Output of the kpsewhich program contains a line starting + // with "kpathsea:", this means that a new MetaFont-run has been + // started. We filter these lines out and print them in boldface. + int startlineindex = line.find("kpathsea:"); + if (startlineindex != -1) { + int endstartline = line.find("\n",startlineindex); + QString startLine = line.mid(startlineindex,endstartline-startlineindex); + if (MFOutputReceived) + TextLabel3->append("<hr>\n<b>"+startLine+"</b>"); + else + TextLabel3->append("<b>"+startLine+"</b>"); + TextLabel3->append(line.mid(endstartline)); + } else + TextLabel3->append(line); + idx = pool.findRev("\n"); + } + + MFOutputReceived = true; +} + +void infoDialog::clear(const QString& op) +{ + headline = op; + pool = QString::null; + MFOutputReceived = false; +} +#include "infodialog.moc" diff --git a/kdvi/infodialog.h b/kdvi/infodialog.h new file mode 100644 index 00000000..916d0d13 --- /dev/null +++ b/kdvi/infodialog.h @@ -0,0 +1,58 @@ +// -*- C++ -*- +// infodialog.h +// +// (C) 2001 Stefan Kebekus +// Distributed under the GPL + +#ifndef INFO_KDVI_H +#define INFO_KDVI_H + +#include <kdialogbase.h> + +#include <qstring.h> + +class dvifile; +class fontPool; +class QTextView; +class QWidget; + + +class infoDialog : public KDialogBase +{ + Q_OBJECT + +public: + infoDialog( QWidget* parent = 0 ); + + /** This method is used to set the data coming from the DVI + file. Note that 0 is a permissible argument, that just means: + "no file loaded" */ + void setDVIData(dvifile *dviFile); + + QTextView* TextLabel1; + QTextView* TextLabel2; + QTextView* TextLabel3; + +public slots: + /** This slot is called when Output from the MetaFont programm + is received via the fontpool/kpsewhich */ + void outputReceiver(const QString&); + + /** This slot is called whenever anything in the fontpool has + changed. If the infoDialog is shown, the dialog could then + query the fontpool for more information. */ + void setFontInfo(fontPool *fp); + + /** Calling this slot clears the text view and stores the + headline. The next time output is received via the + outputReceiver, the headline is displayed in bold on top of + the text view. */ + void clear(const QString&); + +protected: + bool MFOutputReceived; + QString headline; + QString pool; +}; + +#endif // INFO_KDVI_H diff --git a/kdvi/kdvi.desktop b/kdvi/kdvi.desktop new file mode 100644 index 00000000..183bef33 --- /dev/null +++ b/kdvi/kdvi.desktop @@ -0,0 +1,90 @@ +[Desktop Entry] +GenericName=DVI Viewer +GenericName[af]=Dvi Aansig +GenericName[ar]=عارض ملفات DVI +GenericName[az]=DVI Nümayişçisi +GenericName[bg]=Преглед на документи DVI +GenericName[br]=Gweler DVI +GenericName[bs]=Preglednik DVI dokumenata +GenericName[ca]=Visualitzador de DVI +GenericName[cs]=Prohlížeč DVI souborů +GenericName[cy]=Gwelydd DVI +GenericName[da]=DVI-fremviser +GenericName[de]=DVI-Betrachter +GenericName[el]=Προβολέας DVI +GenericName[eo]=DVI-rigardilo +GenericName[es]=Visor de documentos DVI +GenericName[et]=DVI failide vaataja +GenericName[eu]=DVI ikustailea +GenericName[fa]=مشاهدهگر DVI +GenericName[fi]=DVI-näytin +GenericName[fr]=Afficheur DVI +GenericName[ga]=Amharcán DVI +GenericName[gl]=Visor de DVI +GenericName[he]=מציג DVI +GenericName[hi]=डीवीआई प्रदर्शक +GenericName[hr]=Preglednik DVI dokumenata +GenericName[hu]=DVI-nézegető +GenericName[id]=Viewer DVI +GenericName[is]=DVI sjá +GenericName[it]=Visore DVI +GenericName[ja]=DVI ビューア +GenericName[kk]=DVI файлдарын қарау +GenericName[km]=កម្មវិធីមើល DVI +GenericName[ko]=DVI 보기 +GenericName[lt]=DVI Žiūriklis +GenericName[lv]=DVI Skatītājs +GenericName[mk]=Прикажувач на DVI +GenericName[ms]=Pemapar DVI +GenericName[mt]=Werrej DVI +GenericName[nb]=DVI-fremviser +GenericName[nds]=DVI-Kieker +GenericName[ne]=DVI दर्शक +GenericName[nl]=DVI-weergaveprogramma +GenericName[nn]=DVI-lesar +GenericName[pa]=DVI ਦਰਸ਼ਕ +GenericName[pl]=Przeglądarka plików DVI +GenericName[pt]=Visualizador de DVIs +GenericName[pt_BR]=Visualizador de DVI +GenericName[ro]=Vizualizor DVI +GenericName[ru]=Просмотр файлов DVI +GenericName[rw]=Ikigaragaza DVI +GenericName[se]=DVI čájeheaddji +GenericName[sk]=Prehliadač DVI súborov +GenericName[sl]=Pregledovalnik datotek DVI +GenericName[sr]=DVI приказивач +GenericName[sr@Latn]=DVI prikazivač +GenericName[sv]=DVI-visare +GenericName[ta]=DVI காட்சி +GenericName[tg]=Намоиши файли DVI +GenericName[th]=ตัวแสดงผล DVI +GenericName[tr]=DVI Görüntüleyici +GenericName[uk]=Переглядач DVI +GenericName[uz]=DVI koʻruvchi +GenericName[uz@cyrillic]=DVI кўрувчи +GenericName[ven]=Muvhoni wa DVI +GenericName[wa]=Håyneu di fitchîs DVI +GenericName[xh]=Umboniseli we DVI +GenericName[zh_CN]=DVI 查看器 +GenericName[zh_HK]=DVI 檢視器 +GenericName[zh_TW]=DVI 檢視器 +GenericName[zu]=Umboniseli we DVI +Name=KDVI +Name[af]=Kdvi +Name[ar]=برنامج KDVI +Name[eo]=DVI-rigardilo +Name[hi]=के-डीवीआई +Name[zh_TW]=KDVI 檢視器 +MimeType=application/x-dvi;application/x-gzdvi;application/x-bz2dvi; +InitialPreference=6 +Exec=kdvi %f -caption "%c" %i %m +Icon=kdvi +Path= +Type=Application +Terminal=false +ServiceTypes=Browser/View +X-KDE-Library=kviewerpart +X-KDE-BrowserView-Args=dvi +DocPath=kdvi/index.html +X-KDE-StartupNotify=true +Categories=Qt;KDE;Graphics; diff --git a/kdvi/kdvi.h b/kdvi/kdvi.h new file mode 100644 index 00000000..c04fe189 --- /dev/null +++ b/kdvi/kdvi.h @@ -0,0 +1,17 @@ +// -*- C++ -*- +// kdvi.h +// +// global variables and definitions for kdvi. +// +// (C) 2000, Stefan Kebekus. Distributed under the GPL. + +#ifndef KDVI_H +#define KDVI_H + +// Define the following flags to generate debugging output + +// #define DEBUG_FONT 1 +// #define DEBUG_FONTPOOL 1 +// #define DEBUG_PK 1 + +#endif diff --git a/kdvi/kdvi.kcfg b/kdvi/kdvi.kcfg new file mode 100644 index 00000000..aa416757 --- /dev/null +++ b/kdvi/kdvi.kcfg @@ -0,0 +1,26 @@ +<?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" > + <include>fontpool.h</include> + <kcfgfile name="kdvirc" /> + <group name="kdvi" > + <entry key="MakePK" type="Bool" > + <label>Use MetaFont to generate missing fonts. If in doubt, enable this option.</label> + <whatsthis>Allows KDVI to use MetaFont to produce bitmap fonts. Unless you have a very specific reason, you probably want to enable this option.</whatsthis> + <default>true</default> + </entry> + <entry key="ShowPS" type="Bool" > + <label>Show PostScript specials. If in doubt, enable this option.</label> + <whatsthis>Some DVI files contain PostScript graphics. If enabled, KDVI will use the Ghostview PostScript interpreter to display these. You probably want to enable this option, unless you have a DVI-file whose PostScript part is broken, or too large for your machine.</whatsthis> + <default>true</default> + </entry> + <entry key="UseFontHints" type="Bool" > + <label>Use font hinting. You should enable this, if the use of font hinting improves readability on your machine.</label> + <whatsthis>Many modern fonts contain "font hinting" information which can be used to improve the appearance of a font on low-resolution displays, such as a computer monitor, or a notebook screen. However, many people find the "improved" fonts quite ugly and prefer to have this option disabled.</whatsthis> + <default>false</default> + </entry> + <entry key="EditorCommand" type="Path" /> + </group> +</kcfg> diff --git a/kdvi/kdvi.lsm b/kdvi/kdvi.lsm new file mode 100644 index 00000000..7c42cd2c --- /dev/null +++ b/kdvi/kdvi.lsm @@ -0,0 +1,14 @@ +Begin3 +Title: kdvi +Version: 0.4 +Entered-date: October 15, 1997 +Description: TeX DVI previewer for the K Desktop Environment +Keywords: KDE, TeX, DVI, X11, Qt +Author: Markku Hihnala <mah@ee.oulu.fi> +Maintained-by: Markku Hihnala <mah@ee.oulu.fi> +Primary-site: ftp://ftp.kde.org/pub/kde/ +Alternate-site: +Original-site: ftp://ftp.kde.org/pub/kde/ +Platforms: Unix, Qt +Copying-policy: GPL +End diff --git a/kdvi/kdvi_multipage.cpp b/kdvi/kdvi_multipage.cpp new file mode 100644 index 00000000..973e4d5f --- /dev/null +++ b/kdvi/kdvi_multipage.cpp @@ -0,0 +1,482 @@ +#include <config.h> +#include <kaction.h> +#include <kaboutdata.h> +#include <kaboutdialog.h> +#include <kapplication.h> +#include <kbugreport.h> +#include <kconfigdialog.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstdaction.h> +#include <ktempfile.h> +#include <ktip.h> +#include <qtimer.h> + +#include <kparts/part.h> +#include <kparts/genericfactory.h> + +#include "kdvi_multipage.h" +#include "documentWidget.h" +#include "dviFile.h" +#include "dviPageCache.h" +#include "dviWidget.h" +#include "fontpool.h" +#include "kprinterwrapper.h" +#include "kviewpart.h" +#include "marklist.h" +#include "optionDialogFontsWidget.h" +#include "optionDialogSpecialWidget.h" +#include "performanceMeasurement.h" +#include "prefs.h" +#include "renderedDocumentPagePixmap.h" + + +#include <qlabel.h> + +//#define KDVI_MULTIPAGE_DEBUG + +#ifdef PERFORMANCE_MEASUREMENT +// These objects are explained in the file "performanceMeasurement.h" +QTime performanceTimer; +int performanceFlag = 0; +#endif + +typedef KParts::GenericFactory<KDVIMultiPage> KDVIMultiPageFactory; +K_EXPORT_COMPONENT_FACTORY(kdvipart, KDVIMultiPageFactory) + + + +KDVIMultiPage::KDVIMultiPage(QWidget *parentWidget, const char *widgetName, QObject *parent, + const char *name, const QStringList& args) + : KMultiPage(parentWidget, widgetName, parent, name), DVIRenderer(parentWidget) +{ + Q_UNUSED(args); +#ifdef PERFORMANCE_MEASUREMENT + performanceTimer.start(); +#endif + + searchUsed = false; + + setInstance(KDVIMultiPageFactory::instance()); + + // Points to the same object as renderer to avoid downcasting. + // FIXME: Remove when the API of the Renderer-class is finished. + DVIRenderer.setName("DVI renderer"); + setRenderer(&DVIRenderer); + + docInfoAction = new KAction(i18n("Document &Info"), "info", 0, &DVIRenderer, SLOT(showInfo()), actionCollection(), "info_dvi"); + embedPSAction = new KAction(i18n("Embed External PostScript Files..."), 0, this, SLOT(slotEmbedPostScript()), actionCollection(), "embed_postscript"); + new KAction(i18n("Enable All Warnings && Messages"), 0, this, SLOT(doEnableWarnings()), actionCollection(), "enable_msgs"); + exportPSAction = new KAction(i18n("PostScript..."), 0, &DVIRenderer, SLOT(exportPS()), actionCollection(), "export_postscript"); + exportPDFAction = new KAction(i18n("PDF..."), 0, &DVIRenderer, SLOT(exportPDF()), actionCollection(), "export_pdf"); + + KStdAction::tipOfDay(this, SLOT(showTip()), actionCollection(), "help_tipofday"); + + setXMLFile("kdvi_part.rc"); + + preferencesChanged(); + + enableActions(false); + // Show tip of the day, when the first main window is shown. + QTimer::singleShot(0,this,SLOT(showTipOnStart())); +} + + +KDVIMultiPage::~KDVIMultiPage() +{ + delete docInfoAction; + delete embedPSAction; + delete exportPSAction; + delete exportPDFAction; + + Prefs::writeConfig(); +} + + +KAboutData* KDVIMultiPage::createAboutData() +{ + KAboutData* about = new KAboutData("kdvi", I18N_NOOP("KDVI"), "1.3", + I18N_NOOP("A previewer for Device Independent files (DVI files) produced by the TeX typesetting system."), + KAboutData::License_GPL, + "Markku Hinhala, Stephan Kebekus", + I18N_NOOP("This program displays Device Independent (DVI) files which are produced by the TeX typesetting system.\n" + "KDVI 1.3 is based on original code from KDVI version 0.43 and xdvik.")); + + about->addAuthor ("Stefan Kebekus", + I18N_NOOP("Current Maintainer."), + "kebekus@kde.org", + "http://www.mi.uni-koeln.de/~kebekus"); + + about->addAuthor ("Markku Hinhala", I18N_NOOP("Author of kdvi 0.4.3")); + about->addAuthor ("Nicolai Langfeldt", I18N_NOOP("Maintainer of xdvik")); + about->addAuthor ("Paul Vojta", I18N_NOOP("Author of xdvi")); + about->addCredit ("Philipp Lehmann", I18N_NOOP("Testing and bug reporting.")); + about->addCredit ("Wilfried Huss", I18N_NOOP("Re-organisation of source code.")); + + return about; +} + + +void KDVIMultiPage::slotEmbedPostScript() +{ + DVIRenderer.embedPostScript(); + emit askingToCheckActions(); +} + + +void KDVIMultiPage::setEmbedPostScriptAction() +{ + if ((DVIRenderer.dviFile == 0) || (DVIRenderer.dviFile->numberOfExternalPSFiles == 0)) + embedPSAction->setEnabled(false); + else + embedPSAction->setEnabled(true); +} + + +void KDVIMultiPage::slotSave() +{ + // 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 (0, i18n("The file %1\nexists. Do you want to overwrite that file?").arg(fileName), + i18n("Overwrite File"), i18n("Overwrite")); + if (r == KMessageBox::Cancel) + return; + } + + // TODO: error handling... + if ((DVIRenderer.dviFile != 0) && (DVIRenderer.dviFile->dvi_Data() != 0)) + DVIRenderer.dviFile->saveAs(fileName); + + return; +} + + +void KDVIMultiPage::slotSave_defaultFilename() +{ + // TODO: error handling... + if (DVIRenderer.dviFile != 0) + DVIRenderer.dviFile->saveAs(m_file); + return; +} + + +void KDVIMultiPage::setFile(bool r) +{ + enableActions(r); +} + + +QStringList KDVIMultiPage::fileFormats() const +{ + QStringList r; + r << i18n("*.dvi *.DVI|TeX Device Independent Files (*.dvi)"); + return r; +} + + +void KDVIMultiPage::addConfigDialogs(KConfigDialog* configDialog) +{ + static optionDialogFontsWidget* fontConfigWidget = 0; + + fontConfigWidget = new optionDialogFontsWidget(scrollView()); + optionDialogSpecialWidget* specialConfigWidget = new optionDialogSpecialWidget(scrollView()); + + configDialog->addPage(fontConfigWidget, Prefs::self(), i18n("TeX Fonts"), "fonts"); + configDialog->addPage(specialConfigWidget, Prefs::self(), i18n("DVI Specials"), "dvi"); + configDialog->setHelp("preferences", "kdvi"); +} + + +void KDVIMultiPage::preferencesChanged() +{ + // Call method from parent class + KMultiPage::preferencesChanged(); +#ifdef KDVI_MULTIPAGE_DEBUG + kdDebug(4300) << "preferencesChanged" << endl; +#endif + + bool showPS = Prefs::showPS(); + bool useFontHints = Prefs::useFontHints(); + + DVIRenderer.setPrefs( showPS, Prefs::editorCommand(), useFontHints); +} + + +void KDVIMultiPage::print() +{ + // Obtain a fully initialized KPrinter structure, and disable all + // entries in the "Page Size & Placement" tab of the printer dialog. + KPrinter *printer = getPrinter(false); + // Abort with an error message if no KPrinter could be initialized + if (printer == 0) { + kdError(4300) << "KPrinter not available" << endl; + return; + } + + // Show the printer options dialog. Return immediately if the user + // aborts. + if (!printer->setup(parentWdg, i18n("Print %1").arg(m_file.section('/', -1)) )) + return; + + // This funny method call is necessary for the KPrinter to return + // proper results in printer->orientation() below. It seems that + // KPrinter does some options parsing in that method. + ((KDVIPrinterWrapper *)printer)->doPreparePrinting(); + if (printer->pageList().isEmpty()) { + KMessageBox::error( scrollView(), + i18n("The list of pages you selected was empty.\n" + "Maybe you made an error in selecting the pages, " + "e.g. by giving an invalid range like '7-2'.") ); + return; + } + + // Turn the results of the options requestor into a list arguments + // which are used by dvips. + QString dvips_options = QString::null; + // Print in reverse order. + if ( printer->pageOrder() == KPrinter::LastPageFirst ) + dvips_options += "-r "; + // Print only odd pages. + if ( printer->pageSet() == KPrinter::OddPages ) + dvips_options += "-A "; + // Print only even pages. + if ( printer->pageSet() == KPrinter::EvenPages ) + dvips_options += "-B "; + // We use the printer->pageSize() method to find the printer page + // size, and pass that information on to dvips. Unfortunately, dvips + // does not understand all of these; what exactly dvips understands, + // depends on its configuration files. Consequence: expect problems + // with unusual paper sizes. + switch( printer->pageSize() ) { + case KPrinter::A4: + dvips_options += "-t a4 "; + break; + case KPrinter::B5: + dvips_options += "-t b5 "; + break; + case KPrinter::Letter: + dvips_options += "-t letter "; + break; + case KPrinter::Legal: + dvips_options += "-t legal "; + break; + case KPrinter::Executive: + dvips_options += "-t executive "; + break; + case KPrinter::A0: + dvips_options += "-t a0 "; + break; + case KPrinter::A1: + dvips_options += "-t a1 "; + break; + case KPrinter::A2: + dvips_options += "-t a2 "; + break; + case KPrinter::A3: + dvips_options += "-t a3 "; + break; + case KPrinter::A5: + dvips_options += "-t a5 "; + break; + case KPrinter::A6: + dvips_options += "-t a6 "; + break; + case KPrinter::A7: + dvips_options += "-t a7 "; + break; + case KPrinter::A8: + dvips_options += "-t a8 "; + break; + case KPrinter::A9: + dvips_options += "-t a9 "; + break; + case KPrinter::B0: + dvips_options += "-t b0 "; + break; + case KPrinter::B1: + dvips_options += "-t b1 "; + break; + case KPrinter::B10: + dvips_options += "-t b10 "; + break; + case KPrinter::B2: + dvips_options += "-t b2 "; + break; + case KPrinter::B3: + dvips_options += "-t b3 "; + break; + case KPrinter::B4: + dvips_options += "-t b4 "; + break; + case KPrinter::B6: + dvips_options += "-t b6 "; + break; + case KPrinter::B7: + dvips_options += "-t b7 "; + break; + case KPrinter::B8: + dvips_options += "-t b8 "; + break; + case KPrinter::B9: + dvips_options += "-t b9 "; + break; + case KPrinter::C5E: + dvips_options += "-t c5e "; + break; + case KPrinter::Comm10E: + dvips_options += "-t comm10e "; + break; + case KPrinter::DLE: + dvips_options += "-t dle "; + break; + case KPrinter::Folio: + dvips_options += "-t folio "; + break; + case KPrinter::Ledger: + dvips_options += "-t ledger "; + break; + case KPrinter::Tabloid: + dvips_options += "-t tabloid "; + break; + default: + break; + } + // Orientation + if ( printer->orientation() == KPrinter::Landscape ) + dvips_options += "-t landscape "; + + + // List of pages to print. + QValueList<int> pageList = printer->pageList(); + dvips_options += "-pp "; + int commaflag = 0; + for( QValueList<int>::ConstIterator it = pageList.begin(); it != pageList.end(); ++it ) { + if (commaflag == 1) + dvips_options += QString(","); + else + commaflag = 1; + dvips_options += QString("%1").arg(*it); + } + + // Now print. For that, export the DVI-File to PostScript. Note that + // dvips will run concurrently to keep the GUI responsive, keep log + // of dvips and allow abort. Giving a non-zero printer argument + // means that the dvi-widget will print the file when dvips + // terminates, and then delete the output file. + KTempFile tf; + DVIRenderer.exportPS(tf.name(), dvips_options, printer); + + // "True" may be a bit euphemistic. However, since dvips runs + // concurrently, there is no way of telling the result of the + // printing command at this stage. + return; +} + + +void KDVIMultiPage::enableActions(bool b) +{ + KMultiPage::enableActions(b); + + docInfoAction->setEnabled(b); + exportPSAction->setEnabled(b); + exportPDFAction->setEnabled(b); + + setEmbedPostScriptAction(); +} + + +void KDVIMultiPage::doEnableWarnings() +{ + KMessageBox::information (scrollView(), i18n("All messages and warnings will now be shown.")); + KMessageBox::enableAllMessages(); + KTipDialog::setShowOnStart(true); +} + + +void KDVIMultiPage::showTip() +{ + KTipDialog::showTip(scrollView(), "kdvi/tips", true); +} + + +void KDVIMultiPage::showTipOnStart() +{ + KTipDialog::showTip(scrollView(), "kdvi/tips"); +} + + +DocumentWidget* KDVIMultiPage::createDocumentWidget() +{ + DVIWidget* documentWidget = new DVIWidget(scrollView()->viewport(), scrollView(), pageCache, + "singlePageWidget" ); + + // Lets not forget the connections we make in the KMultiPage + connect(documentWidget, SIGNAL(clearSelection()), this, SLOT(clearSelection())); + connect(this, SIGNAL(enableMoveTool(bool)), documentWidget, SLOT(slotEnableMoveTool(bool))); + + // Handle source links + connect(documentWidget, SIGNAL(SRCLink(const QString&, QMouseEvent*, DocumentWidget*)), getRenderer(), + SLOT(handleSRCLink(const QString& ,QMouseEvent*, DocumentWidget*))); + + return documentWidget; +} + + +void KDVIMultiPage::initializePageCache() +{ + pageCache = new DVIPageCache(); +} + + +void KDVIMultiPage::showFindTextDialog() +{ + if ((getRenderer().isNull()) || (getRenderer()->supportsTextSearch() == false)) + return; + + if (!searchUsed) + { + // WARNING: This text appears several times in the code. Change + // everywhere, or nowhere! + if (KMessageBox::warningContinueCancel( scrollView(), + i18n("<qt>This function searches the DVI file for plain text. Unfortunately, this version of " + "KDVI treats only plain ASCII characters properly. Symbols, ligatures, mathematical " + "formulae, accented characters, and non-English text, such as Russian or Korean, will " + "most likely be messed up completely. Continue anyway?</qt>"), + i18n("Function May Not Work as Expected"), + KStdGuiItem::cont(), + "warning_search_text_may_not_work") == KMessageBox::Cancel) + return; + + // Remember that we don't need to show the warning message again. + searchUsed = true; + } + + // Now really show the search widget + KMultiPage::showFindTextDialog(); +} + +#include "kdvi_multipage.moc" diff --git a/kdvi/kdvi_multipage.h b/kdvi/kdvi_multipage.h new file mode 100644 index 00000000..713afab7 --- /dev/null +++ b/kdvi/kdvi_multipage.h @@ -0,0 +1,96 @@ +// -*- C++ -*- +#ifndef KDVIMULTIPAGE_H +#define KDVIMULTIPAGE_H + +#include "kmultipage.h" +#include "dviRenderer.h" + +#include <qstringlist.h> + +class KPrinter; + +class KDVIMultiPage : public KMultiPage +{ + Q_OBJECT + +public: + KDVIMultiPage(QWidget *parentWidget, const char *widgetName, QObject *parent, + const char *name, const QStringList& args = QStringList()); + virtual ~KDVIMultiPage(); + +// Interface definition start ------------------------------------------------ + + /// returns the list of supported file formats + virtual QStringList fileFormats() const; + + virtual void setFile(bool r); + + virtual void print(); + + /// KDVI offers read- and write functionality must re-implement this + /// method and return true here. + virtual bool isReadWrite() {return true;} + + virtual void addConfigDialogs(KConfigDialog* configDialog); + + static KAboutData* createAboutData(); + +private: + virtual DocumentWidget* createDocumentWidget(); + + virtual void initializePageCache(); + + /** Used to enable the export menu when a file is successfully + loaded. */ + virtual void enableActions(bool); + +public slots: + /** Opens a file requestor and saves. This really saves the content + of the DVI-file, and does not just start a copy job */ + virtual void slotSave(); + + /** Similar to slotSave, but does not ask for a filename. */ + virtual void slotSave_defaultFilename(); + + void setEmbedPostScriptAction(); + + void slotEmbedPostScript(); + + virtual void preferencesChanged(); + + /** Shows the "text search" dialog, if text search is supported by + the renderer. Otherwise, the method returns immediately. + We reimplement this slot to show a warning message that informs the + user about the currently limited search capabilities of KDVI. */ + virtual void showFindTextDialog(); + +protected slots: + void doExportText(); + void doEnableWarnings(); + + void showTip(); + void showTipOnStart(); + +private: + // Points to the same object as renderer to avoid downcasting. + // FIXME: Remove when the API of the Renderer-class is finished. + dviRenderer DVIRenderer; + + // Set to true if we used the search function atleast once. + // It is used to remember if we already have show the warning message. + bool searchUsed; + + /************************************************************* + * Methods and classes concerned with the find functionality * + *************************************************************/ + + /** Pointers to several actions which are disabled if no file is + loaded. */ + KAction *docInfoAction; + KAction *embedPSAction; + KAction *exportPDFAction; + KAction *exportPSAction; +}; + + +#endif diff --git a/kdvi/kdvi_multipage_texthandling.cpp b/kdvi/kdvi_multipage_texthandling.cpp new file mode 100644 index 00000000..c667584e --- /dev/null +++ b/kdvi/kdvi_multipage_texthandling.cpp @@ -0,0 +1,70 @@ +// +// Class: kdvi_multipage +// Author: Stefan Kebekus +// +// (C) 2001-2005, Stefan Kebekus. +// +// Previewer for TeX DVI files. +// +// 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. +// +// Please report bugs or improvements, etc. via the "Report bug"-Menu +// of kdvi. + +#include <config.h> + +#include <kaction.h> +#include <kdebug.h> +#include <keditcl.h> +#include <kfiledialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <qapplication.h> +#include <qprogressdialog.h> + +#include "kdvi_multipage.h" +#include "dviFile.h" +#include "documentWidget.h" +#include "renderedDocumentPagePixmap.h" + + +//#define KDVI_MULTIPAGE_DEBUG + + +void KDVIMultiPage::doExportText() +{ +#ifdef KDVI_MULTIPAGE_DEBUG + kdDebug(4300) << "KDVIMultiPage::doExportText() called" << endl; +#endif + + // Paranoid safety checks + if (DVIRenderer.dviFile == 0) + return; + if (DVIRenderer.dviFile->dvi_Data() == 0 ) + return; + + if (KMessageBox::warningContinueCancel( scrollView(), + i18n("<qt>This function exports the DVI file to a plain text. Unfortunately, this version of " + "KDVI treats only plain ASCII characters properly. Symbols, ligatures, mathematical " + "formulae, accented characters, and non-English text, such as Russian or Korean, will " + "most likely be messed up completely.</qt>"), + i18n("Function May Not Work as Expected"), + i18n("Continue Anyway"), + "warning_export_to_text_may_not_work") == KMessageBox::Cancel) + return; + + KMultiPage::doExportText(); +} diff --git a/kdvi/kdvi_part.rc b/kdvi/kdvi_part.rc new file mode 100644 index 00000000..ded5f346 --- /dev/null +++ b/kdvi/kdvi_part.rc @@ -0,0 +1,26 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kdvi_part" version="4"> +<MenuBar> + <Menu name="file"><text>&File</text> + <Action name="info_dvi" group="info_merge"/> + <Menu name="export" group="print_merge" ><text>Export As</text> + <Action name="export_postscript" group="export_merge"/> + <Action name="export_pdf" group="export_merge"/> + </Menu> + </Menu> + + <Menu name="edit"><text>&Edit</text> + <Separator/> + <Action name="embed_postscript"/> + </Menu> + + <Menu name="settings"><text>&Settings</text> + <Action name="enable_msgs" group="additional_settings_merge"/> + </Menu> + + <Menu name="help"><text>&Help</text> + <Action name="help_tipofday" group="help_top"/> + </Menu> +</MenuBar> + +</kpartgui> diff --git a/kdvi/kdvimultipage.desktop b/kdvi/kdvimultipage.desktop new file mode 100644 index 00000000..48a5e1fb --- /dev/null +++ b/kdvi/kdvimultipage.desktop @@ -0,0 +1,18 @@ +[Desktop Entry] +Type=Service +Comment=DVI +Name=KDVIMultiPage +Name[es]=KDVIMultiPágina +Name[fr]=Multi-page KDVI +Name[hu]=KDVITöbbOldalas +Name[nb]=KDVI Flerside +Name[ne]=केडीभीआई बहुपृष्ठ +Name[nl]=KDVIMultiPagina +Name[nn]=KDVI-fleirside +Name[pt]=KDVIMultiPágina +Name[ro]=KDVI Pagini Multiple +Name[sv]=KDVI flera sidor +ServiceTypes=KViewShell/MultiPage +X-KDE-Library=kdvipart +X-KDE-MimeTypes=application/x-dvi +X-KDE-MultiPageVersion=2 diff --git a/kdvi/kprinterwrapper.h b/kdvi/kprinterwrapper.h new file mode 100644 index 00000000..03959361 --- /dev/null +++ b/kdvi/kprinterwrapper.h @@ -0,0 +1,24 @@ +// -*- C++ -*- +// kprinterwrapper.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + + +#ifndef _PRINTERWRAPPER_H +#define _PRINTERWRAPPER_H + +#include "kprinter.h" + + +class KDVIPrinterWrapper : public KPrinter +{ +public: + KDVIPrinterWrapper() : KPrinter(true, QPrinter::ScreenResolution) {; }; + + void doPreparePrinting() { preparePrinting(); }; +}; + +#endif diff --git a/kdvi/main.cpp b/kdvi/main.cpp new file mode 100644 index 00000000..4340f0e8 --- /dev/null +++ b/kdvi/main.cpp @@ -0,0 +1,154 @@ +#include <config.h> + +#include <dcopclient.h> +#include <dcopref.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kdebug.h> +#include <kurl.h> +#include <klocale.h> +#include <kaboutdata.h> +#include <qdir.h> + +#include <stdlib.h> + +#include "kviewshell.h" + + +static KCmdLineOptions options[] = +{ + { "u", 0, 0 }, + { "unique", I18N_NOOP("Check if the file is loaded in another KDVI.\nIf it is, bring up the other KDVI. Otherwise, load the file."), 0 }, + { "g", 0, 0 }, + { "goto <pagenumber>", I18N_NOOP("Navigate to this page"), 0 }, + { "+file(s)", I18N_NOOP("Files to load"), 0 }, + KCmdLineLastOption +}; + + +static const char description[] = I18N_NOOP("A previewer for Device Independent files (DVI files) produced by the TeX typesetting system."); + + +int main(int argc, char** argv) +{ + KAboutData about ("kdvi", I18N_NOOP("KDVI"), "1.4", + description, KAboutData::License_GPL, + "Markku Hinhala, Stephan Kebekus", + I18N_NOOP("This program displays Device Independent (DVI) files which are produced by the TeX typesetting system.\n" + "This KDVI version is based on original code from KDVI version 0.43 and xdvik.")); + + about.addAuthor ("Stefan Kebekus", + I18N_NOOP("Current Maintainer."), + "kebekus@kde.org", + "http://www.mi.uni-koeln.de/~kebekus"); + + about.addAuthor ("Markku Hinhala", I18N_NOOP("Author of kdvi 0.4.3")); + about.addAuthor ("Nicolai Langfeldt", I18N_NOOP("Maintainer of xdvik")); + about.addAuthor ("Paul Vojta", I18N_NOOP("Author of xdvi")); + about.addCredit ("Philipp Lehmann", I18N_NOOP("Testing and bug reporting.")); + about.addCredit ("Wilfried Huss", I18N_NOOP("Re-organisation of source code.")); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions(options); + KApplication app; + + // see if we are starting with session management + if (app.isRestored()) + { + RESTORE(KViewShell); + } + else + { + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + + if (args->isSet("unique")) + { + // With --unique, we need 2 arguments. + if (args->count() < 1) + { + args->usage(); + exit(-1); + } + + // Find the fully qualified file name of the file we are + // loading. Complain, if we are given a URL which does not point + // to a local file. + KURL url(args->url(0)); + + if (!url.isValid()) + { + kdError(4300) << QString(I18N_NOOP("The URL %1 is not well-formed.")).arg(args->arg(0)) << endl; + return -1; + } + + if (!url.isLocalFile()) + { + kdError(4300) << QString(I18N_NOOP("The URL %1 does not point to a local file. You can only specify local " + "files if you are using the '--unique' option.")).arg(args->arg(0)) << endl; + return -1; + } + + QString qualPath = QFileInfo(url.path()).absFilePath(); + + app.dcopClient()->attach(); + // We need to register as "kviewshell" to stay compatible with existing DCOP-skripts. + QCString id = app.dcopClient()->registerAs("unique-kviewshell"); + if (id.isNull()) + kdError(4300) << "There was an error using dcopClient()->registerAs()." << endl; + QCStringList apps = app.dcopClient()->registeredApplications(); + for ( QCStringList::Iterator it = apps.begin(); it != apps.end(); ++it ) + { + if ((*it).find("kviewshell") == 0) + { + QByteArray data, replyData; + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + bool result; + arg << qualPath.stripWhiteSpace(); + if (!app.dcopClient()->call( *it, "kmultipage", "is_file_loaded(QString)", data, replyType, replyData)) + kdError(4300) << "There was an error using DCOP." << endl; + else + { + QDataStream reply(replyData, IO_ReadOnly); + if (replyType == "bool") + { + reply >> result; + if (result == true) + { + if (app.dcopClient()->send( *it, "kmultipage", "jumpToReference(QString)", url.ref()) == true) + { + app.dcopClient()->detach(); + return 0; + } + } + } + else + kdError(4300) << "The DCOP function 'doIt' returned an unexpected type of reply!"; + } + } + } + } + + // We need to register as "kviewshell" to stay compatible with existing DCOP-skripts. + app.dcopClient()->registerAs("kviewshell"); + KViewShell* shell = new KViewShell("application/x-dvi"); + shell->show(); + app.processEvents(); + + if (args->count() > 0) + { + KURL url = args->url(0); + if (!url.hasRef() && args->isSet("goto")) + { + // If the url doesn't already has a reference part, add the + // argument of --goto to the url as reference, to make the + // KViewShell jump to this page. + QString reference = args->getOption("goto"); + url.setHTMLRef(reference); + } + shell->openURL(url); + } + } + + return app.exec(); +} diff --git a/kdvi/make/ChangeLog b/kdvi/make/ChangeLog new file mode 100644 index 00000000..7f808ed6 --- /dev/null +++ b/kdvi/make/ChangeLog @@ -0,0 +1,370 @@ +Thu Apr 13 11:03:10 1995 Ulrik Vieth <vieth@thphy.uni-duesseldorf.de> + + * paths.make: Add new variables for MetaPost directories: + mpinputdir, memdir, mppooldir. + + * makevars.make: Make sure that new variables for MetaPost + directories are passed on to sub-makes. + +Sun Jan 8 12:16:36 1995 Karl Berry <karl@cs.umb.edu> + + * kpathsea 2.6/dviljk 2.5/dvipsk 5.58f/xdvik 18f. + +Wed Jan 4 12:41:25 1995 Karl Berry <karl@cs.umb.edu> + + * tkpathsea.make (kpathsea): Don't depend on texmf.cnf, since it + doesn't exist at the first make. + +Tue Jan 3 13:43:12 1995 Karl Berry <karl@cs.umb.edu> + + * rdepend.make (depend): paths.h is not in the srcdir. + + * config.make (autoconf): Add acsite.m4. + + * dist.make (top_files): FTP belongs here, not in ln_files. + +Sun Jan 1 14:02:42 1995 Karl Berry <karl@cs.umb.edu> + + * makevars.make (makevars): Include web2cdir. + * paths.make (web2cdir): New directory. Suggested by Joachim. + +Sat Dec 31 14:35:29 1994 Karl Berry <karl@cs.umb.edu> + + * tmtpk.make: Just incorporate this in kpathsea/Makefile.in now. + + * rdepend.make (depend): Depend on ourselves. + +Fri Dec 30 15:50:37 1994 Karl Berry <karl@cs.umb.edu> + + * rdepend.make (depend): Use kpathsea_srcdir, not kpathsea_dir. + From Joachim. + +Wed Dec 28 14:16:50 1994 Karl Berry <karl@cs.umb.edu> + + * dist.make (ln_files): Add FTP. + +Mon Dec 26 10:31:14 1994 Karl Berry <karl@cs.umb.edu> + + * dist.make (dist): Copy aclocal.m4 from acsite.m4. + Suggested by interran@uluru.Stanford.EDU (John Interrante). + +Wed Dec 14 15:17:42 1994 Karl Berry <karl@cs.umb.edu> + + * kpathsea 2.5/dviljk 2.4/dvipsk 5.58e/xdvik 18e. + +Sun Dec 11 13:23:12 1994 Karl Berry <karl@cs.umb.edu> + + * rdepend.make (depend): Remove system include files that are + alone on a line. + +Fri Nov 25 09:21:02 1994 Karl Berry <karl@cs.umb.edu> + + * tmtpk.make (MakeTeXPK): Depend on the new filename. + +Tue Nov 15 15:28:14 1994 Karl Berry <karl@cs.umb.edu> + + * tkpathsea.make (makeargs): Change MAKEARGS to XMAKEARGS. + + * targets.make (makeargs): Don't bother to pass $(SHELL). + +Tue Nov 8 19:12:45 1994 Karl Berry <karl@cs.umb.edu> + + * common.make (CFLAGS): Don't include -g, since now it's automatic. + +Sun Nov 6 15:53:36 1994 Karl Berry <karl@cs.umb.edu> + + * paths.make (prefix, exec_prefix): These value are now @prefix@ + and @exec_prefix@. + + * common.make: Call @SET_MAKE@. + + * misc.make (distclean): Remove config.log and config.cache. + + * programs.make (LDFLAGS): Add @LDFLAGS@. + * common.make (CPPFLAGS): Add @CPPFLAGS@. + (CFLAGS): Add @CFLAGS@. + + * dist.make (top_files): Distribute install-sh, not install.sh, + for Autoconf 2.0. + +Sun Oct 30 16:15:34 1994 Karl Berry <karl@cs.umb.edu> + + * config.make (ac_dir): This is now $(gnu)/share. + +Tue Oct 25 17:48:02 1994 Karl Berry <karl@cs.umb.edu> + + * kpathsea 2.3/dviljk 2.3/dvipsk 5.58c/xdvik 18d. + +Sun Oct 23 17:33:56 1994 Karl Berry <karl@cs.umb.edu> + + * targets.make (MakeTeXPK): Make sed substitutions global. + Reported by wfranzki@hlrserv.hlrz.kfa-juelich.de. + +Mon Oct 17 13:28:41 1994 Karl Berry <karl@cs.umb.edu> + + * paths.make (mfpooldir): Doc fix. + +Fri Oct 14 10:31:35 1994 Karl Berry <karl@cs.umb.edu> + + * kpathsea 2.2/dviljk 2.2/dvipsk 5.58b/xdvik 18c. + +Mon Oct 10 15:31:06 1994 Karl Berry <karl@cs.umb.edu> + + * common.make (.SUFFIXES): Declare .c.o. + + * programs.make (LOADLIBES): Omit LEXLIB here. + +Sun Sep 25 15:54:36 1994 Karl Berry <karl@cs.umb.edu> + + * rdepend.make: Doc fix. + + * library.make: New file. + + * makevars.make (makevars): Remove MAKEARGS from here. + + * programs.make (CCLD, link_command): New variables. + (LOADLIBES): Add proglib, LEXLIB. + +Mon Sep 12 11:06:14 1994 Karl Berry (karl@cs.umb.edu) + + * kpathsea 2.1/dviljk 2.1/dvipsk 5.58a/xdvik 18b. + +Sun Sep 11 14:44:21 1994 Karl Berry (karl@cs.umb.edu) + + * targets.make (install-MakeTeXPK): Install this if it didn't + exist, and mkdirchain $(scriptdir). + + * dist.make (top_files): Include aclocal.m4. + +Sat Sep 10 13:40:10 1994 Karl Berry (karl@cs.umb.edu) + + * texi.make (.texi.dvi): No -o option to texi2dvi. + +Thu Sep 8 14:31:59 1994 Karl Berry (karl@cs.umb.edu) + + * kpathsea 2.0, dviljk 2.0, dvipsk 5.55b, xdvik 18a. + +Tue Sep 6 11:39:06 1994 Karl Berry (karl@cs.umb.edu) + + * targets.make (MakeTeXPK): Use psheaderdir, not psconfigdir, and + depend on ourselves. + +Sat Sep 3 08:37:11 1994 Karl Berry (karl@cs.umb.edu) + + * paths.make (psconfigdir): Toss this. + + * misc.make (distclean): Add MakeTeXPK. + + * rdepend.make: Rename from depend.make. + +Fri Sep 2 13:29:14 1994 Karl Berry (karl@cs.umb.edu) + + * targets.make (makeargs, installargs): Declare these here. + + * makevars.make (makevars): No need for ??_fontdir or psmacrodir. + + * misc.make (TAGS): Omit -t, use -i, for Emacs 19.25's etags. + +Thu Sep 1 17:51:10 1994 Karl Berry (karl@cs.umb.edu) + + * dist.make (top_files): Add install.sh. + +Tue Aug 30 14:46:18 1994 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): Touch *.info* if they exist. + +Mon Aug 29 16:28:19 1994 Karl Berry (karl@cs.umb.edu) + + * paths.make (dcfontdir, sauterdir): Move these here, since + everyone has MakeTeXPK now. + +Sun Aug 28 17:09:09 1994 Karl Berry (karl@cs.umb.edu) + + * common.make (INSTALL_FONTS): New variable. + +Thu Aug 25 17:04:43 1994 Karl Berry (karl@cs.umb.edu) + + * kpathsea.make (kpathsea): Also depend on texmf.cnf.in. + + * paths.make (texmf_prefix): Rename to texmf; change uses. + +Sun Aug 21 11:03:48 1994 Karl Berry (karl@cs.umb.edu) + + * programs.make: New file for driver-specific stuff. + + * paths.make (fontnamedir): New definition. + (configdir, headerdir): Prepend with `ps'. + +Sat Aug 13 17:19:53 1994 Karl Berry (karl@cs.umb.edu) + + * misc.make (mostlyclean): Don't remove $(lib), since we've tossed + that. + +Sun Jul 31 14:18:28 1994 Karl Berry (karl@cs.umb.edu) + + * paths.make (DB_DIR, DB_NAME): Remove from here. + +Fri Jul 29 14:56:47 1994 Karl Berry (karl@cs.umb.edu) + + * depend.make (depend): Add dvilj4l.o to the special cases. + +Sun Jul 17 11:37:57 1994 Karl Berry (karl@cs.umb.edu) + + * paths.make (db_dir): Use $TEXMF. + +Mon Jun 27 17:32:47 1994 Karl Berry (karl@cs.umb.edu) + + * paths.make (db_dir): Use $TEXMFROOT. + +Tue Jun 14 12:41:33 1994 Karl Berry (karl@cs.umb.edu) + + * depend.make (depend): No need for depend_encies, I think. + +Mon May 30 13:50:34 1994 Karl Berry (karl@cs.umb.edu) + + * common.make (LDFLAGS): Don't include $(CFLAGS), for Linux's sake. + +Tue May 24 13:26:05 1994 Karl Berry (karl@cs.umb.edu) + + * config.make (stamp-auto, stamp-auto.in): New targets, to avoid + rerunning autoheader/autoconf even when they don't change the main + output files. + +Sun Apr 17 16:11:34 1994 Karl Berry (karl@ra.cs.umb.edu) + + * config.make (configure): Change ; to && in case the cd fails. + +Thu Mar 24 11:12:56 1994 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): Don't append kutil/ChangeLog to the source + ChangeLog. + + * misc.make (extraclean): Don't delete patch*, since that kills + patchlevel.h. + +Sat Mar 5 13:48:15 1994 Karl Berry (karl@cs.umb.edu) + + * common.make (LOADLIBES): Include XLOADLIBES. + +Fri Feb 25 14:21:17 1994 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): Append kutil/ChangeLog to the top level, not + the main program. + +Thu Feb 24 16:11:37 1994 Karl Berry (karl@cs.umb.edu) + + * misc.make (clean): Remove *.lj here. + + * paths.make ({bh,cg,mt}_fontdir, install_fonts): Add these. + +Mon Feb 21 14:04:26 1994 Karl Berry (karl@cs.umb.edu) + + * misc.make (distclean): remove pool files here. + +Wed Feb 16 15:18:13 1994 Karl Berry (karl@cs.umb.edu) + + * paths.make: Doc fix. + +Sun Jan 23 17:17:37 1994 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): Don't fake kpathsea/MACHINES any more, now we + have a real one. + +Fri Jan 14 14:53:12 1994 Karl Berry (karl@cs.umb.edu) + + * paths.make ({tex,mf}pooldir, fmtdir, basedir): Use + $(texmf_prefix)/ini for all these. + +Tue Dec 21 19:23:29 1993 Karl Berry (karl@cs.umb.edu) + + * common.make (LDFLAGS): Don't include $(x_lib_flags) here -- + winds up getting included twice for virmf + +Tue Dec 14 17:40:23 1993 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): Dist the top-level and kutil/ChangeLog. + + * paths.make (formatdir): Rename to fmtdir. + (texprefix): Rename to texmf_prefix, change uses accordingly. + +Fri Dec 10 17:50:39 1993 Karl Berry (karl@cs.umb.edu) + + * paths.make (dvipsprefix): Rename to dvips_prefix. + +Sun Nov 14 11:52:33 1993 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): Do not depend on depend.make and TAGS, since + web2c doesn't have them. + + * paths.make: Change defaults for new hierarchy. + +Thu Nov 11 11:07:22 1993 Karl Berry (karl@cs.umb.edu) + + * common.make (CPPFLAGS, LDFLAGS): xincludedir, xlibdir, wlibs + names have changed. + +Sun Nov 7 15:22:32 1993 Karl Berry (karl@cs.umb.edu) + + * paths.h: Give dire warning that editing Makefiles will not + rebuild paths.h. + +Fri Oct 29 14:01:57 1993 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): chmod a+rw. + +Thu Oct 28 17:48:01 1993 Karl Berry (karl@cs.umb.edu) + + * common.make (CPPFLAGS): Include -I. before -I$(srcdir). + +Fri Oct 22 13:08:19 1993 Karl Berry (karl@cs.umb.edu) + + * paths.make: Remove the paths, and add the dvips directories. + + * common.make (kpathsea_srcdir{,_parent}): Define. From + simon@lia.di.epfl.ch. + +Tue Oct 19 15:59:03 1993 Karl Berry (karl@cs.umb.edu) + + * config.make (stamp-c-auto): New target. + (c-auto.h): Depend on it. + +Sat Oct 9 07:04:45 1993 Karl Berry (karl@cs.umb.edu) + + * misc.make (mostlyclean): Remove programs. + +Sun Oct 3 12:44:04 1993 Karl Berry (karl@cs.umb.edu) + + * misc.make (extraclean): Also remove .blg and .bbl, .vf and .vpl. + (clean): Remove *.pool. + +Tue Sep 28 13:11:01 1993 Karl Berry (karl@cs.umb.edu) + + * common.make (CPPFLAGS): Add $(xincludedir) again; when did I + remove it? + +Fri Sep 24 07:53:45 1993 Karl Berry (karl@cs.umb.edu) + + * common.make (warn_more) [kpathsea]: Move to kpathsea's Makefile. + + * texi.make (.texi.dvi): New rule. + + * common.make (warn_more): Had -pointer-arith twice. + +Thu Sep 23 17:42:42 1993 Karl Berry (karl@cs.umb.edu) + + * common.make (autoconf): Toss aclocal.m4. + * dist.make (top_files): Ditto. + + * common.make (autoheader): New variable, split off from autoconf. + +Sun Aug 29 11:30:39 1993 Karl Berry (karl@cs.umb.edu) + + * dist.make (dist): Remove MACHINES in kpathsea. + + * common.make (CPPFLAGS): Remove the -I. Why did I put it there? + +Sat Aug 28 07:01:52 1993 Karl Berry (karl@cs.umb.edu) + + * unbackslsh.awk: New file. + + * common.make (CPPFLAGS): Add -I before $(xincludedir). diff --git a/kdvi/make/README b/kdvi/make/README new file mode 100644 index 00000000..f91b3d16 --- /dev/null +++ b/kdvi/make/README @@ -0,0 +1,3 @@ +make -- this subdirectory contains Makefile fragments. +configure substitutes them for ac_include lines in Makefile.in. +(This is an enhancement to standard Autoconf; see aclocal.m4.) diff --git a/kdvi/make/common.make b/kdvi/make/common.make new file mode 100644 index 00000000..9d9f822d --- /dev/null +++ b/kdvi/make/common.make @@ -0,0 +1,42 @@ +# common.make -- used by all Makefiles. +SHELL = /bin/sh +@SET_MAKE@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +# CFLAGS is used for both compilation and linking. +CFLAGS = @CFLAGS@ $(XCFLAGS) + +# Do not override CPPFLAGS; change XCPPFLAGS, CFLAGS, XCFLAGS, or DEFS instead. +CPPFLAGS = $(XCPPFLAGS) -I. -I$(srcdir) \ + -I$(kpathsea_parent) -I$(kpathsea_srcdir_parent) \ + $(prog_cflags) @CPPFLAGS@ $(DEFS) +.c.o: + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< +.SUFFIXES: .c .o + +# Installation. +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +# This is used to recursively copy a fonts/ or tex/ directory to +# $(fontdir) or $(texinputdir). +# The first arg is `.', and the second is the target directory. +CP_R = cp -r + +# This is so kpathsea will get remade automatically if you change +# something in it and recompile from the package directory. +kpathsea_parent = .. +kpathsea_dir = $(kpathsea_parent)/kpathsea +kpathsea_srcdir_parent = $(top_srcdir)/.. +kpathsea_srcdir = $(kpathsea_srcdir_parent)/kpathsea +kpathsea = $(kpathsea_dir)/kpathsea.a + +##ifeq ($(CC), gcc) +##XDEFS = -Wall -Wpointer-arith $(warn_more) +##CFLAGS = -g $(XCFLAGS) +##endif +# End of common.make. diff --git a/kdvi/make/config.make b/kdvi/make/config.make new file mode 100644 index 00000000..c35cbebe --- /dev/null +++ b/kdvi/make/config.make @@ -0,0 +1,39 @@ +# config.make -- autoconf rules to remake the Makefile, c-auto.h, etc. + +##ifdef HOSTNAME +##ac_dir = $(gnu)/share/autoconf +##autoconf = $(ac_dir)/acspecific.m4 $(ac_dir)/acgeneral.m4 $(ac_dir)/acsite.m4 +##autoheader = $(ac_dir)/acconfig.h +## +### I define $(autoconf) to acgeneral.m4 and the other Autoconf files, so +### configure automatically gets remade in the sources with a new Autoconf +### release. But it would be bad for installers with Autoconf to remake +### configure (not to mention require Autoconf), so I take out the variable +### $(autoconf) definition before release. +##configure_in = $(srcdir)/configure.in $(kpathsea_srcdir)/common.ac +##$(srcdir)/configure: $(configure_in) $(autoconf) +## cd $(srcdir) && autoconf +##endif + +config.status: $(srcdir)/configure + $(SHELL) $(srcdir)/configure --no-create --verbose + +Makefile: $(srcdir)/Makefile.in config.status + $(SHELL) config.status + +# This rule isn't used for web2c or the top-level Makefile, but it +# doesn't hurt. We don't depend on config.status because configure +# always rewrites config.status, even when it doesn't change. Thus it +# might be newer than c-auto.h when we don't need to remake the latter. +c-auto.h: $(srcdir)/stamp-auto +$(srcdir)/stamp-auto: $(srcdir)/c-auto.h.in + $(SHELL) config.status + touch $(srcdir)/stamp-auto + +##ifdef HOSTNAME +### autoheader reads acconfig.h (and c-auto.h.top) automatically. +##$(srcdir)/c-auto.h.in: $(srcdir)/stamp-auto.in +##$(srcdir)/stamp-auto.in: $(configure_in) $(autoheader) $(srcdir)/acconfig.h +## cd $(srcdir) && autoheader +## touch $(srcdir)/stamp-auto.in +##endif diff --git a/kdvi/make/dist.make b/kdvi/make/dist.make new file mode 100644 index 00000000..39c619c7 --- /dev/null +++ b/kdvi/make/dist.make @@ -0,0 +1,33 @@ +# dist.make -- how to make the distribution tar file. + +top_distdir = $(distname)-$(version) +top_files = ChangeLog FTP Makefile.in configure configure.in README \ + $(HOME)/gnu/gnuorg/COPYING* $(HOME)/gnu/gnuorg/install-sh \ + $(HOME)/bin/mkdirchain \ + $(plain)/texinfo.tex +distdir = $(top_distdir)/$(distname) +kpathsea_distdir = ../$(distname)/$(top_distdir)/kpathsea +ln_files = AUTHORS ChangeLog INSTALL NEWS README TAGS *.in *.h *.c \ + configure *.make .gdbinit stamp-auto + +dist: depend.make TAGS pre-dist-$(distname) + rm -rf $(top_distdir)* + mkdir -p $(distdir) + cd ..; make Makefile ./configure + cd ..; cp -p $(top_files) $(distname)/$(top_distdir) + ln -s $(gnu)/share/autoconf/acsite.m4 $(top_distdir)/aclocal.m4 + -ln $(ln_files) $(distdir) + ln $(program_files) $(distdir) + cd $(kpathsea_dir); $(MAKE) distdir=$(kpathsea_distdir) \ + ln_files='$(ln_files)' distdir + cp -rp ../make $(top_distdir) + ungnumake $(distdir)/Makefile.in $(kpathsea_distdir)/Makefile.in \ + $(top_distdir)/Makefile.in $(top_distdir)/make/*.make +# Remove the extra files our patterns got us. + cd $(top_distdir); rm -f */depend.make */c-auto.h */Makefile + $(MAKE) post-dist-$(distname) + cd $(distdir); add-version $(version) $(version_files) + cd $(distdir); test ! -r *.info || touch *.info* + chmod -R a+rwX $(top_distdir) + GZIP=-9 tar chzf $(top_distdir).tar.gz $(top_distdir) + rm -rf $(top_distdir) diff --git a/kdvi/make/library.make b/kdvi/make/library.make new file mode 100644 index 00000000..c343bfd7 --- /dev/null +++ b/kdvi/make/library.make @@ -0,0 +1,5 @@ +# library.make -- stuff only useful for libraries. +AR = ar +ARFLAGS = cq +RANLIB = @RANLIB@ +# End of library.make. diff --git a/kdvi/make/makevars.make b/kdvi/make/makevars.make new file mode 100644 index 00000000..4b14f1be --- /dev/null +++ b/kdvi/make/makevars.make @@ -0,0 +1,20 @@ +# makevars.make -- the directory names we pass. +# It's important that none of these values contain [ @%], for the sake +# of kpathsea/texmf.sed. +makevars = prefix=$(prefix) exec_prefix=$(exec_prefix) \ + platform=$(platform) \ + bindir=$(bindir) scriptdir=$(scriptdir) libdir=$(libdir) \ + datadir=$(datadir) infodir=$(infodir) includedir=$(includedir) \ + manext=$(manext) mandir=$(mandir) \ + texmf=$(texmf) web2cdir=$(web2cdir) \ + texinputdir=$(texinputdir) mfinputdir=$(mfinputdir) \ + mpinputdir=$(mpinputdir) \ + fontdir=$(fontdir) \ + fmtdir=$(fmtdir) basedir=$(basedir) \ + memdir=$(memdir) \ + texpooldir=$(texpooldir) mfpooldir=$(mfpooldir) \ + mppooldir=$(mppooldir) \ + install_fonts=$(install_fonts) \ + dvipsdir=$(dvipsdir) psheaderdir=$(psheaderdir) \ + default_texsizes='$(default_texsizes)' +# End of makevars.make. diff --git a/kdvi/make/misc.make b/kdvi/make/misc.make new file mode 100644 index 00000000..7ee38cca --- /dev/null +++ b/kdvi/make/misc.make @@ -0,0 +1,31 @@ +# misc.make -- cleaning, etc. +TAGS: *.c *.h + if pwd | grep kpathsea >/dev/null; then \ + etags *.c *.h; else etags -i $(kpathsea_dir)/TAGS *.c *.h; fi + +mostlyclean:: + rm -f *.o $(program) $(programs) squeeze $(library).a + +clean:: mostlyclean + rm -f *.dvi *.lj + +distclean:: clean + rm -f Makefile MakeTeXPK *.pool + rm -f config.status config.log config.cache c-auto.h + +# Although we can remake configure and c-auto.h.in, we don't remove +# them, since many people may lack Autoconf. Use configclean for that. +realclean:: distclean + rm -f TAGS *.info* + +extraclean:: + rm -f *.aux *.bak *.bbl *.blg *.dvi *.log *.orig *.pl *.rej + rm -f *.i *.s *.tfm *.vf *.vpl *\#* *gf *pk *~ + rm -f CONTENTS.tex a.out core mfput.* texput.* + +configclean: + rm -f configure c-auto.h.in c-auto.h + +# Prevent GNU make 3.[59,63) from overflowing arg limit on system V. +.NOEXPORT: +# End of misc.make. diff --git a/kdvi/make/paths.make b/kdvi/make/paths.make new file mode 100644 index 00000000..529612cc --- /dev/null +++ b/kdvi/make/paths.make @@ -0,0 +1,99 @@ +# paths.make -- installation directories. +# +# The compile-time paths are defined in kpathsea/paths.h, which is built +# from kpathsea/paths.h.in and these definitions. See kpathsea/INSTALL +# for a description of how the various path-related files are used and +# created. + +# Do not change prefix and exec_prefix in Makefile.in! +# configure doesn't propagate the change to the other Makefiles. +# Instead, give the -prefix/-exec-prefix options to configure. +# (See kpathsea/INSTALL for more details.) This is arguably +# a bug, but it's not likely to change soon. +prefix = @prefix@ +exec_prefix = @exec_prefix@ +platform = $(shell $(srcdir)/config.guess | sed 's/-.*-/-/') + +# Architecture-dependent executables. +bindir = $(exec_prefix)/bin/$(platform) + +# Architecture-independent executables. +scriptdir = $(bindir) + +# Architecture-dependent files, such as lib*.a files. +libdir = $(exec_prefix)/lib + +# Architecture-independent files. +datadir = $(prefix)/lib + +# Header files. +includedir = $(prefix)/include + +# GNU .info* files. +infodir = $(prefix)/info + +# Unix man pages. +manext = 1 +mandir = $(prefix)/man/man$(manext) + +# TeX & MF-specific directories. Not all of the following are relevant +# for all programs, but it seems cleaner to collect everything in one place. + +# The default paths are now in kpathsea/paths.h.in. Passing all the +# paths to sub-makes can make the arg list too long on system V. + +# The root of the tree. +texmf = $(prefix)/texmf + +# TeX, MF, and MP source files. +texinputdir = $(texmf)/tex +mfinputdir = $(texmf)/mf +mpinputdir = $(texmf)/mp + +# MakeTeXPK.site, texmf.cnf, etc. +web2cdir = $(texmf)/web2c + +# The top-level font directory. +fontdir = $(texmf)/fonts + +# Memory dumps (.fmt, .base, and .mem). +fmtdir = $(texmf)/web2c +basedir = $(texmf)/web2c +memdir = $(texmf)/web2c + +# Pool files. +texpooldir = $(texmf)/web2c +mfpooldir = $(texmf)/web2c +mppooldir = $(texmf)/web2c + +# If install_fonts=true, the PostScript/LaserJet TFM and VF files for +# the builtin fonts get installed in subdirectories of this directory, +# named for the typeface families of these directories. If you don't +# have the default directory setup, you will want to set +# install_fonts=false. Ditto for install_macros. +install_fonts = false +install_macros = false + +# Where the .map files from fontname are installed. +fontnamedir = $(texmf)/fontname + +# Where the dvips configuration files get installed, and where +# psfonts.map is. +dvipsdir = $(texmf)/dvips +psheaderdir = $(dvipsdir) + +# MakeTeXPK will go here to create dc*. +dcfontdir = $(fontdir)/public/dc + +# MakeTeXPK will go here if it exists to create nonstandard CM fonts, +# e.g., cmr11. See ftp.cs.umb.edu:pub/tex/sauter.tar.gz. The Sauter +# files must be in your regular MFINPUTS. +sauterdir = $(fontdir)/public/sauter + +# If a font can't be found close enough to its stated size, we look for +# each of these sizes in the order given. This colon-separated list is +# overridden by the envvar TEXSIZES, and by a program-specific variable +# (e.g., XDVISIZES), and perhaps by a config file (e.g., in dvips). +default_texsizes = 300:600 + +# End of paths.make. diff --git a/kdvi/make/programs.make b/kdvi/make/programs.make new file mode 100644 index 00000000..e54b73b5 --- /dev/null +++ b/kdvi/make/programs.make @@ -0,0 +1,13 @@ +# programs.make -- used by Makefiles for executables only. +# Linking. Don't include $(CFLAGS), since ld -g under Linux forces +# static libraries, including libc.a and libX*.a +LDFLAGS = @LDFLAGS@ $(XLDFLAGS) +LIBS = @LIBS@ +# proglib is for web2c; +# XLOADLIBES is for the installer. +LOADLIBES= $(proglib) $(kpathsea) $(LIBS) -lm $(XLOADLIBES) + +# Why separate CCLD from CC? No particular reason. +CCLD = $(CXX) +link_command = $(CCLD) -o $@ $(LDFLAGS) +# End of programs.make. diff --git a/kdvi/make/rdepend.make b/kdvi/make/rdepend.make new file mode 100644 index 00000000..26ddeeb8 --- /dev/null +++ b/kdvi/make/rdepend.make @@ -0,0 +1,15 @@ +# rdepend.make -- rules for remaking the dependencies. +# Have to use -M, not -MM, since we use <kpathsea/...> instead of +# "kpathsea/..." in the sources. But that means we have to remove the +# directory prefixes and all the system include files. +# And <kpathsea/paths.h> is generated, not part of the distribution. +depend depend.make:: c-auto.h $(top_srcdir)/../make/rdepend.make + $(CC) -M $(CPPFLAGS) *.c \ + | sed -e 's,\.\./kpathsea/,$$(kpathsea_srcdir)/,g' \ + -e 's,$$(kpathsea_srcdir)/paths.h,paths.h,g' \ + -e 's,/usr[^ ]* ,,g' \ + -e 's,/usr[^ ]*$$,,g' \ + -e 's,dvi2xx.o,dvilj.o dvilj2p.o dvilj4.o dvilj4l.o,' \ + | grep -v '^ *\\$$' \ + >depend.make +# End of rdepend.make. diff --git a/kdvi/make/texi.make b/kdvi/make/texi.make new file mode 100644 index 00000000..ff953cc7 --- /dev/null +++ b/kdvi/make/texi.make @@ -0,0 +1,13 @@ +# texi.make -- making .dvi and .info from .texi. + +MAKEINFO = makeinfo +MAKEINFO_FLAGS = --paragraph-indent=2 -I$(HOME)/gnu/gnuorg +# That -I is purely for my own benefit in doing `make dist'. It won't +# hurt anything for you (I hope). +TEXI2DVI = texi2dvi + +.SUFFIXES: .info .dvi .texi +.texi.info: + -$(MAKEINFO) $(MAKEINFO_FLAGS) $< -o $@ +.texi.dvi: + -$(TEXI2DVI) $(TEXI2DVI_FLAGS) $< diff --git a/kdvi/make/tkpathsea.make b/kdvi/make/tkpathsea.make new file mode 100644 index 00000000..dd1b6d17 --- /dev/null +++ b/kdvi/make/tkpathsea.make @@ -0,0 +1,9 @@ +# tkpathsea.make -- remaking kpathsea. + +makeargs = $(MFLAGS) CC='$(CC)' CFLAGS='$(CFLAGS)' $(XMAKEARGS) + +$(kpathsea): $(kpathsea_srcdir)/*.c $(kpathsea_srcdir)/*.h \ + $(kpathsea_srcdir)/texmf.cnf.in $(top_srcdir)/../make/paths.make + cd $(kpathsea_dir); $(MAKE) $(makeargs) + +# End of tkpathsea.make. diff --git a/kdvi/optionDialogFontsWidget.cpp b/kdvi/optionDialogFontsWidget.cpp new file mode 100644 index 00000000..a3a76425 --- /dev/null +++ b/kdvi/optionDialogFontsWidget.cpp @@ -0,0 +1,47 @@ +// optionDiologWidget.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +// Add header files alphabetically + +#include <config.h> + +#include <kcombobox.h> +#include <kdebug.h> +#include <klocale.h> +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +#include "fontpool.h" +#include "optionDialogFontsWidget.h" + + +// Constructs a optionDialogWidget_base which is a child of 'parent', with +// the name 'name' and widget flags set to 'f'. +optionDialogFontsWidget::optionDialogFontsWidget( QWidget* parent, const char* name, WFlags fl ) + : optionDialogFontsWidget_base( parent, name, fl ) +{ +#ifndef HAVE_FREETYPE + kcfg_UseType1Fonts->setChecked(false); + kcfg_UseType1Fonts->setEnabled(false); + kcfg_UseFontHints->setEnabled(false); + kcfg_UseFontHints->setChecked(false); + QToolTip::add(PFB_ButtonGroup, i18n("This version of KDVI does not support type 1 fonts.")); + QWhatsThis::add(PFB_ButtonGroup, i18n("KDVI needs the FreeType library to access type 1 fonts. This library " + "was not present when KDVI was compiled. If you want to use type 1 " + "fonts, you must either install the FreeType library and recompile KDVI " + "yourself, or find a precompiled software package for your operating " + "system.")); +#endif +} + +optionDialogFontsWidget::~optionDialogFontsWidget() +{ +} + +#include "optionDialogFontsWidget.moc" diff --git a/kdvi/optionDialogFontsWidget.h b/kdvi/optionDialogFontsWidget.h new file mode 100644 index 00000000..63ce98cc --- /dev/null +++ b/kdvi/optionDialogFontsWidget.h @@ -0,0 +1,24 @@ +// -*- C++ -*- +// optionDialogFontsWidget.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#ifndef OPTIONDIALOGFONTSWIDGET_H +#define OPTIONDIALOGFONTSWIDGET_H + +#include "optionDialogFontsWidget_base.h" + + +class optionDialogFontsWidget : public optionDialogFontsWidget_base +{ + Q_OBJECT + + public: + optionDialogFontsWidget( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); + ~optionDialogFontsWidget(); +}; + +#endif // OPTIONDIALOGFONTSWIDGET_H diff --git a/kdvi/optionDialogFontsWidget_base.ui b/kdvi/optionDialogFontsWidget_base.ui new file mode 100644 index 00000000..da4ca601 --- /dev/null +++ b/kdvi/optionDialogFontsWidget_base.ui @@ -0,0 +1,64 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>optionDialogFontsWidget_base</class> +<author>Stefan Kebekus</author> +<widget class="QWidget"> + <property name="name"> + <cstring>optionDialogFontsWidget_base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>325</width> + <height>54</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_UseFontHints</cstring> + </property> + <property name="text"> + <string>Use font hinting for Type 1 fonts, if available</string> + </property> + <property name="toolTip" stdset="0"> + <string>You should enable this, if the use of font hinting improves readability on your machine.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Many modern fonts contain "font hinting" information which can be used to improve the appearance of a font on low-resolution displays, such as a computer monitor, or a notebook screen. However, many people find the "improved" fonts quite ugly and prefer to have this option disabled.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>121</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<includes> + <include location="global" impldecl="in implementation">kdialog.h</include> +</includes> +<slots> + <slot>buttonGroup1_clicked(int)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kdvi/optionDialogSpecialWidget.cpp b/kdvi/optionDialogSpecialWidget.cpp new file mode 100644 index 00000000..1e24f6cc --- /dev/null +++ b/kdvi/optionDialogSpecialWidget.cpp @@ -0,0 +1,135 @@ +// optionDialogSpecialWidget.cpp +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +// Add header files alphabetically + +#include <config.h> + +#include <kdebug.h> + +#include <kapplication.h> +#include <kcombobox.h> +#include <klineedit.h> +#include <klocale.h> +#include <kurllabel.h> +#include <qcheckbox.h> +#include <qlabel.h> + +#include "optionDialogSpecialWidget.h" +#include "prefs.h" + + +// Constructs a optionDialogWidget_base which is a child of 'parent', with +// the name 'name' and widget flags set to 'f'. +optionDialogSpecialWidget::optionDialogSpecialWidget( QWidget* parent, const char* name, WFlags fl ) + : optionDialogSpecialWidget_base( parent, name, fl ) +{ + // Set up the list of known and supported editors + editorNameString += i18n("User-Defined Editor"); + editorCommandString += ""; + editorDescriptionString += i18n("Enter the command line below."); + + editorNameString += "Emacs / emacsclient"; + editorCommandString += "emacsclient --no-wait +%l %f || emacs +%l %f"; + editorDescriptionString += i18n("Click 'Help' to learn how to set up Emacs."); + + editorNameString += "Kate"; + editorCommandString += "kate --use --line %l %f"; + editorDescriptionString += i18n("Kate perfectly supports inverse search."); + + editorNameString += "Kile"; + editorCommandString += "kile %f --line %l"; + editorDescriptionString += i18n("Kile works very well"); + + editorNameString += "NEdit"; + editorCommandString += "ncl -noask -line %l %f || nc -noask -line %l %f"; + editorDescriptionString += i18n("NEdit perfectly supports inverse search."); + + editorNameString += "VIM - Vi IMproved / GUI"; + editorCommandString += "gvim --servername KDVI --remote-silent +%l %f"; + editorDescriptionString += i18n("VIM version 6.0 or greater works just fine."); + + editorNameString += "XEmacs / gnuclient"; + editorCommandString += "gnuclient -q +%l %f || xemacs +%l %f"; + editorDescriptionString += i18n("Click 'Help' to learn how to set up XEmacs."); + + for(unsigned int i=0; i<editorNameString.count(); i++) + editorChoice->insertItem(editorNameString[i]); + // Set the proper editor on the "Rendering-Page", try to recognize + // the editor command from the config-file. If the editor command is + // not recognized, switch to "User defined editor". That way, kdvi + // stays compatible even if the EditorCommands[] change between + // different versions of kdvi. + QString currentEditorCommand = Prefs::editorCommand(); + int i; + for(i = editorCommandString.count()-1; i>0; i--) + if (editorCommandString[i] == currentEditorCommand) + break; + if (i == 0) + usersEditorCommand = currentEditorCommand; + slotComboBox(i); + + connect(urll, SIGNAL(leftClickedURL(const QString&)), this, SLOT(slotExtraHelpButton(const QString&))); + connect(editorChoice, SIGNAL( activated( int ) ), this, SLOT( slotComboBox( int ) ) ); + + // Editor description strings (and their translations) vary in + // size. Find the longest description string available to make sure + // that the page is always large enough. + int maximumWidth = 0; + for ( QStringList::Iterator it = editorDescriptionString.begin(); it != editorDescriptionString.end(); ++it ) { + int width = editorDescription->fontMetrics().width(*it); + if (width > maximumWidth) + maximumWidth = width; + } + editorDescription->setMinimumWidth(maximumWidth+10); + + connect(kcfg_EditorCommand, SIGNAL( textChanged (const QString &) ), this, SLOT( slotUserDefdEditorCommand( const QString & ) ) ); +} + +optionDialogSpecialWidget::~optionDialogSpecialWidget() +{ +} + +void optionDialogSpecialWidget::slotUserDefdEditorCommand( const QString &text ) +{ + if (isUserDefdEditor == true) + EditorCommand = usersEditorCommand = text; +} + + +void optionDialogSpecialWidget::slotComboBox(int item) +{ + if (item != editorChoice->currentItem()) + editorChoice->setCurrentItem(item); + + editorDescription->setText(editorDescriptionString[item]); + + if (item != 0) { + isUserDefdEditor = false; + kcfg_EditorCommand->setText(editorCommandString[item]); + kcfg_EditorCommand->setReadOnly(true); + EditorCommand = editorCommandString[item]; + } else { + kcfg_EditorCommand->setText(usersEditorCommand); + kcfg_EditorCommand->setReadOnly(false); + EditorCommand = usersEditorCommand; + isUserDefdEditor = true; + } +} + +void optionDialogSpecialWidget::slotExtraHelpButton( const QString & ) +{ + kapp->invokeHelp( "inv-search", "kdvi" ); +} + +void optionDialogSpecialWidget::apply() +{ + Prefs::setEditorCommand(EditorCommand); +} + + +#include "optionDialogSpecialWidget.moc" diff --git a/kdvi/optionDialogSpecialWidget.h b/kdvi/optionDialogSpecialWidget.h new file mode 100644 index 00000000..cdd81100 --- /dev/null +++ b/kdvi/optionDialogSpecialWidget.h @@ -0,0 +1,36 @@ +// -*- C++ -*- +// optionDialogSpecialWidget.h +// +// Part of KDVI - A DVI previewer for the KDE desktop environemt +// +// (C) 2003 Stefan Kebekus +// Distributed under the GPL + +#ifndef OPTIONDIALOGSPECIALWIDGET_H +#define OPTIONDIALOGSPECIALWIDGET_H + +#include "optionDialogSpecialWidget_base.h" + + +class optionDialogSpecialWidget : public optionDialogSpecialWidget_base +{ + Q_OBJECT + + public: + optionDialogSpecialWidget( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); + ~optionDialogSpecialWidget(); + + public slots: + void apply(); + void slotComboBox(int item); + void slotUserDefdEditorCommand( const QString &text ); + void slotExtraHelpButton( const QString &anchor); + + private: + QStringList editorNameString, editorCommandString, editorDescriptionString; + QString EditorCommand; + bool isUserDefdEditor; + QString usersEditorCommand; +}; + +#endif // OPTIONDIALOGSPECIALWIDGET_H diff --git a/kdvi/optionDialogSpecialWidget_base.ui b/kdvi/optionDialogSpecialWidget_base.ui new file mode 100644 index 00000000..7aa36a26 --- /dev/null +++ b/kdvi/optionDialogSpecialWidget_base.ui @@ -0,0 +1,208 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>optionDialogSpecialWidget_base</class> +<widget class="QWidget"> + <property name="name"> + <cstring>optionDialogSpecialWidget_base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>519</width> + <height>201</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_ShowPS</cstring> + </property> + <property name="text"> + <string>Show PostScript specials</string> + </property> + <property name="toolTip" stdset="0"> + <string>If in doubt, enable this option.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Some DVI files contain PostScript graphics. If enabled, KDVI will use the Ghostview PostScript interpreter to display these. You probably want to enable this option, unless you have a DVI-file whose PostScript part is broken, or too large for your machine.</string> + </property> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup3</cstring> + </property> + <property name="title"> + <string>Editor for Inverse Search</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KComboBox" row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>editorChoice</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>Choose an editor which is used in inverse search.</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Some DVI files contain 'inverse search' information. If such a DVI file is loaded, you can right-click into KDVI and an editor will open, load the TeX file and jump to the correct position. You can select your favorite editor here. If in doubt, 'nedit' is usually a good choice.</p> +<p>Check the KDVI manual to see how to prepare DVI files which support the inverse search.</p></string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Description:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Shell command:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>editorDescription</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Explains about the editor's capabilities in conjunction with inverse search.</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Not all editors are well suited for inverse search. For instance, many editors have no command like 'If the file is not yet loaded, load it. Otherwise, bring the window with the file to the front'. If you are using an editor like this, clicking into the DVI file will always open a new editor, even if the TeX file is already open. Likewise, many editors have no command line argument that would allow KDVI to specify the exact line which you wish to edit.</p> +<p>If you feel that KDVI's support for a certain editor is inadequate, please write to kebekus@kde.org.</p></string> + </property> + </widget> + <widget class="KLineEdit" row="3" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_EditorCommand</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>Shell-command line used to start the editor.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If you are using inverse search, KDVI uses this command line to start the editor. The field '%f' is replaced with the filename, and '%l' is replaced with the line number.</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>Editor:</string> + </property> + </widget> + <widget class="KURLLabel" row="0" column="2"> + <property name="name"> + <cstring>urll</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>What is 'inverse search'? </string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="url" stdset="0"> + <string>inv-search</string> + </property> + </widget> + <spacer row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>390</width> + <height>21</height> + </size> + </property> + </spacer> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kurllabel.h</includehint> +</includehints> +</UI> diff --git a/kdvi/performanceMeasurement.h b/kdvi/performanceMeasurement.h new file mode 100644 index 00000000..b6cb46c7 --- /dev/null +++ b/kdvi/performanceMeasurement.h @@ -0,0 +1,21 @@ +// -*- C++ -*- + +//#define PERFORMANCE_MEASUREMENT + +#ifdef PERFORMANCE_MEASUREMENT +#include <qdatetime.h> + +// This is the central timer used for performance measurement. It is +// set to zero and started when the kdvi_multipage is +// constructed. This object is statically defined in +// kdvi_multipage.cpp. +extern QTime performanceTimer; + +// A flag that is set to true once the first page of the document was +// successfully drawn. This object is statically defined in +// kdvi_multipage.cpp. +// 0 = initial value +// 1 = first page was drawn +// 2 = last page was drawn +extern int performanceFlag; +#endif diff --git a/kdvi/pix/Makefile.am b/kdvi/pix/Makefile.am new file mode 100644 index 00000000..c43e4d48 --- /dev/null +++ b/kdvi/pix/Makefile.am @@ -0,0 +1,2 @@ +KDE_ICON = kdvi + diff --git a/kdvi/pix/hi16-app-kdvi.png b/kdvi/pix/hi16-app-kdvi.png Binary files differnew file mode 100644 index 00000000..dc4c71c6 --- /dev/null +++ b/kdvi/pix/hi16-app-kdvi.png diff --git a/kdvi/pix/hi22-app-kdvi.png b/kdvi/pix/hi22-app-kdvi.png Binary files differnew file mode 100644 index 00000000..0462047e --- /dev/null +++ b/kdvi/pix/hi22-app-kdvi.png diff --git a/kdvi/pix/hi32-app-kdvi.png b/kdvi/pix/hi32-app-kdvi.png Binary files differnew file mode 100644 index 00000000..848eb1c3 --- /dev/null +++ b/kdvi/pix/hi32-app-kdvi.png diff --git a/kdvi/pix/hi48-app-kdvi.png b/kdvi/pix/hi48-app-kdvi.png Binary files differnew file mode 100644 index 00000000..404462dc --- /dev/null +++ b/kdvi/pix/hi48-app-kdvi.png diff --git a/kdvi/pix/hisc-app-kdvi.svgz b/kdvi/pix/hisc-app-kdvi.svgz Binary files differnew file mode 100644 index 00000000..9e45007d --- /dev/null +++ b/kdvi/pix/hisc-app-kdvi.svgz diff --git a/kdvi/prebookmark.h b/kdvi/prebookmark.h new file mode 100644 index 00000000..141c8d6c --- /dev/null +++ b/kdvi/prebookmark.h @@ -0,0 +1,50 @@ +// -*- C++ -*- +/*************************************************************************** + * 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 _PREBOOKMARK_H_ +#define _PREBOOKMARK_H_ + +#include <qstring.h> + +/*! \brief Bookmark representation + +This class presents a bookmark in a format that is used internally by +the DVI prescan routines. +*/ + +class PreBookmark +{ + public: + PreBookmark(const QString& t, const QString& a, Q_UINT16 n) {title=t; anchorName=a; noOfChildren=n;} + PreBookmark() {title=QString::null; anchorName=QString::null; noOfChildren=0;} + + // Title of the bookmark + QString title; + + // Name of the anchor + QString anchorName; + + // Number of subordinate bookmarks + Q_UINT16 noOfChildren; +}; + +#endif diff --git a/kdvi/prefs.kcfgc b/kdvi/prefs.kcfgc new file mode 100644 index 00000000..7b77dce6 --- /dev/null +++ b/kdvi/prefs.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=kdvi.kcfg +ClassName=Prefs +Singleton=true +Mutators=true
\ No newline at end of file diff --git a/kdvi/psgs.cpp b/kdvi/psgs.cpp new file mode 100644 index 00000000..9e23fcad --- /dev/null +++ b/kdvi/psgs.cpp @@ -0,0 +1,340 @@ +// +// ghostscript_interface +// +// Part of KDVI - A framework for multipage text/gfx viewers +// +// (C) 2004 Stefan Kebekus +// Distributed under the GPL + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocio.h> +#include <ktempfile.h> +#include <kurl.h> +#include <qdir.h> +#include <qpainter.h> + +#include "psgs.h" +#include "dviFile.h" +#include "pageNumber.h" + +extern const char psheader[]; + +//#define DEBUG_PSGS + + +pageInfo::pageInfo(const QString& _PostScriptString) { + PostScriptString = new QString(_PostScriptString); + background = Qt::white; + permanentBackground = Qt::white; +} + + +pageInfo::~pageInfo() { + if (PostScriptString != 0L) + delete PostScriptString; +} + + +// ====================================================== + +ghostscript_interface::ghostscript_interface() { + pageList.setAutoDelete(true); + + PostScriptHeaderString = new QString(); + + knownDevices.append("png256"); + knownDevices.append("jpeg"); + knownDevices.append("pnn"); + knownDevices.append("pnnraw"); + gsDevice = knownDevices.begin(); +} + +ghostscript_interface::~ghostscript_interface() { + if (PostScriptHeaderString != 0L) + delete PostScriptHeaderString; +} + + +void ghostscript_interface::setPostScript(const PageNumber& page, const QString& PostScript) { +#ifdef DEBUG_PSGS + kdDebug(4300) << "ghostscript_interface::setPostScript( " << page << ", ... )" << endl; +#endif + + if (pageList.find(page) == 0) { + pageInfo *info = new pageInfo(PostScript); + // Check if dict is big enough + if (pageList.count() > pageList.size() -2) + pageList.resize(pageList.size()*2); + pageList.insert(page, info); + } else + *(pageList.find(page)->PostScriptString) = PostScript; +} + + +void ghostscript_interface::setIncludePath(const QString &_includePath) { + if (_includePath.isEmpty()) + includePath = "*"; // Allow all files + else + includePath = _includePath+"/*"; +} + + +void ghostscript_interface::setBackgroundColor(const PageNumber& page, const QColor& background_color, bool permanent) { +#ifdef DEBUG_PSGS + kdDebug(4300) << "ghostscript_interface::setBackgroundColor( " << page << ", " << background_color << " )" << endl; +#endif + + if (pageList.find(page) == 0) { + pageInfo *info = new pageInfo(QString::null); + info->background = background_color; + if (permanent) + info->permanentBackground = background_color; + // Check if dict is big enough + if (pageList.count() > pageList.size() -2) + pageList.resize(pageList.size()*2); + pageList.insert(page, info); + } else { + pageList.find(page)->background = background_color; + if (permanent) + pageList.find(page)->permanentBackground = background_color; + } +} + +void ghostscript_interface::restoreBackgroundColor(const PageNumber& page) +{ +#ifdef DEBUG_PSGS + kdDebug(4300) << "ghostscript_interface::restoreBackgroundColor( " << page << " )" << endl; +#endif + if (pageList.find(page) == 0) + return; + + pageInfo *info = pageList.find(page); + info->background = info->permanentBackground; +} + +// Returns the background color for a certain page. This color is +// always guaranteed to be valid + +QColor ghostscript_interface::getBackgroundColor(const PageNumber& page) const { +#ifdef DEBUG_PSGS + kdDebug(4300) << "ghostscript_interface::getBackgroundColor( " << page << " )" << endl; +#endif + + if (pageList.find(page) == 0) + return Qt::white; + else + return pageList.find(page)->background; +} + + +void ghostscript_interface::clear() { + PostScriptHeaderString->truncate(0); + + // Deletes all items, removes temporary files, etc. + pageList.clear(); +} + + +void ghostscript_interface::gs_generate_graphics_file(const PageNumber& page, const QString& filename, long magnification) { +#ifdef DEBUG_PSGS + kdDebug(4300) << "ghostscript_interface::gs_generate_graphics_file( " << page << ", " << filename << " )" << endl; +#endif + + if (knownDevices.isEmpty()) { + kdError(4300) << "No known devices found" << endl; + return; + } + + emit(setStatusBarText(i18n("Generating PostScript graphics..."))); + + pageInfo *info = pageList.find(page); + + // Generate a PNG-file + // Step 1: Write the PostScriptString to a File + KTempFile PSfile(QString::null,".ps"); + + QTextStream& os = *PSfile.textStream(); + os << "%!PS-Adobe-2.0\n" + << "%%Creator: kdvi\n" + << "%%Title: KDVI temporary PostScript\n" + << "%%Pages: 1\n" + << "%%PageOrder: Ascend\n" + // HSize and VSize in 1/72 inch + << "%%BoundingBox: 0 0 " + << (Q_INT32)(72*(pixel_page_w/resolution)) << ' ' + << (Q_INT32)(72*(pixel_page_h/resolution)) << '\n' + << "%%EndComments\n" + << "%!\n" + << psheader + << "TeXDict begin " + // HSize in (1/(65781.76*72))inch + << (Q_INT32)(72*65781*(pixel_page_w/resolution)) << ' ' + // VSize in (1/(65781.76*72))inch + << (Q_INT32)(72*65781*(pixel_page_h/resolution)) << ' ' + // Magnification + << (Q_INT32)(magnification) + // dpi and vdpi + << " 300 300" + // Name + << " (test.dvi)" + << " @start end\n" + << "TeXDict begin\n" + // Start page + << "1 0 bop 0 0 a \n"; + + if (PostScriptHeaderString->latin1() != NULL) + os << PostScriptHeaderString->latin1(); + + if (info->background != Qt::white) { + QString colorCommand = QString("gsave %1 %2 %3 setrgbcolor clippath fill grestore\n"). + arg(info->background.red()/255.0). + arg(info->background.green()/255.0). + arg(info->background.blue()/255.0); + os << colorCommand.latin1(); + } + + if (info->PostScriptString->latin1() != NULL) + os << info->PostScriptString->latin1(); + + os << "end\n" + << "showpage \n"; + + PSfile.close(); + + // Step 2: Call GS with the File + QFile::remove(filename.ascii()); + KProcIO proc; + QStringList argus; + argus << "gs"; + argus << "-dSAFER" << "-dPARANOIDSAFER" << "-dDELAYSAFER" << "-dNOPAUSE" << "-dBATCH"; + argus << QString("-sDEVICE=%1").arg(*gsDevice); + argus << QString("-sOutputFile=%1").arg(filename); + argus << QString("-sExtraIncludePath=%1").arg(includePath); + argus << QString("-g%1x%2").arg(pixel_page_w).arg(pixel_page_h); // page size in pixels + argus << QString("-r%1").arg(resolution); // resolution in dpi + argus << "-c" << "<< /PermitFileReading [ ExtraIncludePath ] /PermitFileWriting [] /PermitFileControl [] >> setuserparams .locksafe"; + argus << "-f" << PSfile.name(); + +#ifdef DEBUG_PSGS + kdDebug(4300) << argus.join(" ") << endl; +#endif + + proc << argus; + if (proc.start(KProcess::Block) == false) { + // Starting ghostscript did not work. + // TODO: Issue error message, switch PS support off. + kdError(4300) << "ghostview could not be started" << endl; + } + PSfile.unlink(); + + // Check if gs has indeed produced a file. + if (QFile::exists(filename) == false) { + kdError(4300) << "GS did not produce output." << endl; + + // No. Check is the reason is that the device is not compiled into + // ghostscript. If so, try again with another device. + QString GSoutput; + while(proc.readln(GSoutput) != -1) { + if (GSoutput.contains("Unknown device")) { + kdDebug(4300) << QString("The version of ghostview installed on this computer does not support " + "the '%1' ghostview device driver.").arg(*gsDevice) << endl; + knownDevices.remove(gsDevice); + gsDevice = knownDevices.begin(); + if (knownDevices.isEmpty()) + // TODO: show a requestor of some sort. + KMessageBox::detailedError(0, + i18n("<qt>The version of Ghostview that is installed on this computer does not contain " + "any of the Ghostview device drivers that are known to KDVI. PostScript " + "support has therefore been turned off in KDVI.</qt>"), + i18n("<qt><p>The Ghostview program, which KDVI uses internally to display the " + "PostScript graphics that is included in this DVI file, is generally able to " + "write its output in a variety of formats. The sub-programs that Ghostview uses " + "for these tasks are called 'device drivers'; there is one device driver for " + "each format that Ghostview is able to write. Different versions of Ghostview " + "often have different sets of device drivers available. It seems that the " + "version of Ghostview that is installed on this computer does not contain " + "<strong>any</strong> of the device drivers that are known to KDVI.</p>" + "<p>It seems unlikely that a regular installation of Ghostview would not contain " + "these drivers. This error may therefore point to a serious misconfiguration of " + "the Ghostview installation on your computer.</p>" + "<p>If you want to fix the problems with Ghostview, you can use the command " + "<strong>gs --help</strong> to display the list of device drivers contained in " + "Ghostview. Among others, KDVI can use the 'png256', 'jpeg' and 'pnm' " + "drivers. Note that KDVI needs to be restarted to re-enable PostScript support." + "</p></qt>")); + else { + kdDebug(4300) << QString("KDVI will now try to use the '%1' device driver.").arg(*gsDevice) << endl; + gs_generate_graphics_file(page, filename, magnification); + } + return; + } + } + } + emit(setStatusBarText(QString::null)); +} + + +void ghostscript_interface::graphics(const PageNumber& page, double dpi, long magnification, QPainter* paint) { +#ifdef DEBUG_PSGS + kdDebug(4300) << "ghostscript_interface::graphics( " << page << ", " << dpi << ", ... ) called." << endl; +#endif + + if (paint == 0) { + kdError(4300) << "ghostscript_interface::graphics(PageNumber page, double dpi, long magnification, QPainter *paint) called with paint == 0" << endl; + return; + } + + resolution = dpi; + + pixel_page_w = paint->viewport().width(); + pixel_page_h = paint->viewport().height(); + + pageInfo *info = pageList.find(page); + + // No PostScript? Then return immediately. + if ((info == 0) || (info->PostScriptString->isEmpty())) { +#ifdef DEBUG_PSGS + kdDebug(4300) << "No PostScript found. Not drawing anything." << endl; +#endif + return; + } + + KTempFile gfxFile(QString::null, ".png"); + gfxFile.setAutoDelete(1); + gfxFile.close(); // we are want the filename, not the file + + gs_generate_graphics_file(page, gfxFile.name(), magnification); + + QPixmap MemoryCopy(gfxFile.name()); + paint->drawPixmap(0, 0, MemoryCopy); + return; +} + + +QString ghostscript_interface::locateEPSfile(const QString &filename, const KURL &base) +{ + // If the base URL indicates that the DVI file is local, try to find + // the graphics file in the directory where the DVI file resides + if (base.isLocalFile()) { + QString path = base.path(); // -> "/bar/foo.dvi" + QFileInfo fi1(path); + QFileInfo fi2(fi1.dir(),filename); + if (fi2.exists()) + return fi2.absFilePath(); + } + + // Otherwise, use kpsewhich to find the eps file. + QString EPSfilename; + KProcIO proc; + proc << "kpsewhich" << filename; + proc.start(KProcess::Block); + proc.readln(EPSfilename); + + return EPSfilename.stripWhiteSpace(); +} + +#include "psgs.moc" diff --git a/kdvi/psgs.h b/kdvi/psgs.h new file mode 100644 index 00000000..0b6e679d --- /dev/null +++ b/kdvi/psgs.h @@ -0,0 +1,107 @@ +// -*- C++ -*- +// +// ghostscript_interface +// +// Part of KDVI - A framework for multipage text/gfx viewers +// +// (C) 2004 Stefan Kebekus +// Distributed under the GPL + +#ifndef _PSGS_H_ +#define _PSGS_H_ + +#include <qcolor.h> +#include <qobject.h> +#include <qstring.h> +#include <qintdict.h> + +class PageNumber; +class QPainter; + + +class pageInfo +{ +public: + pageInfo(const QString& _PostScriptString); + ~pageInfo(); + + QColor background; + QColor permanentBackground; + QString *PostScriptString; +}; + + +class ghostscript_interface : public QObject +{ + Q_OBJECT + +public: + ghostscript_interface(); + ~ghostscript_interface(); + + void clear(); + + // sets the PostScript which is used on a certain page + void setPostScript(const PageNumber& page, const QString& PostScript); + + // sets path from additional postscript files may be read + void setIncludePath(const QString &_includePath); + + // Sets the background color for a certain page. If permanent is false then the original + // background color can be restored by calling restoreBackground(page). + // The Option permanent = false is used when we want to display a different paper + // color as the one specified in the dvi file. + void setBackgroundColor(const PageNumber& page, const QColor& background_color, bool permanent = true); + + // Restore the background to the color which was specified by the last call to setBackgroundColor() + // With option permanent = true. + void restoreBackgroundColor(const PageNumber& page); + + // Draws the graphics of the page into the painter, if possible. If + // the page does not contain any graphics, nothing happens + void graphics(const PageNumber& page, double dpi, long magnification, QPainter* paint); + + // Returns the background color for a certain page. If no color was + // set, Qt::white is returned. + QColor getBackgroundColor(const PageNumber& page) const; + + QString *PostScriptHeaderString; + + /** This method tries to find the PostScript file 'filename' in the + DVI file's directory (if the base-URL indicates that the DVI file + is local), and, if that fails, uses kpsewhich to find the file. If + the file is found, the full path (including file name) is + returned. Otherwise, the method returns the first argument. TODO: + use the DVI file's baseURL, once this is implemented. + */ + static QString locateEPSfile(const QString &filename, const KURL &base); + +private: + void gs_generate_graphics_file(const PageNumber& page, const QString& filename, long magnification); + QIntDict<pageInfo> pageList; + + double resolution; // in dots per inch + int pixel_page_w; // in pixels + int pixel_page_h; // in pixels + + QString includePath; + + // Output device that ghostscript is supposed tp use. Default is + // "png256". If that does not work, gs_generate_graphics_file will + // automatically try other known device drivers. If no known output + // device can be found, something is badly wrong. In that case, + // "gsDevice" is set to an empty string, and + // gs_generate_graphics_file will return immediately. + QValueListIterator<QString> gsDevice; + + // A list of known devices, set by the constructor. This includes + // "png256", "pnm". If a device is found to not work, its name is + // removed from the list, and another device name is tried. + QStringList knownDevices; + +signals: + /** Passed through to the top-level kpart. */ + void setStatusBarText( const QString& ); +}; + +#endif diff --git a/kdvi/psheader.txt b/kdvi/psheader.txt new file mode 100644 index 00000000..f5e543d1 --- /dev/null +++ b/kdvi/psheader.txt @@ -0,0 +1,113 @@ +% texc.pro +%! +/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S +N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72 +mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0 +0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{ +landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize +mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[ +matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round +exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{ +statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0] +N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin +/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array +/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2 +array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N +df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A +definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get +}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub} +B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr +1 add N}if}B/id 0 N/rw 0 N/rc 0 N/gp 0 N/cp 0 N/G 0 N/CharBuilder{save 3 +1 roll S A/base get 2 index get S/BitMaps get S get/Cd X pop/ctr 0 N Cdx +0 Cx Cy Ch sub Cx Cw add Cy setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx +sub Cy .1 sub]/id Ci N/rw Cw 7 add 8 idiv string N/rc 0 N/gp 0 N/cp 0 N{ +rc 0 ne{rc 1 sub/rc X rw}{G}ifelse}imagemask restore}B/G{{id gp get/gp +gp 1 add N A 18 mod S 18 idiv pl S get exec}loop}B/adv{cp add/cp X}B +/chg{rw cp id gp 4 index getinterval putinterval A gp add/gp X adv}B/nd{ +/cp 0 N rw exit}B/lsh{rw cp 2 copy get A 0 eq{pop 1}{A 255 eq{pop 254}{ +A A add 255 and S 1 and or}ifelse}ifelse put 1 adv}B/rsh{rw cp 2 copy +get A 0 eq{pop 128}{A 255 eq{pop 127}{A 2 idiv S 128 and or}ifelse} +ifelse put 1 adv}B/clr{rw cp 2 index string putinterval adv}B/set{rw cp +fillstr 0 4 index getinterval putinterval adv}B/fillstr 18 string 0 1 17 +{2 copy 255 put pop}for N/pl[{adv 1 chg}{adv 1 chg nd}{1 add chg}{1 add +chg nd}{adv lsh}{adv lsh nd}{adv rsh}{adv rsh nd}{1 add adv}{/rc X nd}{ +1 add set}{1 add clr}{adv 2 chg}{adv 2 chg nd}{pop nd}]A{bind pop} +forall N/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn +/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put +}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{ +bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A +mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{ +SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{ +userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X +1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4 +index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N +/p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{ +/Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT) +(LaserWriter 16/600)]{A length product length le{A length product exch 0 +exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse +end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask +grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot} +imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round +exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto +fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p +delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M} +B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{ +p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S +rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end + +% special.pro +%! +TeXDict begin/SDict 200 dict N SDict begin/@SpecialDefaults{/hs 612 N +/vs 792 N/ho 0 N/vo 0 N/hsc 1 N/vsc 1 N/ang 0 N/CLIP 0 N/rwiSeen false N +/rhiSeen false N/letter{}N/note{}N/a4{}N/legal{}N}B/@scaleunit 100 N +/@hscale{@scaleunit div/hsc X}B/@vscale{@scaleunit div/vsc X}B/@hsize{ +/hs X/CLIP 1 N}B/@vsize{/vs X/CLIP 1 N}B/@clip{/CLIP 2 N}B/@hoffset{/ho +X}B/@voffset{/vo X}B/@angle{/ang X}B/@rwi{10 div/rwi X/rwiSeen true N}B +/@rhi{10 div/rhi X/rhiSeen true N}B/@llx{/llx X}B/@lly{/lly X}B/@urx{ +/urx X}B/@ury{/ury X}B/magscale true def end/@MacSetUp{userdict/md known +{userdict/md get type/dicttype eq{userdict begin md length 10 add md +maxlength ge{/md md dup length 20 add dict copy def}if end md begin +/letter{}N/note{}N/legal{}N/od{txpose 1 0 mtx defaultmatrix dtransform S +atan/pa X newpath clippath mark{transform{itransform moveto}}{transform{ +itransform lineto}}{6 -2 roll transform 6 -2 roll transform 6 -2 roll +transform{itransform 6 2 roll itransform 6 2 roll itransform 6 2 roll +curveto}}{{closepath}}pathforall newpath counttomark array astore/gc xdf +pop ct 39 0 put 10 fz 0 fs 2 F/|______Courier fnt invertflag{PaintBlack} +if}N/txpose{pxs pys scale ppr aload pop por{noflips{pop S neg S TR pop 1 +-1 scale}if xflip yflip and{pop S neg S TR 180 rotate 1 -1 scale ppr 3 +get ppr 1 get neg sub neg ppr 2 get ppr 0 get neg sub neg TR}if xflip +yflip not and{pop S neg S TR pop 180 rotate ppr 3 get ppr 1 get neg sub +neg 0 TR}if yflip xflip not and{ppr 1 get neg ppr 0 get neg TR}if}{ +noflips{TR pop pop 270 rotate 1 -1 scale}if xflip yflip and{TR pop pop +90 rotate 1 -1 scale ppr 3 get ppr 1 get neg sub neg ppr 2 get ppr 0 get +neg sub neg TR}if xflip yflip not and{TR pop pop 90 rotate ppr 3 get ppr +1 get neg sub neg 0 TR}if yflip xflip not and{TR pop pop 270 rotate ppr +2 get ppr 0 get neg sub neg 0 S TR}if}ifelse scaleby96{ppr aload pop 4 +-1 roll add 2 div 3 1 roll add 2 div 2 copy TR .96 dup scale neg S neg S +TR}if}N/cp{pop pop showpage pm restore}N end}if}if}N/normalscale{ +Resolution 72 div VResolution 72 div neg scale magscale{DVImag dup scale +}if 0 setgray}N/psfts{S 65781.76 div N}N/startTexFig{/psf$SavedState +save N userdict maxlength dict begin/magscale true def normalscale +currentpoint TR/psf$ury psfts/psf$urx psfts/psf$lly psfts/psf$llx psfts +/psf$y psfts/psf$x psfts currentpoint/psf$cy X/psf$cx X/psf$sx psf$x +psf$urx psf$llx sub div N/psf$sy psf$y psf$ury psf$lly sub div N psf$sx +psf$sy scale psf$cx psf$sx div psf$llx sub psf$cy psf$sy div psf$ury sub +TR/showpage{}N/erasepage{}N/copypage{}N/p 3 def @MacSetUp}N/doclip{ +psf$llx psf$lly psf$urx psf$ury currentpoint 6 2 roll newpath 4 copy 4 2 +roll moveto 6 -1 roll S lineto S lineto S lineto closepath clip newpath +moveto}N/endTexFig{end psf$SavedState restore}N/@beginspecial{SDict +begin/SpecialSave save N gsave normalscale currentpoint TR +@SpecialDefaults count/ocount X/dcount countdictstack N}N/@setspecial{ +CLIP 1 eq{newpath 0 0 moveto hs 0 rlineto 0 vs rlineto hs neg 0 rlineto +closepath clip}if ho vo TR hsc vsc scale ang rotate rwiSeen{rwi urx llx +sub div rhiSeen{rhi ury lly sub div}{dup}ifelse scale llx neg lly neg TR +}{rhiSeen{rhi ury lly sub div dup scale llx neg lly neg TR}if}ifelse +CLIP 2 eq{newpath llx lly moveto urx lly lineto urx ury lineto llx ury +lineto closepath clip}if/showpage{}N/erasepage{}N/copypage{}N newpath}N +/@endspecial{count ocount sub{pop}repeat countdictstack dcount sub{end} +repeat grestore SpecialSave restore end}N/@defspecial{SDict begin}N +/@fedspecial{end}B/li{lineto}B/rl{rlineto}B/rc{rcurveto}B/np{/SaveX +currentpoint/SaveY X N 1 setlinecap newpath}N/st{stroke SaveX SaveY +moveto}N/fil{fill SaveX SaveY moveto}N/ellipse{/endangle X/startangle X +/yrad X/xrad X/savematrix matrix currentmatrix N TR xrad yrad scale 0 0 +1 startangle endangle arc savematrix setmatrix}N end diff --git a/kdvi/renderedDviPagePixmap.cpp b/kdvi/renderedDviPagePixmap.cpp new file mode 100644 index 00000000..3d123a24 --- /dev/null +++ b/kdvi/renderedDviPagePixmap.cpp @@ -0,0 +1,44 @@ +/*************************************************************************** + * 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 <config.h> + +#include "renderedDviPagePixmap.h" +#include "hyperlink.h" + + +RenderedDviPagePixmap::RenderedDviPagePixmap() + : RenderedDocumentPagePixmap() +{ + sourceHyperLinkList.reserve(200); +} + +RenderedDviPagePixmap::~RenderedDviPagePixmap() +{ +} + +void RenderedDviPagePixmap::clear() +{ + RenderedDocumentPagePixmap::clear(); + + sourceHyperLinkList.clear(); +} + +#include "renderedDviPagePixmap.moc" diff --git a/kdvi/renderedDviPagePixmap.h b/kdvi/renderedDviPagePixmap.h new file mode 100644 index 00000000..3b0f7272 --- /dev/null +++ b/kdvi/renderedDviPagePixmap.h @@ -0,0 +1,49 @@ +// -*- C++ -*- +/*************************************************************************** + * 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 _RENDEREDDVIPAGEPIXMAP_H_ +#define _RENDEREDDVIPAGEPIXMAP_H_ + +#include "renderedDocumentPagePixmap.h" + +#include <qobject.h> + + +class RenderedDviPagePixmap : public RenderedDocumentPagePixmap +{ + Q_OBJECT + + public: + RenderedDviPagePixmap(); + + virtual ~RenderedDviPagePixmap(); + + virtual void clear(); + + /** \brief List of source hyperlinks + + List of source-hyperlinks in the current page. This vector is + generated when the current page is drawn. + */ + QValueVector<Hyperlink> sourceHyperLinkList; +}; + +#endif diff --git a/kdvi/special.cpp b/kdvi/special.cpp new file mode 100644 index 00000000..23e58441 --- /dev/null +++ b/kdvi/special.cpp @@ -0,0 +1,727 @@ + +// special.cpp + +// Methods for dviRenderer which deal with "\special" commands found in the +// DVI file + +// Copyright 2000--2004, Stefan Kebekus (kebekus@kde.org). + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmimetype.h> +#include <kprocio.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qimage.h> +#include <qstringlist.h> + +#include "dviFile.h" +#include "dviRenderer.h" +#include "hyperlink.h" +#include "kdvi.h" +#include "kdvi_multipage.h" +#include "psgs.h" +#include "xdvi.h" + +//#define DEBUG_SPECIAL + +extern QPainter *foreGroundPainter; + +void dviRenderer::printErrorMsgForSpecials(const QString& msg) +{ + if (dviFile->errorCounter < 25) { + kdError(4300) << msg << endl; + dviFile->errorCounter++; + if (dviFile->errorCounter == 25) + kdError(4300) << i18n("That makes 25 errors. Further error messages will not be printed.") << endl; + } +} + +// Parses a color specification, as explained in the manual to +// dvips. If the spec could not be parsed, an invalid color will be +// returned. + +QColor dviRenderer::parseColorSpecification(const QString& colorSpec) +{ + // Initialize the map of known colors, if that is not done yet. + if (namedColors.isEmpty()) { + namedColors["Red"] = QColor( (int)(255.0*1), (int)(255.0*0), (int)(255.0*0)); + namedColors["Tan"] = QColor( (int)(255.0*0.86), (int)(255.0*0.58), (int)(255.0*0.44)); + namedColors["Blue"] = QColor( (int)(255.0*0), (int)(255.0*0), (int)(255.0*1)); + namedColors["Cyan"] = QColor( (int)(255.0*0), (int)(255.0*1), (int)(255.0*1)); + namedColors["Gray"] = QColor( (int)(255.0*0.5), (int)(255.0*0.5), (int)(255.0*0.5)); + namedColors["Plum"] = QColor( (int)(255.0*0.5), (int)(255.0*0), (int)(255.0*1)); + namedColors["Black"] = QColor( (int)(255.0*0), (int)(255.0*0), (int)(255.0*0)); + namedColors["Brown"] = QColor( (int)(255.0*0.4), (int)(255.0*0), (int)(255.0*0)); + namedColors["Green"] = QColor( (int)(255.0*0), (int)(255.0*1), (int)(255.0*0)); + namedColors["Melon"] = QColor( (int)(255.0*1), (int)(255.0*0.54), (int)(255.0*0.5)); + namedColors["Peach"] = QColor( (int)(255.0*1), (int)(255.0*0.5), (int)(255.0*0.3)); + namedColors["Sepia"] = QColor( (int)(255.0*0.3), (int)(255.0*0), (int)(255.0*0)); + namedColors["White"] = QColor( (int)(255.0*1), (int)(255.0*1), (int)(255.0*1)); + namedColors["Maroon"] = QColor( (int)(255.0*0.68), (int)(255.0*0), (int)(255.0*0)); + namedColors["Orange"] = QColor( (int)(255.0*1), (int)(255.0*0.39), (int)(255.0*0.13)); + namedColors["Orchid"] = QColor( (int)(255.0*0.68), (int)(255.0*0.36), (int)(255.0*1)); + namedColors["Purple"] = QColor( (int)(255.0*0.55), (int)(255.0*0.14), (int)(255.0*1)); + namedColors["Salmon"] = QColor( (int)(255.0*1), (int)(255.0*0.47), (int)(255.0*0.62)); + namedColors["Violet"] = QColor( (int)(255.0*0.21), (int)(255.0*0.12), (int)(255.0*1)); + namedColors["Yellow"] = QColor( (int)(255.0*1), (int)(255.0*1), (int)(255.0*0)); + namedColors["Apricot"] = QColor( (int)(255.0*1), (int)(255.0*0.68), (int)(255.0*0.48)); + namedColors["Emerald"] = QColor( (int)(255.0*0), (int)(255.0*1), (int)(255.0*0.5)); + namedColors["Fuchsia"] = QColor( (int)(255.0*0.45), (int)(255.0*0.01), (int)(255.0*0.92)); + namedColors["Magenta"] = QColor( (int)(255.0*1), (int)(255.0*0), (int)(255.0*1)); + namedColors["SkyBlue"] = QColor( (int)(255.0*0.38), (int)(255.0*1), (int)(255.0*0.88)); + namedColors["Thistle"] = QColor( (int)(255.0*0.88), (int)(255.0*0.41), (int)(255.0*1)); + namedColors["BrickRed"] = QColor( (int)(255.0*0.72), (int)(255.0*0), (int)(255.0*0)); + namedColors["Cerulean"] = QColor( (int)(255.0*0.06), (int)(255.0*0.89), (int)(255.0*1)); + namedColors["Lavender"] = QColor( (int)(255.0*1), (int)(255.0*0.52), (int)(255.0*1)); + namedColors["Mahogany"] = QColor( (int)(255.0*0.65), (int)(255.0*0), (int)(255.0*0)); + namedColors["Mulberry"] = QColor( (int)(255.0*0.64), (int)(255.0*0.08), (int)(255.0*0.98)); + namedColors["NavyBlue"] = QColor( (int)(255.0*0.06), (int)(255.0*0.46), (int)(255.0*1)); + namedColors["SeaGreen"] = QColor( (int)(255.0*0.31), (int)(255.0*1), (int)(255.0*0.5)); + namedColors["TealBlue"] = QColor( (int)(255.0*0.12), (int)(255.0*0.98), (int)(255.0*0.64)); + namedColors["BlueGreen"] = QColor( (int)(255.0*0.15), (int)(255.0*1), (int)(255.0*0.67)); + namedColors["CadetBlue"] = QColor( (int)(255.0*0.38), (int)(255.0*0.43), (int)(255.0*0.77)); + namedColors["Dandelion"] = QColor( (int)(255.0*1), (int)(255.0*0.71), (int)(255.0*0.16)); + namedColors["Goldenrod"] = QColor( (int)(255.0*1), (int)(255.0*0.9), (int)(255.0*0.16)); + namedColors["LimeGreen"] = QColor( (int)(255.0*0.5), (int)(255.0*1), (int)(255.0*0)); + namedColors["OrangeRed"] = QColor( (int)(255.0*1), (int)(255.0*0), (int)(255.0*0.5)); + namedColors["PineGreen"] = QColor( (int)(255.0*0), (int)(255.0*0.75), (int)(255.0*0.16)); + namedColors["RawSienna"] = QColor( (int)(255.0*0.55), (int)(255.0*0), (int)(255.0*0)); + namedColors["RedOrange"] = QColor( (int)(255.0*1), (int)(255.0*0.23), (int)(255.0*0.13)); + namedColors["RedViolet"] = QColor( (int)(255.0*0.59), (int)(255.0*0), (int)(255.0*0.66)); + namedColors["Rhodamine"] = QColor( (int)(255.0*1), (int)(255.0*0.18), (int)(255.0*1)); + namedColors["RoyalBlue"] = QColor( (int)(255.0*0), (int)(255.0*0.5), (int)(255.0*1)); + namedColors["RubineRed"] = QColor( (int)(255.0*1), (int)(255.0*0), (int)(255.0*0.87)); + namedColors["Turquoise"] = QColor( (int)(255.0*0.15), (int)(255.0*1), (int)(255.0*0.8)); + namedColors["VioletRed"] = QColor( (int)(255.0*1), (int)(255.0*0.19), (int)(255.0*1)); + namedColors["Aquamarine"] = QColor( (int)(255.0*0.18), (int)(255.0*1), (int)(255.0*0.7)); + namedColors["BlueViolet"] = QColor( (int)(255.0*0.1), (int)(255.0*0.05), (int)(255.0*0.96)); + namedColors["DarkOrchid"] = QColor( (int)(255.0*0.6), (int)(255.0*0.2), (int)(255.0*0.8)); + namedColors["OliveGreen"] = QColor( (int)(255.0*0), (int)(255.0*0.6), (int)(255.0*0)); + namedColors["Periwinkle"] = QColor( (int)(255.0*0.43), (int)(255.0*0.45), (int)(255.0*1)); + namedColors["Bittersweet"] = QColor( (int)(255.0*0.76), (int)(255.0*0.01), (int)(255.0*0)); + namedColors["BurntOrange"] = QColor( (int)(255.0*1), (int)(255.0*0.49), (int)(255.0*0)); + namedColors["ForestGreen"] = QColor( (int)(255.0*0), (int)(255.0*0.88), (int)(255.0*0)); + namedColors["GreenYellow"] = QColor( (int)(255.0*0.85), (int)(255.0*1), (int)(255.0*0.31)); + namedColors["JungleGreen"] = QColor( (int)(255.0*0.01), (int)(255.0*1), (int)(255.0*0.48)); + namedColors["ProcessBlue"] = QColor( (int)(255.0*0.04), (int)(255.0*1), (int)(255.0*1)); + namedColors["RoyalPurple"] = QColor( (int)(255.0*0.25), (int)(255.0*0.1), (int)(255.0*1)); + namedColors["SpringGreen"] = QColor( (int)(255.0*0.74), (int)(255.0*1), (int)(255.0*0.24)); + namedColors["YellowGreen"] = QColor( (int)(255.0*0.56), (int)(255.0*1), (int)(255.0*0.26)); + namedColors["MidnightBlue"] = QColor( (int)(255.0*0), (int)(255.0*0.44), (int)(255.0*0.57)); + namedColors["YellowOrange"] = QColor( (int)(255.0*1), (int)(255.0*0.58), (int)(255.0*0)); + namedColors["CarnationPink"] = QColor( (int)(255.0*1), (int)(255.0*0.37), (int)(255.0*1)); + namedColors["CornflowerBlue"] = QColor( (int)(255.0*0.35), (int)(255.0*0.87), (int)(255.0*1)); + namedColors["WildStrawberry"] = QColor( (int)(255.0*1), (int)(255.0*0.04), (int)(255.0*0.61)); + } + + QString specType = colorSpec.section(' ', 0, 0); + + if (specType.find("rgb", false) == 0) { + bool ok; + + double r = colorSpec.section(' ', 1, 1).toDouble(&ok); + if ((ok == false) || (r < 0.0) || (r > 1.0)) + return QColor(); + + double g = colorSpec.section(' ', 2, 2).toDouble(&ok); + if ((ok == false) || (g < 0.0) || (g > 1.0)) + return QColor(); + + double b = colorSpec.section(' ', 3, 3).toDouble(&ok); + if ((ok == false) || (b < 0.0) || (b > 1.0)) + return QColor(); + + return QColor((int)(r*255.0+0.5), (int)(g*255.0+0.5), (int)(b*255.0+0.5)); + } + + if (specType.find("hsb", false) == 0) { + bool ok; + + double h = colorSpec.section(' ', 1, 1).toDouble(&ok); + if ((ok == false) || (h < 0.0) || (h > 1.0)) + return QColor(); + + double s = colorSpec.section(' ', 2, 2).toDouble(&ok); + if ((ok == false) || (s < 0.0) || (s > 1.0)) + return QColor(); + + double b = colorSpec.section(' ', 3, 3).toDouble(&ok); + if ((ok == false) || (b < 0.0) || (b > 1.0)) + return QColor(); + + return QColor((int)(h*359.0+0.5), (int)(s*255.0+0.5), (int)(b*255.0+0.5), QColor::Hsv); + } + + if (specType.find("cmyk", false) == 0) { + bool ok; + + double c = colorSpec.section(' ', 1, 1).toDouble(&ok); + if ((ok == false) || (c < 0.0) || (c > 1.0)) + return QColor(); + + double m = colorSpec.section(' ', 2, 2).toDouble(&ok); + if ((ok == false) || (m < 0.0) || (m > 1.0)) + return QColor(); + + double y = colorSpec.section(' ', 3, 3).toDouble(&ok); + if ((ok == false) || (y < 0.0) || (y > 1.0)) + return QColor(); + + double k = colorSpec.section(' ', 3, 3).toDouble(&ok); + if ((ok == false) || (k < 0.0) || (k > 1.0)) + return QColor(); + + // Convert cmyk coordinates to rgb. + double r = 1.0 - c - k; + if (r < 0.0) + r = 0.0; + double g = 1.0 - m - k; + if (g < 0.0) + g = 0.0; + double b = 1.0 - y - k; + if (b < 0.0) + b = 0.0; + + return QColor((int)(r*255.0+0.5), (int)(g*255.0+0.5), (int)(b*255.0+0.5)); + } + + if (specType.find("gray", false) == 0) { + bool ok; + + double g = colorSpec.section(' ', 1, 1).toDouble(&ok); + if ((ok == false) || (g < 0.0) || (g > 1.0)) + return QColor(); + + return QColor((int)(g*255.0+0.5), (int)(g*255.0+0.5), (int)(g*255.0+0.5)); + } + + // Check if the color is one of the known named colors. + QMap<QString, QColor>::Iterator f = namedColors.find(specType); + if (f != namedColors.end()) + return *f; + + return QColor(specType); +} + + + + + + +void dviRenderer::color_special(const QString& _cp) +{ + QString const cp = _cp.stripWhiteSpace(); + + QString command = cp.section(' ', 0, 0); + + if (command == "pop") { + // Take color off the stack + if (colorStack.isEmpty()) + printErrorMsgForSpecials( i18n("Error in DVIfile '%1', page %2. Color pop command issued when the color stack is empty." ). + arg(dviFile->filename).arg(current_page)); + else + colorStack.pop(); + return; + } + + if (command == "push") { + // Get color specification + QColor const col = parseColorSpecification(cp.section(' ', 1)); + // Set color + if (col.isValid()) + colorStack.push(col); + else + colorStack.push(Qt::black); + return; + } + + // Get color specification and set the color for the rest of this + // page + QColor col = parseColorSpecification(cp); + // Set color + if (col.isValid()) + globalColor = col; + else + globalColor = Qt::black; + return; +} + + +void dviRenderer::html_href_special(const QString& _cp) +{ + QString cp = _cp; + cp.truncate(cp.find('"')); + +#ifdef DEBUG_SPECIAL + kdDebug(4300) << "HTML-special, href " << cp.latin1() << endl; +#endif + HTML_href = new QString(cp); +} + + +void dviRenderer::html_anchor_end() +{ +#ifdef DEBUG_SPECIAL + kdDebug(4300) << "HTML-special, anchor-end" << endl; +#endif + + if (HTML_href != NULL) { + delete HTML_href; + HTML_href = NULL; + } +} + + +void dviRenderer::source_special(const QString& cp) +{ + // only when rendering really takes place: set source_href to the + // current special string. When characters are rendered, the + // rendering routine will then generate a DVI_HyperLink and add it + // to the proper list. This DVI_HyperLink is used to match mouse + // positions with the hyperlinks for inverse search. + if (source_href) + *source_href = cp; + else + source_href = new QString(cp); +} + + +void parse_special_argument(const QString& strg, const char* argument_name, int* variable) +{ + int index = strg.find(argument_name); + if (index >= 0) { + QString tmp = strg.mid(index + strlen(argument_name)); + index = tmp.find(' '); + if (index >= 0) + tmp.truncate(index); + + bool OK; + float const tmp_float = tmp.toFloat(&OK); + + if (OK) + *variable = int(tmp_float+0.5); + else + // Maybe we should open a dialog here. + kdError(4300) << i18n("Malformed parameter in the epsf special command.\n" + "Expected a float to follow %1 in %2") + .arg(argument_name).arg(strg) << endl; + } +} + + +void dviRenderer::epsf_special(const QString& cp) +{ +#ifdef DEBUG_SPECIAL + kdDebug(4300) << "epsf-special: psfile=" << cp <<endl; +#endif + + QString include_command = cp.simplifyWhiteSpace(); + + // The line is supposed to start with "..ile=", and then comes the + // filename. Figure out what the filename is and stow it away. Of + // course, this does not work if the filename contains spaces + // (already the simplifyWhiteSpace() above is wrong). If you have + // files like this, go away. + QString EPSfilename_orig = include_command; + EPSfilename_orig.truncate(EPSfilename_orig.find(' ')); + + // Strip enclosing quotation marks which are included by some LaTeX + // macro packages (but not by others). This probably means that + // graphic files are no longer found if the filename really does + // contain quotes, but we don't really care that much. + if ((EPSfilename_orig.at(0) == '\"') && (EPSfilename_orig.at(EPSfilename_orig.length()-1) == '\"')) { + EPSfilename_orig = EPSfilename_orig.mid(1,EPSfilename_orig.length()-2); + } + QString EPSfilename = ghostscript_interface::locateEPSfile(EPSfilename_orig, baseURL); + + // Now parse the arguments. + int llx = 0; + int lly = 0; + int urx = 0; + int ury = 0; + int rwi = 0; + int rhi = 0; + int angle = 0; + + // just to avoid ambiguities; the filename could contain keywords + include_command = include_command.mid(include_command.find(' ')); + + parse_special_argument(include_command, "llx=", &llx); + parse_special_argument(include_command, "lly=", &lly); + parse_special_argument(include_command, "urx=", &urx); + parse_special_argument(include_command, "ury=", &ury); + parse_special_argument(include_command, "rwi=", &rwi); + parse_special_argument(include_command, "rhi=", &rhi); + parse_special_argument(include_command, "angle=", &angle); + + // If we have a png, gif, jpeg or mng file, we need to draw it here. + KMimeType::Ptr const mime_type = KMimeType::findByFileContent(EPSfilename); + QString const & mime_type_name = mime_type->name(); + bool const isGFX = (mime_type_name == "image/png" || + mime_type_name == "image/gif" || + mime_type_name == "image/jpeg" || + mime_type_name == "video/x-mng"); + + // So, if we do not have a PostScript file, but a graphics file, and + // if that file exists, we draw it here. + if (isGFX && QFile::exists(EPSfilename)) { + // Don't show PostScript, just draw the bounding box. For this, + // calculate the size of the bounding box in Pixels. + double bbox_width = urx - llx; + double bbox_height = ury - lly; + + if ((rwi != 0)&&(bbox_width != 0)) { + bbox_height *= rwi/bbox_width; + bbox_width = rwi; + } + if ((rhi != 0)&&(bbox_height != 0)) { + bbox_width *= rhi/bbox_height; + bbox_height = rhi; + } + + double fontPixelPerDVIunit = dviFile->getCmPerDVIunit() * 1200.0/2.54; + + bbox_width *= 0.1 * 65536.0*fontPixelPerDVIunit / shrinkfactor; + bbox_height *= 0.1 * 65536.0*fontPixelPerDVIunit / shrinkfactor; + + QImage image(EPSfilename); + image = image.smoothScale((int)(bbox_width), (int)(bbox_height)); + foreGroundPainter->drawImage( ((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))), currinf.data.pxl_v - (int)bbox_height, image); + return; + } + + if (!_postscript || !QFile::exists(EPSfilename)) { + // Don't show PostScript, just draw the bounding box. For this, + // calculate the size of the bounding box in Pixels. + double bbox_width = urx - llx; + double bbox_height = ury - lly; + + if ((rwi != 0)&&(bbox_width != 0)) { + bbox_height *= rwi/bbox_width; + bbox_width = rwi; + } + if ((rhi != 0)&&(bbox_height != 0)) { + bbox_width *= rhi/bbox_height; + bbox_height = rhi; + } + + double fontPixelPerDVIunit = dviFile->getCmPerDVIunit() * 1200.0/2.54; + + bbox_width *= 0.1 * 65536.0*fontPixelPerDVIunit / shrinkfactor; + bbox_height *= 0.1 * 65536.0*fontPixelPerDVIunit / shrinkfactor; + + QRect bbox(((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))), currinf.data.pxl_v - (int)bbox_height, + (int)bbox_width, (int)bbox_height); + + foreGroundPainter->save(); + + if (QFile::exists(EPSfilename)) + foreGroundPainter->setBrush(Qt::lightGray); + else + foreGroundPainter->setBrush(Qt::red); + foreGroundPainter->setPen(Qt::black); + foreGroundPainter->drawRoundRect(bbox, 2, 2); + QFont f = foreGroundPainter->font(); + f.setPointSize(8); + foreGroundPainter->setFont(f); + if (QFile::exists(EPSfilename)) + foreGroundPainter->drawText (bbox, (int)(Qt::AlignCenter), EPSfilename_orig, -1); + else + foreGroundPainter->drawText (bbox, (int)(Qt::AlignCenter), + i18n("File not found: \n %1").arg(EPSfilename_orig), -1); + foreGroundPainter->restore(); + } + + return; +} + + +void dviRenderer::TPIC_flushPath_special() +{ +#ifdef DEBUG_SPECIAL + kdDebug(4300) << "TPIC special flushPath" << endl; +#endif + + if (number_of_elements_in_path == 0) { + printErrorMsgForSpecials("TPIC special flushPath called when path was empty."); + return; + } + + QPen pen(Qt::black, (int)(penWidth_in_mInch*resolutionInDPI/1000.0 + 0.5)); // Sets the pen size in milli-inches + foreGroundPainter->setPen(pen); + foreGroundPainter->drawPolyline(TPIC_path, 0, number_of_elements_in_path); + number_of_elements_in_path = 0; +} + + +void dviRenderer::TPIC_addPath_special(const QString& cp) +{ +#ifdef DEBUG_SPECIAL + kdDebug(4300) << "TPIC special addPath: " << cp << endl; +#endif + + // Adds a point to the path list + QString cp_noWhiteSpace = cp.stripWhiteSpace(); + bool ok; + float xKoord = cp_noWhiteSpace.section(' ', 0, 0).toFloat(&ok); + if (ok == false) { + printErrorMsgForSpecials( QString("TPIC special; cannot parse first argument in 'pn %1'.").arg(cp) ); + return; + } + float yKoord = cp_noWhiteSpace.section(' ', 1, 1).toFloat(&ok); + if (ok == false) { + printErrorMsgForSpecials( QString("TPIC special; cannot parse second argument in 'pn %1'.").arg(cp) ); + return; + } + + float mag = dviFile->getMagnification()/1000.0; + + int x = (int)( currinf.data.dvi_h/(shrinkfactor*65536.0) + mag*xKoord*resolutionInDPI/1000.0 + 0.5 ); + int y = (int)( currinf.data.pxl_v + mag*yKoord*resolutionInDPI/1000.0 + 0.5 ); + + // Initialize the point array used to store the path + if (TPIC_path.size() == 0) + number_of_elements_in_path = 0; + if (TPIC_path.size() == number_of_elements_in_path) + TPIC_path.resize(number_of_elements_in_path+100); + TPIC_path.setPoint(number_of_elements_in_path++, x, y); +} + + +void dviRenderer::TPIC_setPen_special(const QString& cp) +{ +#ifdef DEBUG_SPECIAL + kdDebug(4300) << "TPIC special setPen: " << cp << endl; +#endif + + // Sets the pen size in milli-inches + bool ok; + penWidth_in_mInch = cp.stripWhiteSpace().toFloat(&ok); + if (ok == false) { + printErrorMsgForSpecials( QString("TPIC special; cannot parse argument in 'pn %1'.").arg(cp) ); + penWidth_in_mInch = 0.0; + return; + } +} + + +void dviRenderer::applicationDoSpecial(char *cp) +{ + QString special_command(cp); + + // First come specials which is only interpreted during rendering, + // and NOT during the prescan phase + + // font color specials + if (strncasecmp(cp, "color", 5) == 0) { + color_special(special_command.mid(5)); + return; + } + + // HTML reference + if (strncasecmp(cp, "html:<A href=", 13) == 0) { + html_href_special(special_command.mid(14)); + return; + } + + // HTML anchor end + if (strncasecmp(cp, "html:</A>", 9) == 0) { + html_anchor_end(); + return; + } + + // TPIC specials + if (strncasecmp(cp, "pn", 2) == 0) { + TPIC_setPen_special(special_command.mid(2)); + return; + } + if (strncasecmp(cp, "pa ", 3) == 0) { + TPIC_addPath_special(special_command.mid(3)); + return; + } + if (strncasecmp(cp, "fp", 2) == 0) { + TPIC_flushPath_special(); + return; + } + + // Encapsulated Postscript File + if (strncasecmp(cp, "PSfile=", 7) == 0) { + epsf_special(special_command.mid(7)); + return; + } + + // source special + if (strncasecmp(cp, "src:", 4) == 0) { + source_special(special_command.mid(4)); + return; + } + + // Unfortunately, in some TeX distribution the hyperref package uses + // the dvips driver by default, rather than the hypertex driver. As + // a result, the DVI files produced are full of PostScript that + // specifies links and anchors, and KDVI would call the ghostscript + // interpreter for every page which makes it really slow. This is a + // major nuisance, so that we try to filter and interpret the + // hypertex generated PostScript here. + if (special_command.startsWith("ps:SDict begin")) { + + // Hyperref: start of hyperref rectangle. At this stage it is not + // yet clear if the rectangle will conain a hyperlink, an anchor, + // or another type of object. We suspect that this rectangle will + // define a hyperlink, allocate a QString and set HTML_href to + // point to this string. The string contains the name of the + // destination which ---due to the nature of the PostScript + // language--- will be defined only after characters are drawn and + // the hyperref rectangle has been closed. We use "glopglyph" as a + // temporary name. Since the pointer HTML_href is not NULL, the + // chracter drawing routines will now underline all characters in + // blue to point out that they correspond to a hyperlink. Also, as + // soon as characters are drawn, the drawing routines will + // allocate a Hyperlink and add it to the top of the vector + // currentlyDrawnPage->hyperLinkList. + if (special_command == "ps:SDict begin H.S end") { + // At this stage, the vector 'hyperLinkList' should not contain + // links with unspecified destinations (i.e. destination set to + // 'glopglyph'). As a protection against bad DVI files, we make + // sure to remove all link rectangles which point to + // 'glopglyph'. + while (!currentlyDrawnPage->hyperLinkList.isEmpty()) + if (currentlyDrawnPage->hyperLinkList.last().linkText == "glopglyph") + currentlyDrawnPage->hyperLinkList.pop_back(); + else + break; + + HTML_href = new QString("glopglyph"); + return; + } + + // Hyperref: end of hyperref rectangle of unknown type or hyperref + // link rectangle. In these cases we set HTML_href to NULL, which + // causes the character drawing routines to stop drawing + // characters underlined in blue. Note that the name of the + // destination is still set to "glopglyph". In a well-formed DVI + // file, this special command is immediately followed by another + // special, where the destination is specified. This special is + // treated below. + if ((special_command == "ps:SDict begin H.R end") || special_command.endsWith("H.L end")) { + if (HTML_href != NULL) { + delete HTML_href; + HTML_href = NULL; + } + return; // end of hyperref rectangle + } + + // Hyperref: end of anchor rectangle. If this special is + // encountered, the rectangle, which was started with "ps:SDict + // begin H.S end" does not contain a link, but an anchor for a + // link. Anchors, however, have already been dealt with in the + // prescan phase and will not be considered here. Thus, we set + // HTML_href to NULL so that character drawing routines will no + // longer underline hyperlinks in blue, and remove the link from + // the hyperLinkList. NOTE: in a well-formed DVI file, the "H.A" + // special comes directly after the "H.S" special. A + // hyperlink-anchor rectangle therefore never contains characters, + // so no character will by accidentally underlined in blue. + if (special_command.endsWith("H.A end")) { + if (HTML_href != NULL) { + delete HTML_href; + HTML_href = NULL; + } + while (!currentlyDrawnPage->hyperLinkList.isEmpty()) + if (currentlyDrawnPage->hyperLinkList.last().linkText == "glopglyph") + currentlyDrawnPage->hyperLinkList.pop_back(); + else + break; + return; // end of hyperref anchor + } + + // Hyperref: specification of a hyperref link rectangle's + // destination. As mentioned above, the destination of a hyperlink + // is specified only AFTER the rectangle has been specified. We + // will therefore go through the list of rectangles stored in + // currentlyDrawnPage->hyperLinkList, find those whose destination + // is open and fill in the value found here. NOTE: the character + // drawing routines sometimes split a single hyperlink rectangle + // into several rectangles (e.g. if the font changes, or when a + // line break is encountered) + if (special_command.startsWith("ps:SDict begin [") && special_command.endsWith(" pdfmark end")) { + if (!currentlyDrawnPage->hyperLinkList.isEmpty()) { + // Parse the PostScript literal text string inside parentheses + // and store it into 'targetName'. The scanner works + // according to "PostScript language reference, third edition" + // - Sec. 3.2.2. The specification is implemented completely: + // balanced parentheses and all escape sequences are + // considered. + QString tmpTargetName = special_command.section('(', 1); + QString targetName; + int parencount = 1; + for(int i=0; i<tmpTargetName.length(); i++) { + if (tmpTargetName[i] == '(') + if ((i == 0) || (tmpTargetName[i-1] != '\\')) + parencount++; + if (tmpTargetName[i] == ')') + if ((i == 0) || (tmpTargetName[i-1] != '\\')) + parencount--; + if (parencount == 0) + break; + targetName += tmpTargetName[i]; + } + targetName = PDFencodingToQString(targetName); + + QValueVector<Hyperlink>::iterator it; + for( it = currentlyDrawnPage->hyperLinkList.begin(); it != currentlyDrawnPage->hyperLinkList.end(); ++it ) + if (it->linkText == "glopglyph") + it->linkText = targetName; + } + return; // hyperref definition of link/anchor/bookmark/etc + } + } + + // Detect text rotation specials that are included by the graphicx + // package. If one of these specials is found, the state of the + // painter is saved, and the coordinate system is rotated + // accordingly + if (special_command.startsWith("ps: gsave currentpoint currentpoint translate ") && + special_command.endsWith(" neg rotate neg exch neg exch translate") ) { + bool ok; + double angle = special_command.section(' ', 5, 5).toDouble(&ok); + if (ok == true) { + int x = ((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))); + int y = currinf.data.pxl_v; + + foreGroundPainter->save(); + // Rotate about the current point + foreGroundPainter->translate(x,y); + foreGroundPainter->rotate(-angle); + foreGroundPainter->translate(-x,-y); + } else + printErrorMsgForSpecials( i18n("Error in DVIfile '%1', page %2. Could not interpret angle in text rotation special." ). + arg(dviFile->filename).arg(current_page)); + } + + // The graphicx package marks the end of rotated text with this + // special. The state of the painter is restored. + if (special_command == "ps: currentpoint grestore moveto") { + foreGroundPainter->restore(); + } + + // The following special commands are not used here; they are of + // interest only during the prescan phase. We recognize them here + // anyway, to make sure that KDVI doesn't complain about + // unrecognized special commands. + if ((cp[0] == '!') || + (cp[0] == '"') || + (strncasecmp(cp, "html:<A name=", 13) == 0) || + (strncasecmp(cp, "ps:", 3) == 0) || + (strncasecmp(cp, "papersize", 9) == 0) || + (strncasecmp(cp, "header", 6) == 0) || + (strncasecmp(cp, "background", 10) == 0) ) + return; + + printErrorMsgForSpecials(i18n("The special command '%1' is not implemented.").arg(special_command)); + return; +} diff --git a/kdvi/squeeze.c b/kdvi/squeeze.c new file mode 100644 index 00000000..d0e2515f --- /dev/null +++ b/kdvi/squeeze.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 1994 Paul Vojta. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * NOTE: + * This routine is adapted from the squeeze.c that comes with dvips; + * it bears the message: + * This software is Copyright 1988 by Radical Eye Software. + * Used with permission. + */ +/* + * This routine squeezes a PostScript file down to its + * minimum. We parse and then output it. + * Adapted for xdvi 1/94. Writes a C program that contains the PS file + * as a constant string. + */ +#include <stdio.h> +#include <string.h> +#define LINELENGTH (72) +#define BUFLENGTH (1000) +#undef putchar +#define putchar(a) (void)putc(a, out) ; +FILE *in, *out ; +static int linepos = 0 ; +static int lastspecial = 1 ; +static int stringlen = 0; + +void specialout(char); +void strout(char *); +void cmdout(char *); + +/* + * This next routine writes out a `special' character. In this case, + * we simply put it out, since any special character terminates the + * preceding token. + */ +void specialout(char c) +{ + if (linepos + 1 > LINELENGTH) { + (void)fputs("\\n\\\n", out); + stringlen += linepos + 1; + linepos = 0 ; + } + putchar(c) ; + linepos++ ; + lastspecial = 1 ; +} +void strout(s) +char *s ; +{ + if (linepos + strlen(s) > LINELENGTH) { + (void)fputs("\\n\\\n", out); + stringlen += linepos + 1; + linepos = 0 ; + } + linepos += strlen(s) ; + while (*s != 0) + putchar(*s++) ; + lastspecial = 1 ; +} +void cmdout(s) +char *s ; +{ + int l ; + + l = strlen(s) ; + if (linepos + l + 1 > LINELENGTH) { + (void)fputs("\\n\\\n", out); + stringlen += linepos + 1; + linepos = 0 ; + lastspecial = 1 ; + } + if (! lastspecial) { + putchar(' ') ; + linepos++ ; + } + while (*s != 0) { + putchar(*s++) ; + } + linepos += l ; + lastspecial = 0 ; +} +char buf[BUFLENGTH] ; + +int main(int argc, char *argv[]) +{ + int c ; + char *b ; + char seeking ; + extern void exit() ; + + if (argc > 3 || (in=(argc < 2 ? stdin : fopen(argv[1], "r")))==NULL || + (out=(argc < 3 ? stdout : fopen(argv[2], "w")))==NULL) { + (void)fprintf(stderr, "Usage: squeeze [infile [outfile]]\n") ; + exit(1) ; + } + (void)fputs("/*\n\ + * DO NOT EDIT THIS FILE!\n\ + * It was created by squeeze.c from another file (see the Makefile).\n\ + */\n\n\ +#ifndef _Xconst\n\ +#if __STDC__\n\ +#define _Xconst const\n\ +#else\n\ +#define _Xconst\n\ +#endif\n\ +#endif\n\n\ +_Xconst char psheader[] = \"\\\n", out); + while (1) { + c = getc(in) ; + if (c==EOF) + break ; + if (c=='%') { + while ((c=getc(in))!='\n') ; + } + if (c <= ' ') + continue ; + switch (c) { +case '{' : +case '}' : +case '[' : +case ']' : + specialout(c) ; + break ; +case '<' : +case '(' : + if (c=='(') + seeking = ')' ; + else + seeking = '>' ; + b = buf ; + *b++ = c ; + do { + c = getc(in) ; + if (b > buf + BUFLENGTH-2) { + (void)fprintf(stderr, "Overran buffer seeking %c", seeking) ; + exit(1) ; + } + *b++ = c ; + if (c=='\\') + *b++ = getc(in) ; + } while (c != seeking) ; + *b++ = 0 ; + strout(buf) ; + break ; +default: + b = buf ; + while ((c>='A'&&c<='Z')||(c>='a'&&c<='z')|| + (c>='0'&&c<='9')||(c=='/')||(c=='@')|| + (c=='!')||(c=='"')||(c=='&')||(c=='*')||(c==':')|| + (c==',')||(c==';')||(c=='?')||(c=='^')||(c=='~')|| + (c=='-')||(c=='.')||(c=='#')||(c=='|')||(c=='_')|| + (c=='=')||(c=='$')||(c=='+')) { + *b++ = c ; + c = getc(in) ; + } + if (b == buf) { + (void)fprintf(stderr, "Oops! Missed a case: %c.\n", c) ; + exit(1) ; + } + *b++ = 0 ; + (void)ungetc(c, in) ; + cmdout(buf) ; + } + } + (void)fprintf(out, "\\n\";\n\n\ +int\tpsheaderlen\t= %d;\n", stringlen + linepos + 1); + return (0) ; + /*NOTREACHED*/ +} diff --git a/kdvi/tips b/kdvi/tips new file mode 100644 index 00000000..399d4427 --- /dev/null +++ b/kdvi/tips @@ -0,0 +1,42 @@ +<tip category="KDVI|General"> +<html> +<p>...that KDVI can also load compressed DVI-files? +</html> +</tip> + +<tip category="KDVI|General"> +<html> +<p>...that you can mark text with the right mouse button and paste it +into any application? +</html> +</tip> + +<tip category="KDVI|General"> +<html> +<p>...that KDVI now supports inverse search? You can click into your DVI file +with the middle mouse button and your editor opens, loads the TeX file, and +jumps to the proper line! <a href="help:/kdvi/inverse-search.html">The +manual explains how to set up your editor for this.</a> +</html> +</tip> + +<tip category="KDVI|General"> +<html> +<p>...that KDVI supports forward search? If you use Emacs or XEmacs, you can +jump directly from the TeX file to the associated place in the DVI file. +<a href="help:/kdvi/forward-search.html">The manual explains how to set up +your editor for this.</a> +</html> +</tip> + +<tip category="KDVI|General"> +<html> +<p>...that KDVI now offers full text search? +</html> +</tip> + +<tip category="KDVI|General"> +<html> +<p>...that KDVI can save your DVI file as PostScript, PDF, and even plain text? +</html> +</tip> diff --git a/kdvi/util.cpp b/kdvi/util.cpp new file mode 100644 index 00000000..d25b7e03 --- /dev/null +++ b/kdvi/util.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1994 Paul Vojta. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * NOTE: + * xdvi is based on prior work as noted in the modification history, below. + */ + +/* + * DVI previewer for X. + * + * Eric Cooper, CMU, September 1985. + * + * Code derived from dvi-imagen.c. + * + * Modification history: + * 1/1986 Modified for X.10 --Bob Scheifler, MIT LCS. + * 7/1988 Modified for X.11 --Mark Eichin, MIT + * 12/1988 Added 'R' option, toolkit, magnifying glass + * --Paul Vojta, UC Berkeley. + * 2/1989 Added tpic support --Jeffrey Lee, U of Toronto + * 4/1989 Modified for System V --Donald Richardson, Clarkson Univ. + * 3/1990 Added VMS support --Scott Allendorf, U of Iowa + * 7/1990 Added reflection mode --Michael Pak, Hebrew U of Jerusalem + * 1/1992 Added greyscale code --Till Brychcy, Techn. Univ. Muenchen + * and Lee Hetherington, MIT + * 4/1994 Added DPS support, bounding box + * --Ricardo Telichevesky + * and Luis Miguel Silveira, MIT RLE. + */ + +#include <config.h> + +#include <kdebug.h> +#include <kmessagebox.h> +#include <klocale.h> + +#include "dviRenderer.h" +#include "xdvi.h" + + + +/* + * General utility routines. + */ + +/* + * Print error message and quit. + */ + +void oops(QString message) +{ + kdError() << i18n("Fatal Error! ") << message << endl; + + KMessageBox::error( NULL, + i18n("Fatal error.\n\n") + + message + + i18n("\n\n\ +This probably means that either you found a bug in KDVI,\n\ +or that the DVI file, or auxiliary files (such as font files, \n\ +or virtual font files) were really badly broken.\n\ +KDVI will abort after this message. If you believe that you \n\ +found a bug, or that KDVI should behave better in this situation\n\ +please report the problem.")); + exit(1); +} + +/* + * + * Read size bytes from the FILE fp, constructing them into a + * signed/unsigned integer. + * + */ + +unsigned long num(FILE *fp, int size) +{ + register long x = 0; + + while (size--) x = (x << 8) | one(fp); + return x; +} + +long snum(FILE *fp, int size) +{ + register long x; + +#ifdef __STDC__ + x = (signed char) getc(fp); +#else + x = (unsigned char) getc(fp); + if (x & 0x80) x -= 0x100; +#endif + while (--size) x = (x << 8) | one(fp); + return x; +} diff --git a/kdvi/vf.cpp b/kdvi/vf.cpp new file mode 100644 index 00000000..36cad0ba --- /dev/null +++ b/kdvi/vf.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 1994 Paul Vojta. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "dvi.h" +#include "dviRenderer.h" +#include "fontpool.h" +#include "xdvi.h" + +extern void oops(QString message); + +/*** + *** VF font reading routines. + *** Public routine is read_index---because virtual characters are presumed + *** to be short, we read the whole virtual font in at once, instead of + *** faulting in characters as needed. + ***/ + +#define LONG_CHAR 242 + +/* + * These are parameters which determine whether macros are combined for + * storage allocation purposes. Small macros ( <= VF_PARM_1 bytes) are + * combined into chunks of size VF_PARM_2. + */ + +#ifndef VF_PARM_1 +#define VF_PARM_1 20 +#endif +#ifndef VF_PARM_2 +#define VF_PARM_2 256 +#endif + +/* + * The main routine + */ + +void TeXFontDefinition::read_VF_index() +{ +#ifdef DEBUG_FONTS + kdDebug(4300) << "font::read_VF_index()" << endl; +#endif + FILE *VF_file = file; + unsigned char cmnd; + unsigned char *avail, *availend; /* available space for macros */ + + flags |= FONT_VIRTUAL; + set_char_p = &dviRenderer::set_vf_char; +#ifdef DEBUG_FONTS + kdDebug(4300) << "TeXFontDefinition::read_VF_index: reading VF pixel file " << filename << endl; +#endif + // Read preamble. + fseek(VF_file, (long) one(VF_file), 1); /* skip comment */ + Q_UINT32 file_checksum = four(VF_file); + + if (file_checksum && checksum && file_checksum != checksum) + kdError(4300) << i18n("Checksum mismatch") << "(dvi = " << checksum << "u, vf = " << file_checksum << + "u)" << i18n(" in font file ") << filename << endl; + (void) four(VF_file); /* skip design size */ + + // Read the fonts. + first_font = NULL; + while ((cmnd = one(VF_file)) >= FNTDEF1 && cmnd <= FNTDEF4) { + int TeXnumber = num(VF_file, (int) cmnd - FNTDEF1 + 1); + Q_UINT32 checksum = four(VF_file); + Q_UINT32 scale = four(VF_file); + Q_UINT32 design = four(VF_file); + Q_UNUSED(design); + Q_UINT16 len = one(VF_file) + one(VF_file); /* sequence point in the middle */ + char *fontname = new char[len + 1]; + fread(fontname, sizeof(char), len, VF_file); + fontname[len] = '\0'; + +#ifdef DEBUG_FONTS + kdDebug(4300) << "Virtual font defines subfont \"" << fontname << "\" scale=" << scale << " design=" << design << endl; +#endif + + // According to Knuth's documentation found in the web source code + // of the "vftovp" program (which seems to be the standard + // definition of virtual fonts), the "scale" is a fixed point + // number which describes extra enlargement that the virtual font + // imposes. One obtains the enlargement by dividing 2^20. + double enlargement_factor = double(scale)/(1<<20) * enlargement; + + // TeXFontDefinition *newfontp = font_pool->appendx(fontname, checksum, (Q_UINT32)(scaled_size_in_DVI_units*enlargement_factor), enlargement_factor); + TeXFontDefinition *newfontp = font_pool->appendx(fontname, checksum, (Q_UINT32)((double(scale)/(1<<20))*scaled_size_in_DVI_units), enlargement_factor); + + // Insert font in dictionary and make sure the dictionary is big + // enough. + if (vf_table.size()-2 <= vf_table.count()) + // Not quite optimal. The size of the dictionary should be a + // prime. I don't care. + vf_table.resize(vf_table.size()*2); + vf_table.insert(TeXnumber, newfontp); + + if (first_font == NULL) + first_font = newfontp; + } + + // Prepare macro array. + macrotable = new macro[max_num_of_chars_in_font]; + if (macrotable == 0) { + kdError() << i18n("Could not allocate memory for a macro table.") << endl; + exit(0); + } + + // Read macros. + avail = availend = NULL; + for (; cmnd <= LONG_CHAR; cmnd = one(VF_file)) { + macro *m; + int len; + unsigned long cc; + long width; + + if (cmnd == LONG_CHAR) { /* long form packet */ + len = four(VF_file); + cc = four(VF_file); + width = four(VF_file); + if (cc >= 256) { + kdError() << i18n("Virtual character ") << cc << i18n(" in font ") + << fontname << i18n(" ignored.") << endl; + fseek(VF_file, (long) len, 1); + continue; + } + } else { /* short form packet */ + len = cmnd; + cc = one(VF_file); + width = num(VF_file, 3); + } + m = ¯otable[cc]; + + m->dvi_advance_in_units_of_design_size_by_2e20 = width; + if (len > 0) { + if (len <= availend - avail) { + m->pos = avail; + avail += len; + } else { + m->free_me = true; + if (len <= VF_PARM_1) { + m->pos = avail = new unsigned char [VF_PARM_2]; + availend = avail + VF_PARM_2; + avail += len; + } else + m->pos = new unsigned char[len]; + } + fread((char *) m->pos, 1, len, VF_file); + m->end = m->pos + len; + } + } + if (cmnd != POST) + oops(i18n("Wrong command byte found in VF macro list: %1").arg(cmnd)); + + fclose (VF_file); + file = NULL; +} diff --git a/kdvi/xdvi.h b/kdvi/xdvi.h new file mode 100644 index 00000000..0770cd8b --- /dev/null +++ b/kdvi/xdvi.h @@ -0,0 +1,24 @@ +// -*- C++ -*- +#ifndef _xdvi_h +#define _xdvi_h + +#include <stdio.h> + +/* + * Written by Eric C. Cooper, CMU + */ + +#define ROUNDUP(x,y) (((x)+(y)-1)/(y)) + +extern unsigned long num (FILE *, int); +extern long snum(FILE *, int); +extern struct WindowRec mane, currwin; + +#define one(fp) ((unsigned char) getc(fp)) +#define sone(fp) ((long) one(fp)) +#define two(fp) num (fp, 2) +#define stwo(fp) snum(fp, 2) +#define four(fp) num (fp, 4) +#define sfour(fp) snum(fp, 4) + +#endif /* _xdvi_h */ |