summaryrefslogtreecommitdiffstats
path: root/kdvi/dviRenderer_export.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdvi/dviRenderer_export.cpp')
-rw-r--r--kdvi/dviRenderer_export.cpp391
1 files changed, 391 insertions, 0 deletions
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 = "";
+}