1 // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; c-brace-offset: 0; -*-
3 // ghostscript_interface
5 // Part of KDVI - A framework for multipage text/gfx viewers
7 // (C) 2004 Stefan Kebekus
8 // Distributed under the GPL
13 #include "psheader.cpp"
15 #include "kvs_debug.h"
16 #include "pageNumber.h"
19 #include <kmessagebox.h>
21 #include <ktemporaryfile.h>
27 #include <QTextStream>
32 //extern char psheader[];
34 pageInfo::pageInfo(const QString
& _PostScriptString
) {
35 PostScriptString
= new QString(_PostScriptString
);
36 background
= Qt::white
;
37 permanentBackground
= Qt::white
;
41 pageInfo::~pageInfo() {
42 if (PostScriptString
!= 0L)
43 delete PostScriptString
;
47 // ======================================================
49 ghostscript_interface::ghostscript_interface() {
50 pageList
.setAutoDelete(true);
52 PostScriptHeaderString
= new QString();
54 knownDevices
.append("png16m");
55 knownDevices
.append("jpeg");
56 knownDevices
.append("pnn");
57 knownDevices
.append("pnnraw");
58 gsDevice
= knownDevices
.begin();
61 ghostscript_interface::~ghostscript_interface() {
62 if (PostScriptHeaderString
!= 0L)
63 delete PostScriptHeaderString
;
67 void ghostscript_interface::setPostScript(const PageNumber
& page
, const QString
& PostScript
) {
69 kDebug(kvs::dvi
) << "ghostscript_interface::setPostScript( " << page
<< ", ... )";
72 if (pageList
.find(page
) == 0) {
73 pageInfo
*info
= new pageInfo(PostScript
);
74 // Check if dict is big enough
75 if (pageList
.count() > pageList
.size() -2)
76 pageList
.resize(pageList
.size()*2);
77 pageList
.insert(page
, info
);
79 *(pageList
.find(page
)->PostScriptString
) = PostScript
;
83 void ghostscript_interface::setIncludePath(const QString
&_includePath
) {
84 if (_includePath
.isEmpty())
85 includePath
= "*"; // Allow all files
87 includePath
= _includePath
+"/*";
91 void ghostscript_interface::setBackgroundColor(const PageNumber
& page
, const QColor
& background_color
, bool permanent
) {
93 kDebug(kvs::dvi
) << "ghostscript_interface::setBackgroundColor( " << page
<< ", " << background_color
<< " )";
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
;
100 info
->permanentBackground
= background_color
;
101 // Check if dict is big enough
102 if (pageList
.count() > pageList
.size() -2)
103 pageList
.resize(pageList
.size()*2);
104 pageList
.insert(page
, info
);
106 pageList
.find(page
)->background
= background_color
;
108 pageList
.find(page
)->permanentBackground
= background_color
;
112 void ghostscript_interface::restoreBackgroundColor(const PageNumber
& page
)
115 kDebug(kvs::dvi
) << "ghostscript_interface::restoreBackgroundColor( " << page
<< " )";
117 if (pageList
.find(page
) == 0)
120 pageInfo
*info
= pageList
.find(page
);
121 info
->background
= info
->permanentBackground
;
124 // Returns the background color for a certain page. This color is
125 // always guaranteed to be valid
127 QColor
ghostscript_interface::getBackgroundColor(const PageNumber
& page
) const {
129 kDebug(kvs::dvi
) << "ghostscript_interface::getBackgroundColor( " << page
<< " )";
132 if (pageList
.find(page
) == 0)
135 return pageList
.find(page
)->background
;
139 void ghostscript_interface::clear() {
140 PostScriptHeaderString
->truncate(0);
142 // Deletes all items, removes temporary files, etc.
147 void ghostscript_interface::gs_generate_graphics_file(const PageNumber
& page
, const QString
& filename
, long magnification
) {
149 kDebug(kvs::dvi
) << "ghostscript_interface::gs_generate_graphics_file( " << page
<< ", " << filename
<< " )";
152 if (knownDevices
.isEmpty()) {
153 kError(kvs::dvi
) << "No known devices found" << endl
;
157 pageInfo
*info
= pageList
.find(page
);
159 // Generate a PNG-file
160 // Step 1: Write the PostScriptString to a File
161 KTemporaryFile PSfile
;
162 PSfile
.setAutoRemove(false);
163 PSfile
.setSuffix(".ps");
165 const QString PSfileName
= PSfile
.fileName();
167 QTextStream
os(&PSfile
);
168 os
<< "%!PS-Adobe-2.0\n"
169 << "%%Creator: kdvi\n"
170 << "%%Title: KDVI temporary PostScript\n"
172 << "%%PageOrder: Ascend\n"
173 // HSize and VSize in 1/72 inch
174 << "%%BoundingBox: 0 0 "
175 << (qint32
)(72*(pixel_page_w
/resolution
)) << ' '
176 << (qint32
)(72*(pixel_page_h
/resolution
)) << '\n'
181 // HSize in (1/(65781.76*72))inch
182 << (qint32
)(72*65781*(pixel_page_w
/resolution
)) << ' '
183 // VSize in (1/(65781.76*72))inch
184 << (qint32
)(72*65781*(pixel_page_h
/resolution
)) << ' '
186 << (qint32
)(magnification
)
194 << "1 0 bop 0 0 a \n";
196 if (!PostScriptHeaderString
->toLatin1().isNull())
197 os
<< PostScriptHeaderString
->toLatin1();
199 if (info
->background
!= Qt::white
) {
200 QString colorCommand
= QString("gsave %1 %2 %3 setrgbcolor clippath fill grestore\n").
201 arg(info
->background
.red()/255.0).
202 arg(info
->background
.green()/255.0).
203 arg(info
->background
.blue()/255.0);
204 os
<< colorCommand
.toLatin1();
207 if (!info
->PostScriptString
->toLatin1().isNull())
208 os
<< info
->PostScriptString
->toLatin1();
215 // Step 2: Call GS with the File
216 QFile::remove(filename
.toAscii());
220 argus
<< "-dSAFER" << "-dPARANOIDSAFER" << "-dDELAYSAFER" << "-dNOPAUSE" << "-dBATCH";
221 argus
<< QString("-sDEVICE=%1").arg(*gsDevice
);
222 argus
<< QString("-sOutputFile=%1").arg(filename
);
223 argus
<< QString("-sExtraIncludePath=%1").arg(includePath
);
224 argus
<< QString("-g%1x%2").arg(pixel_page_w
).arg(pixel_page_h
); // page size in pixels
225 argus
<< QString("-r%1").arg(resolution
); // resolution in dpi
226 argus
<< "-dTextAlphaBits=4 -dGraphicsAlphaBits=2"; // Antialiasing
227 argus
<< "-c" << "<< /PermitFileReading [ ExtraIncludePath ] /PermitFileWriting [] /PermitFileControl [] >> setuserparams .locksafe";
228 argus
<< "-f" << PSfileName
;
231 kDebug(kvs::dvi
) << argus
.join(" ");
235 int res
= proc
.execute();
238 // Starting ghostscript did not work.
239 // TODO: Issue error message, switch PS support off.
240 kError(kvs::dvi
) << "ghostview could not be started" << endl
;
245 // Check if gs has indeed produced a file.
246 if (QFile::exists(filename
) == false) {
247 kError(kvs::dvi
) << "GS did not produce output." << endl
;
249 // No. Check is the reason is that the device is not compiled into
250 // ghostscript. If so, try again with another device.
252 proc
.setReadChannel(QProcess::StandardOutput
);
253 while(proc
.canReadLine()) {
254 GSoutput
= QString::fromLocal8Bit(proc
.readLine());
255 if (GSoutput
.contains("Unknown device")) {
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
;
258 knownDevices
.erase(gsDevice
);
259 gsDevice
= knownDevices
.begin();
260 if (knownDevices
.isEmpty())
261 // TODO: show a requestor of some sort.
262 KMessageBox::detailedError(0,
263 i18n("<qt>The version of Ghostview that is installed on this computer does not contain "
264 "any of the Ghostview device drivers that are known to Okular. PostScript "
265 "support has therefore been turned off in Okular.</qt>"),
266 i18n("<qt><p>The Ghostview program, which Okular uses internally to display the "
267 "PostScript graphics that is included in this DVI file, is generally able to "
268 "write its output in a variety of formats. The sub-programs that Ghostview uses "
269 "for these tasks are called 'device drivers'; there is one device driver for "
270 "each format that Ghostview is able to write. Different versions of Ghostview "
271 "often have different sets of device drivers available. It seems that the "
272 "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 "<p>It seems unlikely that a regular installation of Ghostview would not contain "
275 "these drivers. This error may therefore point to a serious misconfiguration of "
276 "the Ghostview installation on your computer.</p>"
277 "<p>If you want to fix the problems with Ghostview, you can use the command "
278 "<strong>gs --help</strong> to display the list of device drivers contained in "
279 "Ghostview. Among others, Okular can use the 'png256', 'jpeg' and 'pnm' "
280 "drivers. Note that Okular needs to be restarted to re-enable PostScript support."
283 kDebug(kvs::dvi
) << QString("Okular will now try to use the '%1' device driver.").arg(*gsDevice
);
284 gs_generate_graphics_file(page
, filename
, magnification
);
293 void ghostscript_interface::graphics(const PageNumber
& page
, double dpi
, long magnification
, QPainter
* paint
) {
295 kDebug(kvs::dvi
) << "ghostscript_interface::graphics( " << page
<< ", " << dpi
<< ", ... ) called.";
299 kError(kvs::dvi
) << "ghostscript_interface::graphics(PageNumber page, double dpi, long magnification, QPainter *paint) called with paint == 0" << endl
;
305 pixel_page_w
= paint
->viewport().width();
306 pixel_page_h
= paint
->viewport().height();
308 pageInfo
*info
= pageList
.find(page
);
310 // No PostScript? Then return immediately.
311 if ((info
== 0) || (info
->PostScriptString
->isEmpty())) {
313 kDebug(kvs::dvi
) << "No PostScript found. Not drawing anything.";
318 QTemporaryFile gfxFile
;
320 const QString gfxFileName
= gfxFile
.fileName();
321 // We are want the filename, not the file.
324 gs_generate_graphics_file(page
, gfxFileName
, magnification
);
326 QImage
MemoryCopy(gfxFileName
);
327 paint
->drawImage(0, 0, MemoryCopy
);
332 QString
ghostscript_interface::locateEPSfile(const QString
&filename
, const KUrl
&base
)
334 // If the base URL indicates that the DVI file is local, try to find
335 // the graphics file in the directory where the DVI file resides
336 if (base
.isLocalFile()) {
337 QString path
= base
.path(); // -> "/bar/foo.dvi"
339 QFileInfo
fi2(fi1
.dir(),filename
);
341 return fi2
.absoluteFilePath();
344 // Otherwise, use kpsewhich to find the eps file.
346 proc
<< "kpsewhich" << filename
;
348 return QString::fromLocal8Bit(proc
.readLine().trimmed());