Coverage Report

Created: 2026-02-26 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kdegraphics-thumbnailers/ps/gscreator.cpp
Line
Count
Source
1
/*  This file is part of the KDE libraries
2
    SPDX-FileCopyrightText: 2001 Malte Starostik <malte@kde.org>
3
4
    Handling of EPS previews SPDX-FileCopyrightText: 2003 Philipp Hullmann <phull@gmx.de>
5
6
    SPDX-License-Identifier: LGPL-2.0-or-later
7
*/
8
9
/*  This function gets a path of a DVI, EPS, PS or PDF file and
10
    produces a PNG-Thumbnail which is stored as a QImage
11
12
    The program works as follows
13
14
    1. Test if file is a DVI file
15
16
    2. Create a child process (1), in which the
17
       file is to be changed into a PNG
18
19
    3. Child-process (1) :
20
21
    4. If file is DVI continue with 6
22
23
    5. If file is no DVI continue with 9
24
25
    6. Create another child process (2), in which the DVI is
26
       turned into PS using dvips
27
28
    7. Parent process (2) :
29
       Turn the recently created PS file into a PNG file using gs
30
31
    8. continue with 10
32
33
    9. Turn the PS,PDF or EPS file into a PNG file using gs
34
35
    10. Parent process (1)
36
        store data in a QImage
37
*/
38
39
#ifdef HAVE_CONFIG_H
40
#include <config.h>
41
#endif
42
43
44
#include <assert.h>
45
#include <ctype.h>
46
#include <stdlib.h>
47
#include <stdio.h>
48
#include <unistd.h>
49
#include <signal.h>
50
#ifdef HAVE_SYS_SELECT_H
51
#include <sys/select.h>
52
#endif
53
#include <sys/time.h>
54
#include <sys/wait.h>
55
#include <fcntl.h>
56
#include <errno.h>
57
58
#include <QColor>
59
#include <QFile>
60
#include <QImage>
61
#include <QVector>
62
63
64
#include "gscreator.h"
65
#include "dscparse.h"
66
67
#include <KPluginFactory>
68
69
0
K_PLUGIN_CLASS_WITH_JSON(GSCreator, "gsthumbnail.json")
Unexecuted instantiation: gsthumbnail_factory::tr(char const*, char const*, int)
Unexecuted instantiation: gsthumbnail_factory::~gsthumbnail_factory()
70
0
71
0
// This PS snippet will be prepended to the actual file so that only
72
0
// the first page is output.
73
0
static const char *psprolog =
74
0
    "%!PS-Adobe-3.0\n"
75
0
    "/.showpage.orig /showpage load def\n"
76
0
    "/.showpage.firstonly {\n"
77
0
    "    .showpage.orig\n"
78
0
    "    quit\n"
79
0
    "} def\n"
80
0
    "/showpage { .showpage.firstonly } def\n";
81
0
82
0
// This is the code recommended by Adobe tech note 5002 for including
83
0
// EPS files.
84
0
static const char *epsprolog =
85
0
    "%!PS-Adobe-3.0\n"
86
0
    "userdict begin /pagelevel save def /showpage { } def\n"
87
0
    "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit\n"
88
0
    "[ ] 0 setdash newpath false setoverprint false setstrokeadjust\n";
