You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
810 lines
25 KiB
810 lines
25 KiB
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; c-brace-offset: 0; -*- |
|
// |
|
// 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 "dviRenderer.h" |
|
#include "dviFile.h" |
|
#include "dvisourcesplitter.h" |
|
#include "hyperlink.h" |
|
#include "kvs_debug.h" |
|
#include "prebookmark.h" |
|
#include "psgs.h" |
|
//#include "renderedDviPagePixmap.h" |
|
#include "dviPageInfo.h" |
|
|
|
#include <math.h> |
|
#include <QTime> |
|
#include <kconfig.h> |
|
#include <kglobal.h> |
|
#include <klocale.h> |
|
#include <kmessagebox.h> |
|
#include <kmimetype.h> |
|
#include <kstandarddirs.h> |
|
#include <kvbox.h> |
|
|
|
#include <QApplication> |
|
#include <QCheckBox> |
|
#include <QEventLoop> |
|
#include <QFileInfo> |
|
#include <QHBoxLayout> |
|
#include <QLabel> |
|
#include <QPainter> |
|
#include <QProgressBar> |
|
#include <QRegExp> |
|
|
|
//#define DEBUG_DVIRENDERER |
|
|
|
QPainter *foreGroundPainter; // QPainter used for text |
|
|
|
|
|
//------ now comes the dviRenderer class implementation ---------- |
|
|
|
dviRenderer::dviRenderer() |
|
: dviFile(0), |
|
resolutionInDPI(0), |
|
embedPS_progress(0), |
|
embedPS_numOfProgressedFiles(0), |
|
shrinkfactor(3), |
|
source_href(0), |
|
HTML_href(0), |
|
editorCommand(""), |
|
PostScriptOutPutString(0), |
|
PS_interface(new ghostscript_interface), |
|
_postscript(true), |
|
line_boundary_encountered(false), |
|
word_boundary_encountered(false), |
|
current_page(0), |
|
penWidth_in_mInch(0), |
|
number_of_elements_in_path(0), |
|
currentlyDrawnPage(0), |
|
m_eventLoop(0) |
|
{ |
|
#ifdef DEBUG_DVIRENDERER |
|
//kDebug(kvs::dvi) << "dviRenderer( parent=" << par << " )"; |
|
#endif |
|
|
|
// connect(&font_pool, SIGNAL( setStatusBarText( const QString& ) ), this, SIGNAL( setStatusBarText( const QString& ) ) ); |
|
// connect( &clearStatusBarTimer, SIGNAL(timeout()), this, SLOT(clearStatusBar()) ); |
|
// pass status bar messages through |
|
// connect(PS_interface, SIGNAL( setStatusBarText( const QString& ) ), this, SIGNAL( setStatusBarText( const QString& ) ) ); |
|
} |
|
|
|
|
|
dviRenderer::~dviRenderer() |
|
{ |
|
#ifdef DEBUG_DVIRENDERER |
|
kDebug(kvs::dvi) << "~dviRenderer"; |
|
#endif |
|
|
|
QMutexLocker locker(&mutex); |
|
|
|
delete PS_interface; |
|
delete dviFile; |
|
} |
|
|
|
#if 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 ); |
|
} |
|
|
|
|
|
void dviRenderer::showInfo() |
|
{ |
|
QMutexLocker locker(&mutex); |
|
|
|
info->setDVIData(dviFile); |
|
info->show(); |
|
} |
|
#endif |
|
|
|
|
|
//------ this function calls the dvi interpreter ---------- |
|
|
|
|
|
void dviRenderer::drawPage(RenderedDocumentPagePixmap* page) |
|
{ |
|
#ifdef DEBUG_DVIRENDERER |
|
//kDebug(kvs::dvi) << "dviRenderer::drawPage(documentPage *) called, page number " << page->pageNumber; |
|
#endif |
|
|
|
// Paranoid safety checks |
|
if (page == 0) { |
|
kError(kvs::dvi) << "dviRenderer::drawPage(documentPage *) called with argument == 0" << endl; |
|
return; |
|
} |
|
// Paranoid safety checks |
|
if (page->pageNumber == 0) { |
|
kError(kvs::dvi) << "dviRenderer::drawPage(documentPage *) called for a documentPage with page number 0" << endl; |
|
return; |
|
} |
|
|
|
QMutexLocker locker(&mutex); |
|
|
|
|
|
if ( dviFile == 0 ) { |
|
kError(kvs::dvi) << "dviRenderer::drawPage(documentPage *) called, but no dviFile class allocated." << endl; |
|
page->clear(); |
|
return; |
|
} |
|
if (page->pageNumber > dviFile->total_pages) { |
|
kError(kvs::dvi) << "dviRenderer::drawPage(documentPage *) called for a documentPage with page number " << page->pageNumber |
|
<< " but the current dviFile has only " << dviFile->total_pages << " pages." << endl; |
|
return; |
|
} |
|
if ( dviFile->dvi_Data() == 0 ) { |
|
kError(kvs::dvi) << "dviRenderer::drawPage(documentPage *) called, but no dviFile is loaded yet." << endl; |
|
page->clear(); |
|
return; |
|
} |
|
|
|
double resolution = page->resolution; |
|
|
|
if (resolution != resolutionInDPI) |
|
setResolution(resolution); |
|
|
|
currentlyDrawnPage = page; |
|
shrinkfactor = 1200/resolutionInDPI; |
|
current_page = page->pageNumber-1; |
|
|
|
|
|
// Reset colors |
|
colorStack.clear(); |
|
globalColor = Qt::black; |
|
|
|
int pageWidth = page->width; |
|
int pageHeight = page->height; |
|
|
|
QImage img(pageWidth, pageHeight, QImage::Format_RGB32); |
|
foreGroundPainter = new QPainter(&img); |
|
if (foreGroundPainter != 0) { |
|
errorMsg.clear(); |
|
draw_page(); |
|
delete foreGroundPainter; |
|
foreGroundPainter = 0; |
|
} |
|
else |
|
{ |
|
kDebug(kvs::dvi) << "painter creation failed."; |
|
} |
|
page->img = img; |
|
//page->setImage(img); |
|
|
|
// Postprocess hyperlinks |
|
// Without that, based on the way TeX draws certain characters like german "Umlaute", |
|
// some hyperlinks would be broken into two overlapping parts, in the middle of a word. |
|
QVector<Hyperlink>::iterator i = page->hyperLinkList.begin(); |
|
QVector<Hyperlink>::iterator j; |
|
while (i != page->hyperLinkList.end()) |
|
{ |
|
// Iterator j always points to the element after i. |
|
j = i; |
|
j++; |
|
|
|
if (j == page->hyperLinkList.end()) |
|
break; |
|
|
|
Hyperlink& hi = *i; |
|
Hyperlink& hj = *j; |
|
|
|
bool merged = false; |
|
|
|
// Merge all hyperlinks that point to the same target, and have the same baseline. |
|
while (hi.linkText == hj.linkText && hi.baseline == hj.baseline) |
|
{ |
|
merged = true; |
|
hi.box = hi.box.unite(hj.box); |
|
|
|
j++; |
|
if (j == page->hyperLinkList.end()) |
|
break; |
|
|
|
hj = *j; |
|
} |
|
|
|
if (merged) |
|
{ |
|
i = page->hyperLinkList.erase(++i, j); |
|
} |
|
else |
|
{ |
|
i++; |
|
} |
|
} |
|
|
|
#if 0 |
|
page->isEmpty = false; |
|
if (errorMsg.isEmpty() != true) { |
|
KMessageBox::detailedError(parentWidget, |
|
i18n("<qt><strong>File corruption</strong> Okular could not interpret your DVI file. This is " |
|
"most commonly caused by a corrupted file.</qt>"), |
|
errorMsg, i18n("DVI File Error")); |
|
errorMsg.clear(); |
|
currentlyDrawnPage = 0; |
|
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 |
|
//FIXME |
|
//QTimer::singleShot( 0, this, SLOT(showThatSourceInformationIsPresent()) ); |
|
} |
|
} |
|
#endif |
|
currentlyDrawnPage = 0; |
|
} |
|
|
|
|
|
void dviRenderer::getText(RenderedDocumentPagePixmap* page) |
|
{ |
|
bool postscriptBackup = _postscript; |
|
// Disable postscript-specials temporarely to speed up text extraction. |
|
_postscript = false; |
|
|
|
drawPage(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 = KGlobal::config(); |
|
KConfigGroup saver(config, "Notification Messages"); |
|
bool showMsg = config->readEntry( "KDVI-info_on_source_specials", true); |
|
|
|
if (showMsg) { |
|
KDialogBase dialog(i18n("KDVI: Information"), KDialogBase::Yes, KDialogBase::Yes, KDialogBase::Yes, |
|
parentWidget, "information", true, true, KStandardGuiItem::ok()); |
|
|
|
KVBox *topcontents = new KVBox (&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->addWidget(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->addWidget(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(); |
|
|
|
showMsg = !checkbox->isChecked(); |
|
if (!showMsg) { |
|
KConfigGroup saver(config, "Notification Messages"); |
|
config->writeEntry( "KDVI-info_on_source_specials", showMsg); |
|
} |
|
config->sync(); |
|
} |
|
} |
|
*/ |
|
|
|
void dviRenderer::embedPostScript() |
|
{ |
|
#ifdef DEBUG_DVIRENDERER |
|
kDebug(kvs::dvi) << "dviRenderer::embedPostScript()"; |
|
#endif |
|
|
|
if (!dviFile) |
|
return; |
|
|
|
/* embedPS_progress = new KProgressDialog(parentWidget, |
|
i18n("Embedding PostScript Files"), QString(), true); */ |
|
if (!embedPS_progress) |
|
return; |
|
embedPS_progress->setAllowCancel(false); |
|
embedPS_progress->showCancelButton(false); |
|
embedPS_progress->setMinimumDuration(400); |
|
embedPS_progress->progressBar()->setMaximum(dviFile->numberOfExternalPSFiles); |
|
embedPS_progress->progressBar()->setValue(0); |
|
embedPS_numOfProgressedFiles = 0; |
|
|
|
quint16 currPageSav = current_page; |
|
errorMsg.clear(); |
|
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[int(current_page)]; |
|
end_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(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; |
|
embedPS_progress = 0; |
|
|
|
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.clear(); |
|
} 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(), "embeddingDone");*/ |
|
|
|
// Prescan phase starts here |
|
#ifdef PERFORMANCE_MEASUREMENT |
|
//kDebug(kvs::dvi) << "Time elapsed till prescan phase starts " << performanceTimer.elapsed() << "ms"; |
|
//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[int(current_page)]; |
|
end_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(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 |
|
//kDebug(kvs::dvi) << "Time required for prescan phase: " << preScanTimer.restart() << "ms"; |
|
#endif |
|
current_page = currPageSav; |
|
_isModified = true; |
|
} |
|
|
|
|
|
bool dviRenderer::isValidFile(const QString& filename) const |
|
{ |
|
QFile f(filename); |
|
if (!f.open(QIODevice::ReadOnly)) |
|
return false; |
|
|
|
unsigned char test[4]; |
|
if ( f.read( (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.seek( n-4 ); |
|
|
|
unsigned char trailer[4] = { 0xdf,0xdf,0xdf,0xdf }; |
|
|
|
if ( f.read( (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 |
|
kDebug(kvs::dvi) << "dviRenderer::setFile( fname='" << fname << "' )"; //, ref='" << ref << "', sourceMarker=" << sourceMarker << " )"; |
|
#endif |
|
|
|
//QMutexLocker lock(&mutex); |
|
|
|
QFileInfo fi(fname); |
|
QString filename = fi.absoluteFilePath(); |
|
|
|
// If fname is the empty string, then this means: "close". Delete |
|
// the dvifile and the pixmap. |
|
if (fname.isEmpty()) { |
|
// Delete DVI file |
|
/* if(info) |
|
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>", filename), |
|
i18n("File Error")); |
|
*/ |
|
return false; |
|
} |
|
|
|
QApplication::setOverrideCursor( Qt::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 could not interprete your DVI file. This is " |
|
"most commonly caused by a corrupted file.</qt>"), |
|
dviFile_new->errorMsg, i18n("DVI File Error"));*/ |
|
delete dviFile_new; |
|
return false; |
|
} |
|
|
|
delete dviFile; |
|
dviFile = dviFile_new; |
|
numPages = dviFile->total_pages; |
|
/* if(info) |
|
info->setDVIData(dviFile); */ |
|
_isModified = false; |
|
baseURL = base; |
|
|
|
font_pool.setExtraSearchPath( fi.absolutePath() ); |
|
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.lastIndexOf('/')); |
|
} |
|
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 |
|
//kDebug(kvs::dvi) << "Time elapsed till prescan phase starts " << performanceTimer.elapsed() << "ms"; |
|
//QTime preScanTimer; |
|
//preScanTimer.start(); |
|
#endif |
|
dviFile->numberOfExternalPSFiles = 0; |
|
quint16 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[int(current_page)]; |
|
end_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(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; |
|
|
|
#if 0 |
|
// Generate the list of bookmarks |
|
bookmarks.clear(); |
|
Q3PtrStack<Bookmark> stack; |
|
stack.setAutoDelete (false); |
|
QVector<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(); |
|
#endif |
|
|
|
#ifdef PERFORMANCE_MEASUREMENT |
|
//kDebug(kvs::dvi) << "Time required for prescan phase: " << preScanTimer.restart() << "ms"; |
|
#endif |
|
current_page = currPageSav; |
|
// PRESCAN ENDS HERE |
|
|
|
pageSizes.resize(0); |
|
if (dviFile->suggestedPageSize != 0) { |
|
// Fill the vector pageSizes with total_pages identical entries |
|
pageSizes.fill(*(dviFile->suggestedPageSize), dviFile->total_pages); |
|
} |
|
QApplication::restoreOverrideCursor(); |
|
return true; |
|
} |
|
|
|
Anchor dviRenderer::parseReference(const QString &reference) |
|
{ |
|
QMutexLocker locker(&mutex); |
|
|
|
#ifdef DEBUG_DVIRENDERER |
|
kError(kvs::dvi) << "dviRenderer::parseReference( " << reference << " ) called" << endl; |
|
#endif |
|
|
|
if (dviFile == 0) |
|
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; |
|
|
|
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.indexOf("src:", 0, Qt::CaseInsensitive) == 0) { |
|
|
|
// Extract the file name and the numeral part from the reference string |
|
DVI_SourceFileSplitter splitter(reference, dviFile->filename); |
|
quint32 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>", refLineNumber, refFileName), |
|
i18n("Could Not Find Reference")); |
|
*/ |
|
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 |
|
|
|
QVector<DVI_SourceFileAnchor>::iterator bestMatch = sourceHyperLinkAnchors.end(); |
|
QVector<DVI_SourceFileAnchor>::iterator it; |
|
for( it = sourceHyperLinkAnchors.begin(); it != sourceHyperLinkAnchors.end(); ++it ) |
|
if (refFileName.trimmed() == it->fileName.trimmed() |
|
|| refFileName.trimmed() == it->fileName.trimmed() + ".tex" |
|
) { |
|
anchorForRefFileFound = true; |
|
|
|
if ( (it->line <= refLineNumber) && |
|
( (bestMatch == sourceHyperLinkAnchors.end()) || (it->line > bestMatch->line) ) ) |
|
bestMatch = it; |
|
} |
|
|
|
if (bestMatch != sourceHyperLinkAnchors.end()) |
|
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>", refLineNumber, refFileName), |
|
i18n( "Could Not Find Reference" )); |
|
*/ |
|
} |
|
else |
|
return Anchor(); |
|
return Anchor(); |
|
} |
|
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() ); |
|
} |
|
|
|
|
|
void dviRenderer::handleSRCLink(const QString &linkText, const QPoint& point, DocumentWidget *win) |
|
{ |
|
Q_UNUSED( linkText ); |
|
Q_UNUSED( point ); |
|
Q_UNUSED( win ); |
|
#if 0 |
|
KSharedPtr<DVISourceEditor> editor(new DVISourceEditor(*this, parentWidget, linkText, point, win)); |
|
if (editor->started()) |
|
editor_ = editor; |
|
#endif |
|
} |
|
|
|
|
|
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.indexIn( 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.indexIn( 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.indexIn( pdfstring )) != -1) { |
|
pdfstring = pdfstring.replace(pos, 4, QChar(rx.cap(2).toInt(0,8))); |
|
} |
|
return pdfstring; |
|
} |
|
|
|
|
|
void dviRenderer::exportPDF() |
|
{ |
|
/* |
|
KSharedPtr<DVIExport> exporter(new DVIExportToPDF(*this, parentWidget)); |
|
if (exporter->started()) |
|
all_exports_[exporter.data()] = exporter; |
|
*/ |
|
} |
|
|
|
|
|
void dviRenderer::exportPS(const QString& fname, const QStringList& options, QPrinter* printer) |
|
{ |
|
KSharedPtr<DVIExport> exporter(new DVIExportToPS(*this, parentWidget, fname, options, printer)); |
|
if (exporter->started()) |
|
all_exports_[exporter.data()] = exporter; |
|
} |
|
|
|
/* |
|
void dviRenderer::update_info_dialog(const QString& text, bool clear) |
|
{ |
|
if (clear) |
|
info->clear(text); |
|
else |
|
info->outputReceiver(text); |
|
} |
|
|
|
void dviRenderer::editor_finished(const DVISourceEditor*) |
|
{ |
|
editor_.attach(0); |
|
} |
|
*/ |
|
|
|
void dviRenderer::export_finished(const DVIExport* key) |
|
{ |
|
typedef QMap<const DVIExport*, KSharedPtr<DVIExport> > ExportMap; |
|
ExportMap::iterator it = all_exports_.find(key); |
|
if (it != all_exports_.end()) |
|
all_exports_.remove(key); |
|
} |
|
|
|
void dviRenderer::setEventLoop(QEventLoop *el) |
|
{ |
|
if (el == NULL) |
|
{ |
|
delete m_eventLoop; |
|
m_eventLoop = NULL; |
|
} |
|
else |
|
m_eventLoop = el; |
|
} |
|
|
|
#include "dviRenderer.moc"
|
|
|