psgs.cpp 11.1 KB
Newer Older
1
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; c-brace-offset: 0; -*-
2 3 4 5 6 7 8 9
//
// ghostscript_interface
//
// Part of KDVI - A framework for multipage text/gfx viewers
//
// (C) 2004 Stefan Kebekus
// Distributed under the GPL

10
#include <config.h>
Stephan Kulow's avatar
Stephan Kulow committed
11

Angus Leeming's avatar
Angus Leeming committed
12
#include "psgs.h"
13
#include "psheader.cpp"
Angus Leeming's avatar
Angus Leeming committed
14
#include "dviFile.h"
15
#include "kvs_debug.h"
Angus Leeming's avatar
Angus Leeming committed
16 17
#include "pageNumber.h"

Stefan Kebekus's avatar
Stefan Kebekus committed
18
#include <klocale.h>
19
#include <kmessagebox.h>
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
20
#include <kprocess.h>
21
#include <ktemporaryfile.h>
Wilfried Huss's avatar
Wilfried Huss committed
22
#include <kurl.h>
Angus Leeming's avatar
Angus Leeming committed
23 24 25

#include <QDir>
#include <QPainter>
Laurent Montel's avatar
Laurent Montel committed
26
#include <QPixmap>
Wilfried Huss's avatar
Wilfried Huss committed
27
#include <QTextStream>
28
#include <QTimer>
Stephan Kulow's avatar
Stephan Kulow committed
29

Stefan Kebekus's avatar
Stefan Kebekus committed
30
//#define DEBUG_PSGS
31

32
//extern char psheader[];
33

Wilfried Huss's avatar
Wilfried Huss committed
34
pageInfo::pageInfo(const QString& _PostScriptString) {
Waldo Bastian's avatar
Waldo Bastian committed
35
  PostScriptString = new QString(_PostScriptString);
36 37
  background  = Qt::white;
  permanentBackground = Qt::white;
38 39
}

40

41 42 43 44 45
pageInfo::~pageInfo() {
  if (PostScriptString != 0L)
    delete PostScriptString;
}

46 47 48

// ======================================================

49
ghostscript_interface::ghostscript_interface() {
Wilfried Huss's avatar
Wilfried Huss committed
50
  pageList.setAutoDelete(true);
51 52

  PostScriptHeaderString = new QString();
53

Wilfried Huss's avatar
Wilfried Huss committed
54
  knownDevices.append("png16m");
55 56 57 58
  knownDevices.append("jpeg");
  knownDevices.append("pnn");
  knownDevices.append("pnnraw");
  gsDevice = knownDevices.begin();
59 60 61
}

ghostscript_interface::~ghostscript_interface() {
62 63
  if (PostScriptHeaderString != 0L)
    delete PostScriptHeaderString;
64 65
}

66

Wilfried Huss's avatar
Wilfried Huss committed
67
void ghostscript_interface::setPostScript(const PageNumber& page, const QString& PostScript) {
Stefan Kebekus's avatar
Stefan Kebekus committed
68
#ifdef DEBUG_PSGS
69
  kDebug(kvs::dvi) << "ghostscript_interface::setPostScript( " << page << ", ... )";
Stefan Kebekus's avatar
Stefan Kebekus committed
70
#endif
Stefan Kebekus's avatar
Stefan Kebekus committed
71

72
  if (pageList.find(page) == 0) {
73 74
    pageInfo *info = new pageInfo(PostScript);
    // Check if dict is big enough
75 76 77
    if (pageList.count() > pageList.size() -2)
      pageList.resize(pageList.size()*2);
    pageList.insert(page, info);
Angus Leeming's avatar
Angus Leeming committed
78
  } else
79
    *(pageList.find(page)->PostScriptString) = PostScript;
80 81
}

82

83 84 85 86 87 88
void ghostscript_interface::setIncludePath(const QString &_includePath) {
  if (_includePath.isEmpty())
     includePath = "*"; // Allow all files
  else
     includePath = _includePath+"/*";
}
89

90