89
0
90
0
static const char * gsargs_ps[] = {
91
0
    "gs",
92
0
    "-sDEVICE=png16m",
93
0
    "-sOutputFile=-",
94
0
    "-dSAFER",
95
0
    "-dPARANOIDSAFER",
96
0
    "-dNOPAUSE",
97
0
    "-dFirstPage=1",
98
0
    "-dLastPage=1",
99
0
    "-q",
100
0
    "-",
101
0
    nullptr, // file name
102
0
    "-c",
103
0
    "showpage",
104
0
    "-c",
105
0
    "quit",
106
0
    nullptr
107
0
};
108
0
109
0
static const char * gsargs_eps[] = {
110
0
    "gs",
111
0
    "-sDEVICE=png16m",
112
0
    "-sOutputFile=-",
113
0
    "-dSAFER",
114
0
    "-dPARANOIDSAFER",
115
0
    "-dNOPAUSE",
116
0
    nullptr, // page size
117
0
    nullptr, // resolution
118
0
    "-q",
119
0
    "-",
120
0
    nullptr, // file name
121
0
    "-c",
122
0
    "pagelevel",
123
0
    "-c",
124
0
    "restore",
125
0
    "-c",
126
0
    "end",
127
0
    "-c",
128
0
    "showpage",
129
0
    "-c",
130
0
    "quit",
131
0
    nullptr
132
0
};
133
0
134
0
static const char *dvipsargs[] = {
135
0
    "dvips",
136
0
    "-n",
137
0
    "1",
138
0
    "-q",
139
0
    "-o",
140
0
    "-",
141
0
    nullptr, // file name
142
0
    nullptr
143
0
};
144
0
145
0
static bool correctDVI(const QString& filename);
146
0
147
0
148
0
namespace {
149
0
  bool got_sig_term = false;
150
0
  void handle_sigterm( int ) {
151
0
    got_sig_term = true;
152
0
  }
153
}
154
155
GSCreator::GSCreator(QObject *parent, const QVariantList &args)
156
17.8k
  : KIO::ThumbnailCreator(parent, args)
