~ubuntu-branches/ubuntu/warty/kdebase/warty

« back to all changes in this revision

Viewing changes to kioslave/thumbnail/gscreator.cpp

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-09-16 04:51:45 UTC
  • Revision ID: james.westby@ubuntu.com-20040916045145-9vr63kith3k1cpza
Tags: upstream-3.2.2
ImportĀ upstreamĀ versionĀ 3.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  This file is part of the KDE libraries
 
2
    Copyright (C) 2001 Malte Starostik <malte@kde.org>
 
3
 
 
4
    This library is free software; you can redistribute it and/or
 
5
    modify it under the terms of the GNU Library General Public
 
6
    License as published by the Free Software Foundation; either
 
7
    version 2 of the License, or (at your option) any later version.
 
8
 
 
9
    This library is distributed in the hope that it will be useful,
 
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
    Library General Public License for more details.
 
13
 
 
14
    You should have received a copy of the GNU Library General Public License
 
15
    along with this library; see the file COPYING.LIB.  If not, write to
 
16
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
    Boston, MA 02111-1307, USA.
 
18
*/
 
19
 
 
20
// $Id: gscreator.cpp,v 1.19.2.1 2004/03/19 15:10:56 waba Exp $
 
21
 
 
22
 
 
23
/*  This function gets a path of a DVI, EPS, PS or PDF file and
 
24
    produces a PNG-Thumbnail which is stored as a QImage
 
25
 
 
26
    The program works as follows
 
27
 
 
28
    1. Test if file is a DVI file
 
29
 
 
30
    2. Create a child process (1), in which the 
 
31
       file is to be changed into a PNG
 
32
       
 
33
    3. Child-process (1) :
 
34
 
 
35
    4. If file is DVI continue with 6
 
36
    
 
37
    5. If file is no DVI continue with 9
 
38
 
 
39
    6. Create another child process (2), in which the DVI is
 
40
       turned into PS using dvips
 
41
 
 
42
    7. Parent process (2) :
 
43
       Turn the recently created PS file into a PNG file using gs
 
44
       
 
45
    8. continue with 10
 
46
 
 
47
    9. Turn the PS,PDF or EPS file into a PNG file using gs
 
48
 
 
49
    10. Parent process (1)
 
50
        store data in a QImage
 
51
*/
 
52
 
 
53
#ifdef HAVE_CONFIG_H
 
54
#include <config.h>
 
55
#endif
 
56
 
 
57
 
 
58
#include <assert.h>
 
59
#include <stdlib.h>
 
60
#include <unistd.h>
 
61
#include <signal.h>
 
62
#ifdef HAVE_SYS_SELECT_H
 
63
#include <sys/select.h>
 
64
#endif
 
65
#include <sys/time.h>
 
66
#include <sys/wait.h>
 
67
#include <fcntl.h>
 
68
#include <errno.h>
 
69
 
 
70
#include <qfile.h>
 
71
#include <qimage.h>
 
72
 
 
73
 
 
74
#include "gscreator.h"
 
75
 
 
76
 
 
77
 
 
78
extern "C"
 
79
{
 
80
    ThumbCreator *new_creator()
 
81
    {
 
82
        return new GSCreator;
 
83
    }
 
84
}
 
85
 
 
86
// This PS snippet will be prepended to the actual file so that only
 
87
// the first page is output.
 
88
static const char *prolog =
 
89
    "%!PS-Adobe-3.0\n"
 
90
    "/.showpage.orig /showpage load def\n"
 
91
    "/.showpage.firstonly {\n"
 
92
    "    .showpage.orig\n"
 
93
    "    quit\n"
 
94
    "} def\n"
 
95
    "/showpage { .showpage.firstonly } def\n";
 
96
 
 
97
static const char * gsargs[] = {
 
98
    "gs",
 
99
    "-sDEVICE=png16m",
 
100
    "-sOutputFile=-",
 
101
    "-dSAFER",
 
102
    "-dPARANOIDSAFER",
 
103
    "-dNOPAUSE",
 
104
    "-dFirstPage=1",
 
105
    "-dLastPage=1",
 
106
    "-q",
 
107
    "-",
 
108
    0, // file name
 
109
        "-c",
 
110
        "showpage",
 
111
        "-c",
 
112
        "quit",
 
113
    0
 
114
};
 