Wilfried Huss's avatar
Wilfried Huss committed
91
void ghostscript_interface::setBackgroundColor(const PageNumber& page, const QColor& background_color, bool permanent) {
92
#ifdef DEBUG_PSGS
93
  kDebug(kvs::dvi) << "ghostscript_interface::setBackgroundColor( " << page << ", " << background_color << " )";
94 95
#endif

96
  if (pageList.find(page) == 0) {
97
    pageInfo *info = new pageInfo(QString::null);	//krazy:exclude=nullstrassign for old broken gcc
98
    info->background = background_color;
99 100
    if (permanent)
      info->permanentBackground = background_color;
101
    // Check if dict is big enough
102 103 104
    if (pageList.count() > pageList.size() -2)
      pageList.resize(pageList.size()*2);
    pageList.insert(page, info);
105
  } else {
106
    pageList.find(page)->background = background_color;
107 108 109
    if (permanent)
      pageList.find(page)->permanentBackground = background_color;
  }
110 111
}

Wilfried Huss's avatar
Wilfried Huss committed
112
void ghostscript_interface::restoreBackgroundColor(const PageNumber& page)
113 114
{
#ifdef DEBUG_PSGS
115
  kDebug(kvs::dvi) << "ghostscript_interface::restoreBackgroundColor( " << page << " )";
116 117 118 119 120 121 122
#endif
  if (pageList.find(page) == 0)
    return;

  pageInfo *info = pageList.find(page);
  info->background = info->permanentBackground;
}
123

124 125 126
// Returns the background color for a certain page. This color is
// always guaranteed to be valid

Wilfried Huss's avatar
Wilfried Huss committed
127
QColor ghostscript_interface::getBackgroundColor(const PageNumber& page) const {
128
#ifdef DEBUG_PSGS
129
  kDebug(kvs::dvi) << "ghostscript_interface::getBackgroundColor( " << page << " )";
130 131
#endif

Angus Leeming's avatar
Angus Leeming committed
132
  if (pageList.find(page) == 0)
133
    return Qt::white;
Angus Leeming's avatar
Angus Leeming committed
134
  else
135
    return pageList.find(page)->background;
136 137 138
}


Wilfried Huss's avatar
Wilfried Huss committed
139
void ghostscript_interface::clear() {
140
  PostScriptHeaderString->truncate(0);
Angus Leeming's avatar
Angus Leeming committed
141

142
  // Deletes all items, removes temporary files, etc.
143
  pageList.clear();
144 145
}

146