157
17.8k
{
158
17.8k
}
159
160
KIO::ThumbnailResult GSCreator::create(const KIO::ThumbnailRequest &request)
161
17.8k
{
162
17.8k
  const QString path = request.url().toLocalFile();
163
17.8k
  const int width = request.targetSize().width();
164
17.8k
  const int height = request.targetSize().height();
165
// The code in the loop (when testing whether got_sig_term got set)
166
// should read some variation of:
167
//    parentJob()->wasKilled()
168
//
169
// Unfortunatelly, that's currently impossible without breaking BIC.
170
// So we need to catch the signal ourselves.
171
// Otherwise, on certain funny PS files (for example
172
// http://www.tjhsst.edu/~Eedanaher/pslife/life.ps )
173
// gs would run forever after we were dead.
174
// #### Reconsider for KDE 4 ###
175
// (24/12/03 - luis_pedro)
176
//
177
17.8k
  typedef void ( *sighandler_t )( int );
178
  // according to linux's "man signal" the above typedef is a gnu extension
179
17.8k
  sighandler_t oldhandler = signal( SIGTERM, handle_sigterm );
180
181
17.8k
  int input[2];
182
17.8k
  int output[2];
183
17.8k
  int dvipipe[2];
184
185
17.8k
  QByteArray data(1024, '\0');
186
187
17.8k
  bool ok = false;
188
189
  // Test if file is DVI
190
17.8k
  bool no_dvi =!correctDVI(request.url().toLocalFile());
191
192
17.8k
  if (pipe(input) == -1) {
193
2.87k
    return KIO::ThumbnailResult::fail();
194
2.87k
  }
195
15.0k
  if (pipe(output) == -1) {
196
0
    close(input[0]);
197
0
    close(input[1]);
198
0
    return KIO::ThumbnailResult::fail();
199
0
  }
200
201
15.0k
  KDSC dsc;
202
15.0k
  endComments = false;
203
15.0k
  dsc.setCommentHandler(this);
204
205
15.0k
  if (no_dvi)
206
15.0k
  {
207
15.0k
    FILE* fp = fopen(QFile::encodeName(path), "r");
208
15.0k
    if (fp == nullptr) return KIO::ThumbnailResult::fail();
209
210
15.0k
    char buf[4096];
211
15.0k
    int count;
212
30.0k
    while ((count = fread(buf, sizeof(char), 4096, fp)) != 0
213
15.0k
           && !endComments) {
214
15.0k
      dsc.scanData(buf, count);
215
15.0k
    }
216
15.0k
    fclose(fp);
217
218
15.0k
    if (dsc.pjl() || dsc.ctrld()) {
219
      // this file is a mess.
220
254
      return KIO::ThumbnailResult::fail();
221
254
    }
222
15.0k
  }
223
224
14.7k
  std::unique_ptr<KDSCBBOX> bbox = dsc.bbox();
225
226
14.7k
  const bool is_encapsulated = no_dvi
227
14.7k
    && (path.endsWith(QLatin1String(".eps"), Qt::CaseInsensitive)
228
14.7k
        || path.endsWith(QLatin1String(".epsi"), Qt::CaseInsensitive))
229
0
    && bbox.get() != nullptr
230
0
    && (bbox->width() > 0)
231
0
    && (bbox->height() > 0)
232
0
    && (dsc.page_count() <= 1);
233
234
14.7k
  char translation[64] = "";
235
14.7k
  char pagesize[32] = "";
236
14.7k
  char resopt[32] = "";
237
238
14.7k
  if (is_encapsulated) {
239
    // GhostScript's rendering at the extremely low resolutions
240
    // required for thumbnails leaves something to be desired. To
241
    // get nicer images, we render to four times the required
242
    // resolution and let QImage scale the result.
243
0
    const int hres = (width * 72) / bbox->width();
244
0
    const int vres = (height * 72) / bbox->height();
245
0
    const int resolution = (hres > vres ? vres : hres) * 4;
246
0
    const int gswidth = ((bbox->urx() - bbox->llx()) * resolution) / 72;
247
0
    const int gsheight = ((bbox->ury() - bbox->lly()) * resolution) / 72;
248
249
0
    snprintf(pagesize, 31, "-g%ix%i", gswidth, gsheight);
250
0
    snprintf(resopt, 31, "-r%i", resolution);
251
0
    snprintf(translation, 63,
252
0
       " 0 %i sub 0 %i sub translate\n", bbox->llx(),
253
0
       bbox->lly());
254
0
  }
255
256
14.7k
  const CDSC_PREVIEW_TYPE previewType =
257
14.7k
    static_cast<CDSC_PREVIEW_TYPE>(dsc.preview());
258
259
14.7k
  switch (previewType) {
260
24
  case CDSC_TIFF:
261
504
  case CDSC_WMF:
262
504
  case CDSC_PICT:
263
    // FIXME: these should take precedence, since they can hold
264
    // color previews, which EPSI can't (or can it?).
265
504
     break;
266
426
  case CDSC_EPSI:
267
426
    {
268
426
      if (!bbox) {
269
402
        break;
270
402
      }
271
24
      const int xscale = bbox->width() / width;
272
24
      const int yscale = bbox->height() / height;
273
24
      const int scale = xscale < yscale ? xscale : yscale;
274
24
      if (scale == 0) break;
275
12
      if (auto result = getEPSIPreview(path,
276
12
                         dsc.beginpreview(),
277
12
                         dsc.endpreview(),
278
12
                         bbox->width() / scale,
279
12
                         bbox->height() / scale); result.isValid())
280
0
        return result;
281
      // If the preview extraction routine fails, gs is used to
282
      // create a thumbnail.
283
12
    }
284
12
    break;
285
13.8k
  case CDSC_NOPREVIEW:
286
13.8k
  default:
287
    // need to run ghostscript in these cases
288
13.8k
    break;
289
14.7k
  }
290
291
14.7k
  pid_t pid = fork();
292
14.7k
  if (pid == 0) {
293
    // Child process (1)
294
295
    //    close(STDERR_FILENO);
296
297
    // find first zero entry in gsargs and put the filename
298
    // or - (stdin) there, if DVI
299
0
    const char **gsargs = gsargs_ps;
300
0
    const char **arg = gsargs;
301
302
0
    if (no_dvi && is_encapsulated) {
303
0
      gsargs = gsargs_eps;
304
0
      arg = gsargs;
305
306
      // find first zero entry and put page size there
307
0
      while (*arg) ++arg;
308
0
      *arg = pagesize;
309
310
      // find second zero entry and put resolution there
311
0
      while (*arg) ++arg;
312
0
      *arg = resopt;
313
0
    }
314
315
    // find next zero entry and put the filename there
316
0
    QByteArray fname = QFile::encodeName( path );
317
0
    while (*arg)
318
0
      ++arg;
319
0
    if( no_dvi )
320
0
      *arg = fname.data();
321
0
    else
322
0
      *arg = "-";
323
324
    // find first zero entry in dvipsargs and put the filename there
325
0
    arg = dvipsargs;
326
0
    while (*arg)
327
0
      ++arg;
328
0
    *arg = fname.data();
329
330
0
    if( !no_dvi ){
331
0
      pipe(dvipipe);
332
0
      pid_t pid_two = fork();
333
0
      if( pid_two == 0 ){
334
  // Child process (2), reopen stdout on the pipe "dvipipe" and exec dvips
335
336
0
  close(input[0]);
337
0
  close(input[1]);
338
0
  close(output[0]);
339
0
  close(output[1]);
340
0
  close(dvipipe[0]);
341
342
0
  dup2( dvipipe[1], STDOUT_FILENO);
343
344
0
  execvp(dvipsargs[0], const_cast<char *const *>(dvipsargs));
345
0
  _exit(1);
346
0
      }
347
0
      else if(pid_two != -1){
348
0
  close(input[1]);
349
0
  close(output[0]);
350
0
  close(dvipipe[1]);
351
352
0
  dup2( dvipipe[0], STDIN_FILENO);
353
0
  dup2( output[1], STDOUT_FILENO);
354
355
0
  execvp(gsargs[0], const_cast<char *const *>(gsargs));
356
0
  _exit(1);
357
0
      }
358
0
      else{
359
  // fork() (2) failed, close these
360
0
  close(dvipipe[0]);
361
0
  close(dvipipe[1]);
362
0
      }
363
364
0
    }
365
0
    else if( no_dvi ){
366
      // Reopen stdin/stdout on the pipes and exec gs
367
0
      close(input[1]);
368
0
      close(output[0]);
369
370
0
      dup2(input[0], STDIN_FILENO);
371
0
      dup2(output[1], STDOUT_FILENO);
372
373
0
      execvp(gsargs[0], const_cast<char *const *>(gsargs));
374
0
      _exit(1);
375
0
    }
376
0
  }
377
14.7k
  else if (pid != -1) {
378
    // Parent process, write first-page-only-hack (the hack is not
379
    // used if DVI) and read the png output
380
14.7k
    close(input[0]);
381
14.7k
    close(output[1]);
382
14.7k
    const char *prolog;
383
14.7k
    if (is_encapsulated)
384
0
      prolog = epsprolog;
385
14.7k
    else
386
14.7k
      prolog = psprolog;
387
14.7k
    int count = write(input[1], prolog, strlen(prolog));
388
14.7k
    if (is_encapsulated)
389
0
      write(input[1], translation, strlen(translation));
390
391
14.7k
    close(input[1]);
392
14.7k
    if (count == static_cast<int>(strlen(prolog))) {
393
14.7k
      int offset = 0;
394
29.5k
  while (!ok) {
395
14.7k
    fd_set fds;
396
14.7k
    FD_ZERO(&fds);
397
14.7k
    FD_SET(output[0], &fds);
398
14.7k
    struct timeval tv;
399
14.7k
    tv.tv_sec = 20;
400
14.7k
    tv.tv_usec = 0;
401
402
14.7k
    got_sig_term = false;
403
14.7k
    if (select(output[0] + 1, &fds, nullptr, nullptr, &tv) <= 0) {
404
0
            if ( ( errno == EINTR || errno == EAGAIN ) && !got_sig_term ) continue;
405
0
      break; // error, timeout or master wants us to quit (SIGTERM)
406
0
          }
407
14.7k
    if (FD_ISSET(output[0], &fds)) {
408
14.7k
      count = read(output[0], data.data() + offset, 1024);
409
14.7k
      if (count == -1)
410
0
        break;
411
14.7k
      else
412
14.7k
        if (count) // prepare for next block
413
0
    {
414
0
      offset += count;
415
0
      data.resize(offset + 1024);
416
0
    }
417
14.7k
        else // got all data
418
14.7k
    {
419
14.7k
      data.resize(offset);
420
14.7k
      ok = true;
421
14.7k
    }
422
14.7k
    }
423
14.7k
  }
424
14.7k
    }
425
14.7k
    if (!ok) // error or timeout, gs probably didn't exit yet
426
0
    {
427
0
      kill(pid, SIGTERM);
428
0
    }
429
430
14.7k
    int status = 0;
431
14.7k
    int ret;
432
14.7k
    do {
433
14.7k
      ret = waitpid(pid, &status, 0);
434
14.7k
    } while (ret == -1 && errno == EINTR);
435
14.7k
    if (ret != pid || (status != 0  && status != 256) )
436
0
      ok = false;
437
14.7k
  }
438
0
  else {
439
    // fork() (1) failed, close these
440
0
    close(input[0]);
441
0
    close(input[1]);
442
0
    close(output[1]);
443
0
  }
444
14.7k
  close(output[0]);
445
446
14.7k
  QImage img;
447
14.7k
  bool loaded = img.loadFromData( data );
448
449
14.7k
  if (!loaded) {
450
    // Sometimes gs spits some warning messages before the actual image
451
    // try to skip them
452
14.7k
    const QByteArray pngHeader = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
453
14.7k
    const int pngMarkerIndex = data.indexOf(pngHeader);
454
14.7k
    if (pngMarkerIndex > 0) {
455
0
      data = data.mid(pngMarkerIndex);
456
0
      loaded = img.loadFromData( data );
457
0
    }
458
14.7k
  }
459
460
14.7k
  if ( got_sig_term &&
461
0
  oldhandler != SIG_ERR &&
462
0
  oldhandler != SIG_DFL &&
463
0
  oldhandler != SIG_IGN ) {
464
0
    oldhandler( SIGTERM ); // propagate the signal. Other things might rely on it
465
0
  }
466
14.7k
  if ( oldhandler != SIG_ERR ) signal( SIGTERM, oldhandler );
467
468
14.7k
  if (loaded) {
469
0
    return KIO::ThumbnailResult::pass(img);
470
0
  }
471
472
14.7k
  return KIO::ThumbnailResult::fail();
473
14.7k
}
474
475
void GSCreator::comment(Name name)
476
37.4k
{
477
37.4k
    switch (name) {
478
13
    case EndPreview:
479
1.40k
    case BeginProlog:
480
4.51k
    case Page:
481
4.51k
      endComments = true;
482
4.51k
      break;
483
484
32.9k
    default:
485
32.9k
      break;
486
37.4k
    }
487
37.4k
}
488
489
// Quick function to check if the filename corresponds to a valid DVI
490
// file. Returns true if <filename> is a DVI file, false otherwise.
491
492
static bool correctDVI(const QString& filename)
493
17.8k
{
494
17.8k
  QFile f(filename);
495
17.8k
  if (!f.open(QIODevice::ReadOnly))
496
0
    return false;
497
498
17.8k
  unsigned char test[4];
499
17.8k
  if ( f.read( (char *)test,2)<2 || test[0] != 247 || test[1] != 2  )
500
17.7k
    return false;
501
502
94
  int n = f.size();
503
94
  if ( n < 134 ) // Too short for a dvi file
504
18
    return false;
505
76
  f.seek( n-4 );
506
507
76
  unsigned char trailer[4] = { 0xdf,0xdf,0xdf,0xdf };
508
509
76
  if ( f.read( (char *)test, 4 )<4 || strncmp( (char *)test, (char*) trailer, 4 ) )
510
76
    return false;
511
  // We suppose now that the dvi file is complete and OK
512
0
  return true;
513
76
}
514
515
KIO::ThumbnailResult GSCreator::getEPSIPreview(const QString &path, long start, long
516
             end, int imgwidth, int imgheight)