115
 
 
116
static const char *dvipsargs[] = {
 
117
    "dvips",
 
118
    "-n", 
 
119
    "1",
 
120
    "-q",
 
121
    "-o",
 
122
    "-",
 
123
    0, // file name
 
124
    0
 
125
};
 
126
 
 
127
static bool correctDVI(const QString& filename);
 
128
 
 
129
 
 
130
namespace {
 
131
        bool got_sig_term = false;
 
132
        void handle_sigterm( int ) {
 
133
                got_sig_term = true;
 
134
        }
 
135
}
 
136
 
 
137
 
 
138
bool GSCreator::create(const QString &path, int, int, QImage &img)
 
139
{
 
140
// The code in the loop (when testing whether got_sig_term got set)
 
141
// should read some variation of:
 
142
//              parentJob()->wasKilled()
 
143
//
 
144
// Unfortunatelly, that's currently impossible without breaking BIC.
 
145
// So we need to catch the signal ourselves.
 
146
// Otherwise, on certain funny PS files (for example
 
147
// http://www.tjhsst.edu/~Eedanaher/pslife/life.ps )
 
148
// gs would run forever after we were dead.
 
149
// #### Reconsider for KDE 4 ###
 
150
// (24/12/03 - luis_pedro)
 
151
//
 
152
  typedef void ( *sighandler_t )( int );
 
153
  // according to linux's "man signal" the above typedef is a gnu extension
 
154
  sighandler_t oldhandler = signal( SIGTERM, handle_sigterm );
 
155
  
 
156
  int input[2];
 
157
  int output[2];
 
158
  int dvipipe[2]; 
 
159
 
 
160
  QByteArray data(1024);
 
161
 
 
162
  bool ok = false;
 
163
 
 
164
  // Test if file is DVI
 
165
  bool no_dvi =!correctDVI(path);
 
166
 
 
167
 
 
168
  if (pipe(input) == -1) {
 
169
    return false;
 
170
  }
 
171
  if (pipe(output) == -1) {
 
172
    close(input[0]);
 
173
    close(input[1]);
 
174
    return false;
 
175
  }
 
176
  
 
177
  pid_t pid = fork(); 
 
178
  if (pid == 0) {
 
179
    // Child process (1)
 
180
 
 
181
    //    close(STDERR_FILENO);
 
182
 
 
183
    // find first zero entry in gsargs and put the filename 
 
184
    // or - (stdin) there, if DVI 
 
185
    const char **arg = gsargs;
 
186
    QCString fname = QFile::encodeName( path );
 
187
    while (*arg)
 
188
      ++arg;
 
189
    if( no_dvi )
 
190
      *arg = fname.data();
 
191
    else if( !no_dvi ) 
 
192
      *arg = "-";
 
193
 
 
194
    // find first zero entry in dvipsargs and put the filename there    
 
195
    arg = dvipsargs;
 
196
    while (*arg)
 
197
      ++arg;
 
198
    *arg = fname.data();
 
199
    
 
200
    if( !no_dvi ){
 
201
      pipe(dvipipe);
 
202
      pid_t pid_two = fork();
 
203
      if( pid_two == 0 ){
 
204
        // Child process (2), reopen stdout on the pipe "dvipipe" and exec dvips
 
205
        
 
206
        close(input[0]);            
 
207
        close(input[1]);
 
208
        close(output[0]);
 
209
        close(output[1]);
 
210
        close(dvipipe[0]);
 
211
        
 
212
        dup2( dvipipe[1], STDOUT_FILENO);
 
213
        
 
214
        execvp(dvipsargs[0], const_cast<char *const *>(dvipsargs));
 
215
        exit(1);
 
216
      } 
 
217
      else if(pid_two != -1){
 
218
        close(input[1]);
 
219
        close(output[0]);
 
220
        close(dvipipe[1]);
 
221
 
 
222
        dup2( dvipipe[0], STDIN_FILENO);
 
223
        dup2( output[1], STDOUT_FILENO);
 
224
 
 
225
        execvp(gsargs[0], const_cast<char *const *>(gsargs));       
 
226
        exit(1);
 
227
      }
 
228
      else{
 
229
        // fork() (2) failed, close these
 
230
        close(dvipipe[0]);
 
231
        close(dvipipe[1]);
 
232
      }
 
233
              
 
234
    } 
 
235
    else if( no_dvi ){
 
236
      // Reopen stdin/stdout on the pipes and exec gs
 
237
      close(input[1]);
 
238
      close(output[0]);
 
239
 
 
240
      dup2(input[0], STDIN_FILENO);
 
241
      dup2(output[1], STDOUT_FILENO);     
 
242
 
 
243
      execvp(gsargs[0], const_cast<char *const *>(gsargs));
 
244
      exit(1);
 
245
    }
 
246
  } 
 
247
  else if (pid != -1) {
 
248
    // Parent process, write first-page-only-hack (the hack is not
 
249
    // used if DVI) and read the png output
 
250
    close(input[0]);
 
251
    close(output[1]);
 
252
    int count = write(input[1], prolog, strlen(prolog));
 
253
    close(input[1]);
 
254
    if (count == static_cast<int>(strlen(prolog))) {
 
255
      int offset = 0;
 
256
        while (!ok) {
 
257
          fd_set fds;
 
258
          FD_ZERO(&fds);
 
259
          FD_SET(output[0], &fds);
 
260
          struct timeval tv;
 
261
          tv.tv_sec = 20;
 
262
          tv.tv_usec = 0;
 
263
 
 
264
          got_sig_term = false;
 
265
          if (select(output[0] + 1, &fds, 0, 0, &tv) <= 0) {
 
266
            if ( ( errno == EINTR || errno == EAGAIN ) && !got_sig_term ) continue;
 
267
            break; // error, timeout or master wants us to quit (SIGTERM)
 
268
          }
 
269
          if (FD_ISSET(output[0], &fds)) {
 
270
            count = read(output[0], data.data() + offset, 1024);
 
271
            if (count == -1)
 
272
              break;
 
273
            else
 
274
              if (count) // prepare for next block
 
275
                {
 
276
                  offset += count;
 
277
                  data.resize(offset + 1024);
 
278
                }
 
279
              else // got all data
 
280
                {
 
281
                  data.resize(offset);
 
282
                  ok = true;
 
283
                }
 
284
          }
 
285
        }
 
286
    }
 
287
    if (!ok) // error or timeout, gs probably didn't exit yet
 
288
    {
 
289
      kill(pid, SIGTERM);
 
290
    }
 
291
 
 
292
    int status = 0;
 
293
    if (waitpid(pid, &status, 0) != pid || (status != 0  && status != 256) )
 
294
      ok = false;
 
295
  } 
 
296
  else {
 
297
    // fork() (1) failed, close these
 
298
    close(input[0]);
 
299
    close(input[1]);
 
300
    close(output[1]);
 
301
  }
 
302
  close(output[0]);
 
303
  
 
304
  int l = img.loadFromData( data );
 
305
 
 
306
  if ( got_sig_term && 
 
307
        oldhandler != SIG_ERR &&
 
308
        oldhandler != SIG_DFL &&
 
309
        oldhandler != SIG_IGN ) {
 
310
          oldhandler( SIGTERM ); // propagate the signal. Other things might rely on it
 
311
  }
 
312
  if ( oldhandler != SIG_ERR ) signal( SIGTERM, oldhandler );
 
313
  
 
314
  return ok && l;
 
315
}
 