Wilfried Huss's avatar
Wilfried Huss committed
147
void ghostscript_interface::gs_generate_graphics_file(const PageNumber& page, const QString& filename, long magnification) {
148
#ifdef DEBUG_PSGS
149
  kDebug(kvs::dvi) << "ghostscript_interface::gs_generate_graphics_file( " << page << ", " << filename << " )";
150
#endif
151 152

  if (knownDevices.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
153
    kError(kvs::dvi) << "No known devices found" << endl;
154
    return;
155
  }
156

157
  pageInfo *info = pageList.find(page);
158 159

  // Generate a PNG-file
160
  // Step 1: Write the PostScriptString to a File
161 162 163
  KTemporaryFile PSfile;
  PSfile.setAutoRemove(false);
  PSfile.setSuffix(".ps");
Angus Leeming's avatar
Angus Leeming committed
164 165
  PSfile.open();
  const QString PSfileName = PSfile.fileName();
Wilfried Huss's avatar
Wilfried Huss committed
166

Angus Leeming's avatar
Angus Leeming committed
167
  QTextStream os(&PSfile);
Wilfried Huss's avatar
Wilfried Huss committed
168 169 170 171 172 173 174
  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 "
Laurent Montel's avatar
Laurent Montel committed
175 176
     << (qint32)(72*(pixel_page_w/resolution)) << ' '
     << (qint32)(72*(pixel_page_h/resolution)) << '\n'
Wilfried Huss's avatar
Wilfried Huss committed
177 178
     << "%%EndComments\n"
     << "%!\n"
179
     << psheader
Wilfried Huss's avatar
Wilfried Huss committed
180 181
     << "TeXDict begin "
        // HSize in (1/(65781.76*72))inch
Laurent Montel's avatar
Laurent Montel committed
182
     << (qint32)(72*65781*(pixel_page_w/resolution)) << ' '
Angus Leeming's avatar
Angus Leeming committed
183
        // VSize in (1/(65781.76*72))inch
Laurent Montel's avatar
Laurent Montel committed
184
     << (qint32)(72*65781*(pixel_page_h/resolution)) << ' '
Wilfried Huss's avatar
Wilfried Huss committed
185
        // Magnification
Laurent Montel's avatar
Laurent Montel committed
186
     << (qint32)(magnification)
Wilfried Huss's avatar
Wilfried Huss committed
187 188 189 190 191 192 193 194 195
        // dpi and vdpi
     << " 300 300"
        // Name
     << " (test.dvi)"
     << " @start end\n"
     << "TeXDict begin\n"
        // Start page
     << "1 0 bop 0 0 a \n";

Wilfried Huss's avatar
Wilfried Huss committed
196
  if (!PostScriptHeaderString->toLatin1().isNull())
Wilfried Huss's avatar
Wilfried Huss committed
197
    os << PostScriptHeaderString->toLatin1();
198 199 200

  if (info->background != Qt::white) {
    QString colorCommand = QString("gsave %1 %2 %3 setrgbcolor clippath fill grestore\n").
201 202 203
      arg(info->background.red()/255.0).
      arg(info->background.green()/255.0).
      arg(info->background.blue()/255.0);
Wilfried Huss's avatar
Wilfried Huss committed
204
    os << colorCommand.toLatin1();
205 206
  }

Wilfried Huss's avatar
Wilfried Huss committed
207
  if (!info->PostScriptString->toLatin1().isNull())
Wilfried Huss's avatar
Wilfried Huss committed
208
    os << info->PostScriptString->toLatin1();
Wilfried Huss's avatar
Wilfried Huss committed
209 210 211 212

  os << "end\n"
     << "showpage \n";

213
  PSfile.close();
214 215

  // Step 2: Call GS with the File
Wilfried Huss's avatar
Wilfried Huss committed
216
  QFile::remove(filename.toAscii());
Christian Ehrlicher's avatar
Christian Ehrlicher committed
217
  KProcess proc;
218 219 220 221 222 223 224 225
  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
Wilfried Huss's avatar
Wilfried Huss committed
226
  argus << "-dTextAlphaBits=4 -dGraphicsAlphaBits=2"; // Antialiasing
227
  argus << "-c" << "<< /PermitFileReading [ ExtraIncludePath ] /PermitFileWriting [] /PermitFileControl [] >> setuserparams .locksafe";
Wilfried Huss's avatar
Wilfried Huss committed
228
  argus << "-f" << PSfileName;
229

230
#ifdef DEBUG_PSGS
231
  kDebug(kvs::dvi) << argus.join(" ");
232
#endif
233
 
234
  proc << argus;
235 236 237
  int res = proc.execute();
  
  if ( res ) {
238
    // Starting ghostscript did not work. 
239
    // TODO: Issue error message, switch PS support off.
Wilfried Huss's avatar
Wilfried Huss committed
240
    kError(kvs::dvi) << "ghostview could not be started" << endl;
241 242
  } 

Angus Leeming's avatar
Angus Leeming committed
243
  PSfile.remove();
244

245 246
 // Check if gs has indeed produced a file.
  if (QFile::exists(filename) == false) {
Stephan Kulow's avatar
Stephan Kulow committed
247
    kError(kvs::dvi) << "GS did not produce output." << endl;
248

249
    // No. Check is the reason is that the device is not compiled into
250
    // ghostscript. If so, try again with another device.
251
    QString GSoutput;
Christian Ehrlicher's avatar
Christian Ehrlicher committed
252 253 254
    proc.setReadChannel(QProcess::StandardOutput);
    while(proc.canReadLine()) {
      GSoutput = QString::fromLocal8Bit(proc.readLine());
255
      if (GSoutput.contains("Unknown device")) {
Stephan Kulow's avatar
Stephan Kulow committed
256
	kDebug(kvs::dvi) << QString("The version of ghostview installed on this computer does not support "
257
                                     "the '%1' ghostview device driver.").arg(*gsDevice) << endl;
Stephan Kulow's avatar
Stephan Kulow committed
258
	knownDevices.erase(gsDevice);
259 260 261 262 263
	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 "
264 265 266
					  "any of the Ghostview device drivers that are known to okular. PostScript "
					  "support has therefore been turned off in okular.</qt>"), 
				     i18n("<qt><p>The Ghostview program, which okular uses internally to display the "
267 268 269 270 271 272
					  "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 "
273
					  "<strong>any</strong> of the device drivers that are known to okular.</p>"
274 275 276 277 278
					  "<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 "
279 280
					  "Ghostview. Among others, okular can use the 'png256', 'jpeg' and 'pnm' "
					  "drivers. Note that okular needs to be restarted to re-enable PostScript support."
281 282
					  "</p></qt>"));
	else {
283
	  kDebug(kvs::dvi) << QString("Okular will now try to use the '%1' device driver.").arg(*gsDevice);
284 285 286
	  gs_generate_graphics_file(page, filename, magnification);
	}
	return;
287 288 289
      }
    }
  }