517
12
{
518
12
  FILE *fp;
519
12
  fp = fopen(QFile::encodeName(path), "r");
520
12
  if (fp == nullptr) return KIO::ThumbnailResult::fail();
521
522
12
  const long previewsize = end - start + 1;
523
524
12
  char *buf = (char *) malloc(previewsize);
525
12
  fseek(fp, start, SEEK_SET);
526
12
  int count = fread(buf, sizeof(char), previewsize - 1, fp);
527
12
  fclose(fp);
528
12
  buf[previewsize - 1] = 0;
529
12
  if (count != previewsize - 1)
530
0
  {
531
0
    free(buf);
532
0
    return KIO::ThumbnailResult::fail();
533
0
  }
534
535
12
  QString previewstr = QString::fromLatin1(buf);
536
12
  free(buf);
537
538
12
  int offset = 0;
539
744
  while ((offset < previewsize) && !(previewstr[offset].isDigit())) offset++;
540
12
  int digits = 0;
541
192
  while ((offset + digits < previewsize) && previewstr[offset + digits].isDigit()) digits++;
542
12
  int width = previewstr.mid(offset, digits).toInt();
543
12
  offset += digits + 1;
544
96
  while ((offset < previewsize) && !(previewstr[offset].isDigit())) offset++;
545
12
  digits = 0;
546
366
  while ((offset + digits < previewsize) && previewstr[offset + digits].isDigit()) digits++;
547
12
  int height = previewstr.mid(offset, digits).toInt();
548
12
  offset += digits + 1;
549
132
  while ((offset < previewsize) && !(previewstr[offset].isDigit())) offset++;
550
12
  digits = 0;
551
30
  while ((offset + digits < previewsize) && previewstr[offset + digits].isDigit()) digits++;
552
12
  int depth = previewstr.mid(offset, digits).toInt();
553
554
  // skip over the rest of the BeginPreview comment
555
270
  while ((offset < previewsize) &&
556
270
         previewstr[offset] != QLatin1Char('\n') &&
557
270
     previewstr[offset] != QLatin1Char('\r')) offset++;
558
228
  while ((offset < previewsize) && previewstr[offset] != QLatin1Char('%')) offset++;
559
560
12
  unsigned int imagedepth;
561
12
  switch (depth) {
562
0
  case 1:
563
0
  case 2:
564
0
  case 4:
565
10
  case 8:
566
10
    imagedepth = 8;
567
10
    break;
568
0
  case 12: // valid, but not (yet) supported
569
2
  default: // illegal value
570
2
    return KIO::ThumbnailResult::fail();
571
12
  }
572
573
10
  unsigned int colors = (1U << depth);
574
10
  QImage img(width, height, QImage::Format_Indexed8);
575
10
  img.setColorCount(colors);
576
577
10
  if (imagedepth <= 8) {
578
2.57k
    for (unsigned int gray = 0; gray < colors; gray++) {
579
2.56k
      unsigned int grayvalue = (255U * (colors - 1 - gray)) /
580
2.56k
  (colors - 1);
581
2.56k
      img.setColor(gray, qRgb(grayvalue, grayvalue, grayvalue));
582
2.56k
    }
583
10
  }
584
585
10
  const unsigned int bits_per_scan_line = width * depth;
586
10
  unsigned int bytes_per_scan_line = bits_per_scan_line / 8;
587
10
  if (bits_per_scan_line % 8) bytes_per_scan_line++;
588
10
  const unsigned int bindatabytes = height * bytes_per_scan_line;
589
10
  QVector<unsigned char> bindata(bindatabytes);
590
591
10
  for (unsigned int i = 0; i < bindatabytes; i++) {
592
0
    if (offset >= previewsize)
593
0
      return KIO::ThumbnailResult::fail();
594
595
0
    while (!isxdigit(previewstr[offset].toLatin1()) &&
596
0
     offset < previewsize)
597
0
      offset++;
598
599
0
    bool ok = false;
600
0
    bindata[i] = static_cast<unsigned char>(previewstr.mid(offset, 2).toUInt(&ok, 16));
601
0
    if (!ok)
602
0
      return KIO::ThumbnailResult::fail();
603
604
0
    offset += 2;
605
0
  }
606
607
20.0k
  for (int scanline = 0; scanline < height; scanline++) {
608
20.0k
    unsigned char *scanlineptr = img.scanLine(scanline);
609
610
20.0k
    for (int pixelindex = 0; pixelindex < width; pixelindex++) {
611
0
      unsigned char pixelvalue = 0;
612
0
      const unsigned int bitoffset =
613
0
        scanline * bytes_per_scan_line * 8U + pixelindex * depth;
614
0
      for (int depthindex = 0; depthindex < depth;
615
0
           depthindex++) {
616
0
        const unsigned int byteindex = (bitoffset + depthindex) / 8U;
617
0
        const unsigned int bitindex =
618
0
          7 - ((bitoffset + depthindex) % 8U);
619
0
        const unsigned char bitvalue =
620
0
          (bindata[byteindex] & static_cast<unsigned char>(1U << bitindex)) >> bitindex;
621
0
        pixelvalue |= (bitvalue << depthindex);
622
0
      }
623
0
      scanlineptr[pixelindex] = pixelvalue;
624
0
    }
625
20.0k
  }
626
627
10
  QImage outimg = img.convertToFormat(QImage::Format_RGB32).scaled(imgwidth, imgheight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
628
629
10
  return !outimg.isNull() ? KIO::ThumbnailResult::pass(outimg) : KIO::ThumbnailResult::fail();
630
10
}
631
632
#include "gscreator.moc"