316
 
 
317
ThumbCreator::Flags GSCreator::flags() const
 
318
{
 
319
    return static_cast<Flags>(DrawFrame);
 
320
}
 
321
 
 
322
 
 
323
// Quick function to check if the filename corresponds to a valid DVI
 
324
// file. Returns true if <filename> is a DVI file, false otherwise.
 
325
 
 
326
static bool correctDVI(const QString& filename)
 
327
{
 
328
  QFile f(filename);
 
329
  if (!f.open(IO_ReadOnly))
 
330
    return FALSE;
 
331
 
 
332
  unsigned char test[4];
 
333
  if ( f.readBlock( (char *)test,2)<2 || test[0] != 247 || test[1] != 2  )
 
334
    return FALSE;
 
335
 
 
336
  int n = f.size();
 
337
  if ( n < 134 ) // Too short for a dvi file
 
338
    return FALSE;
 
339
  f.at( n-4 );
 
340
 
 
341
  unsigned char trailer[4] = { 0xdf,0xdf,0xdf,0xdf };
 
342
 
 
343
  if ( f.readBlock( (char *)test, 4 )<4 || strncmp( (char *)test, (char*) trailer, 4 ) )
 
344
    return FALSE;
 
345
  // We suppose now that the dvi file is complete and OK
 
346
  return TRUE;
 
347
}