290 291 292
}


Wilfried Huss's avatar
Wilfried Huss committed
293
void ghostscript_interface::graphics(const PageNumber& page, double dpi, long magnification, QPainter* paint) {
294
#ifdef DEBUG_PSGS
295
  kDebug(kvs::dvi) << "ghostscript_interface::graphics( " << page << ", " << dpi << ", ... ) called.";
296
#endif
297

Stefan Kebekus's avatar
Stefan Kebekus committed
298
  if (paint == 0) {
Laurent Montel's avatar
Laurent Montel committed
299
    kError(kvs::dvi) << "ghostscript_interface::graphics(PageNumber page, double dpi, long magnification, QPainter *paint) called with paint == 0" << endl;
Stefan Kebekus's avatar
Stefan Kebekus committed
300 301 302
    return;
  }

303
  resolution   = dpi;
304

Stefan Kebekus's avatar
Stefan Kebekus committed
305 306
  pixel_page_w = paint->viewport().width();
  pixel_page_h = paint->viewport().height();
307

308
  pageInfo *info = pageList.find(page);
309 310

  // No PostScript? Then return immediately.
311
  if ((info == 0) || (info->PostScriptString->isEmpty())) {
312
#ifdef DEBUG_PSGS
313
    kDebug(kvs::dvi) << "No PostScript found. Not drawing anything.";
314 315
#endif
    return;
316
  }
Wilfried Huss's avatar
Wilfried Huss committed
317

Angus Leeming's avatar
Angus Leeming committed
318 319 320 321 322
  QTemporaryFile gfxFile;
  gfxFile.open();
  const QString gfxFileName = gfxFile.fileName();
  // We are want the filename, not the file.
  gfxFile.close();
Wilfried Huss's avatar
Wilfried Huss committed
323

Angus Leeming's avatar
Angus Leeming committed
324
  gs_generate_graphics_file(page, gfxFileName, magnification);
325 326 327
  
  QImage MemoryCopy(gfxFileName);
  paint->drawImage(0, 0, MemoryCopy);
328
  return;
Stephan Kulow's avatar
Stephan Kulow committed
329
}
Stefan Kebekus's avatar
Stefan Kebekus committed
330 331


Laurent Montel's avatar
Laurent Montel committed
332
QString ghostscript_interface::locateEPSfile(const QString &filename, const KUrl &base)
Stefan Kebekus's avatar
Stefan Kebekus committed
333
{
Wilfried Huss's avatar
Wilfried Huss committed
334 335 336 337 338 339 340
  // 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())
Laurent Montel's avatar
Laurent Montel committed
341
      return fi2.absoluteFilePath();
Stefan Kebekus's avatar
Stefan Kebekus committed
342
  }
Angus Leeming's avatar
Angus Leeming committed
343

Wilfried Huss's avatar
Wilfried Huss committed
344
  // Otherwise, use kpsewhich to find the eps file.
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
345
  KProcess proc;
346
  proc << "kpsewhich" << filename;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
347 348
  proc.execute();
  return QString::fromLocal8Bit(proc.readLine().trimmed());
Stefan Kebekus's avatar
Stefan Kebekus committed
349 350
}

David Faure's avatar
David Faure committed
351
#include "psgs.moc"