summaryrefslogtreecommitdiffstats
path: root/kdvi/psgs.cpp
blob: 9f1f925fa7291efd4f802b2d28d20eb1a862a8e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
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 <tqdir.h>
#include <tqpainter.h>

#include "psgs.h"
#include "dviFile.h"
#include "pageNumber.h"

extern const char psheader[];

//#define DEBUG_PSGS


pageInfo::pageInfo(const TQString& _PostScriptString) {
  PostScriptString = new TQString(_PostScriptString);
  background  = TQt::white;
  permanentBackground = TQt::white;
}


pageInfo::~pageInfo() {
  if (PostScriptString != 0L)
    delete PostScriptString;
}


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

ghostscript_interface::ghostscript_interface() {
  pageList.setAutoDelete(true);

  PostScriptHeaderString = new TQString();

  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 TQString& 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 TQString &_includePath) {
  if (_includePath.isEmpty())
     includePath = "*"; // Allow all files
  else
     includePath = _includePath+"/*";
}


void ghostscript_interface::setBackgroundColor(const PageNumber& page, const TQColor& 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(TQString());
    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

TQColor ghostscript_interface::getBackgroundColor(const PageNumber& page) const {
#ifdef DEBUG_PSGS
  kdDebug(4300) << "ghostscript_interface::getBackgroundColor( " << page << " )" << endl;
#endif

  if (pageList.find(page) == 0) 
    return TQt::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 TQString& 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(TQString(),".ps");

  TQTextStream& 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 "
     << (TQ_INT32)(72*(pixel_page_w/resolution)) << ' '
     << (TQ_INT32)(72*(pixel_page_h/resolution)) << '\n'
     << "%%EndComments\n"
     << "%!\n"
     << psheader
     << "TeXDict begin "
        // HSize in (1/(65781.76*72))inch
     << (TQ_INT32)(72*65781*(pixel_page_w/resolution)) << ' '
        // VSize in (1/(65781.76*72))inch 
     << (TQ_INT32)(72*65781*(pixel_page_h/resolution)) << ' '
        // Magnification
     << (TQ_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 != TQt::white) {
    TQString colorCommand = TQString("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
  TQFile::remove(filename.ascii());
  KProcIO proc;
  TQStringList argus;
  argus << "gs";
  argus << "-dSAFER" << "-dPARANOIDSAFER" << "-dDELAYSAFER" << "-dNOPAUSE" << "-dBATCH";
  argus << TQString("-sDEVICE=%1").tqarg(*gsDevice);
  argus << TQString("-sOutputFile=%1").tqarg(filename);
  argus << TQString("-sExtraIncludePath=%1").tqarg(includePath);
  argus << TQString("-g%1x%2").tqarg(pixel_page_w).tqarg(pixel_page_h); // page size in pixels
  argus << TQString("-r%1").tqarg(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 (TQFile::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.
    TQString GSoutput;
    while(proc.readln(GSoutput) != -1) {
      if (GSoutput.contains("Unknown device")) {
	kdDebug(4300) << TQString("The version of ghostview installed on this computer does not support "
				   "the '%1' ghostview device driver.").tqarg(*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) << TQString("KDVI will now try to use the '%1' device driver.").tqarg(*gsDevice) << endl;
	  gs_generate_graphics_file(page, filename, magnification);
	}
	return;
      }
    }
  }
  emit(setStatusBarText(TQString()));
}


void ghostscript_interface::graphics(const PageNumber& page, double dpi, long magnification, TQPainter* 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, TQPainter *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(TQString(), ".png");
  gfxFile.setAutoDelete(1);
  gfxFile.close(); // we are want the filename, not the file

  gs_generate_graphics_file(page, gfxFile.name(), magnification);

  TQPixmap MemoryCopy(gfxFile.name());
  paint->drawPixmap(0, 0, MemoryCopy);
  return;
}


TQString ghostscript_interface::locateEPSfile(const TQString &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()) {
    TQString path = base.path();       // -> "/bar/foo.dvi"
    TQFileInfo fi1(path);
    TQFileInfo fi2(fi1.dir(),filename);
    if (fi2.exists())
      return fi2.absFilePath();
  }
  
  // Otherwise, use kpsewhich to find the eps file.
  TQString EPSfilename;
  KProcIO proc;
  proc << "kpsewhich" << filename;
  proc.start(KProcess::Block);
  proc.readln(EPSfilename);

  return EPSfilename.stripWhiteSpace();
}

#include "psgs.moc"