Coverage Report

Created: 2026-04-29 07:00

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.1k
  : KIO::ThumbnailCreator(parent, args)
157
17.1k
{
158
17.1k
}
159
160
KIO::ThumbnailResult GSCreator::create(const KIO::ThumbnailRequest &request)
161
17.1k
{
162
17.1k
  const QString path = request.url().toLocalFile();
163
17.1k
  const int width = request.targetSize().width();
164
17.1k
  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.1k
  typedef void ( *sighandler_t )( int );
178
  // according to linux's "man signal" the above typedef is a gnu extension
179
17.1k
  sighandler_t oldhandler = signal( SIGTERM, handle_sigterm );
180
181
17.1k
  int input[2];
182
17.1k
  int output[2];
183
17.1k
  int dvipipe[2];
184
185
17.1k
  QByteArray data(1024, '\0');
186
187
17.1k
  bool ok = false;
188
189
  // Test if file is DVI
190
17.1k
  bool no_dvi =!correctDVI(request.url().toLocalFile());
191
192
17.1k
  if (pipe(input) == -1) {
193
1.50k
    return KIO::ThumbnailResult::fail();
194
1.50k
  }
195
15.6k
  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.6k
  KDSC dsc;
202
15.6k
  endComments = false;
203
15.6k
  dsc.setCommentHandler(this);
204
205
15.6k
  if (no_dvi)
206
15.6k
  {
207
15.6k
    FILE* fp = fopen(QFile::encodeName(path), "r");
208
15.6k
    if (fp == nullptr) return KIO::ThumbnailResult::fail();
209
210
15.6k
    char buf[4096];
211
15.6k
    int count;
212
31.2k
    while ((count = fread(buf, sizeof(char), 4096, fp)) != 0
213
15.6k
           && !endComments) {
214
15.6k
      dsc.scanData(buf, count);
215
15.6k
    }
216
15.6k
    fclose(fp);
217
218
15.6k
    if (dsc.pjl() || dsc.ctrld()) {
219
      // this file is a mess.
220
254
      return KIO::ThumbnailResult::fail();
221
254
    }
222
15.6k
  }
223
224
15.3k
  std::unique_ptr<KDSCBBOX> bbox = dsc.bbox();
225
226
15.3k
  const bool is_encapsulated = no_dvi
227
15.3k
    && (path.endsWith(QLatin1String(".eps"), Qt::CaseInsensitive)
228
15.3k
        || 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
15.3k
  char translation[64] = "";
235
15.3k
  char pagesize[32] = "";
236
15.3k
  char resopt[32] = "";
237
238
15.3k
  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
15.3k
  const CDSC_PREVIEW_TYPE previewType =
257
15.3k
    static_cast<CDSC_PREVIEW_TYPE>(dsc.preview());
258
259
15.3k
  switch (previewType) {
260
21
  case CDSC_TIFF:
261
497
  case CDSC_WMF:
262
497
  case CDSC_PICT:
263
    // FIXME: these should take precedence, since they can hold
264
    // color previews, which EPSI can't (or can it?).
265
497
     break;
266
647
  case CDSC_EPSI:
267
647
    {
268
647
      if (!bbox) {
269
276
        break;
270
276
      }
271
371
      const int xscale = bbox->width() / width;
272
371
      const int yscale = bbox->height() / height;
273
371
      const int scale = xscale < yscale ? xscale : yscale;
274
371
      if (scale == 0) break;
275
352
      if (auto result = getEPSIPreview(path,
276
352
                         dsc.beginpreview(),
277
352
                         dsc.endpreview(),
278
352
                         bbox->width() / scale,
279
352
                         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
352
    }
284
352
    break;
285
14.2k
  case CDSC_NOPREVIEW:
286
14.2k
  default:
287
    // need to run ghostscript in these cases
288
14.2k
    break;
289
15.3k
  }
290
291
15.3k
  pid_t pid = fork();
292
15.3k
  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
15.3k
  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
15.3k
    close(input[0]);
381
15.3k
    close(output[1]);
382
15.3k
    const char *prolog;
383
15.3k
    if (is_encapsulated)
384
0
      prolog = epsprolog;
385
15.3k
    else
386
15.3k
      prolog = psprolog;
387
15.3k
    int count = write(input[1], prolog, strlen(prolog));
388
15.3k
    if (is_encapsulated)
389
0
      write(input[1], translation, strlen(translation));
390
391
15.3k
    close(input[1]);
392
15.3k
    if (count == static_cast<int>(strlen(prolog))) {
393
15.3k
      int offset = 0;
394
30.7k
  while (!ok) {
395
15.3k
    fd_set fds;
396
15.3k
    FD_ZERO(&fds);
397
15.3k
    FD_SET(output[0], &fds);
398
15.3k
    struct timeval tv;
399
15.3k
    tv.tv_sec = 20;
400
15.3k
    tv.tv_usec = 0;
401
402
15.3k
    got_sig_term = false;
403
15.3k
    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
15.3k
    if (FD_ISSET(output[0], &fds)) {
408
15.3k
      count = read(output[0], data.data() + offset, 1024);
409
15.3k
      if (count == -1)
410
0
        break;
411
15.3k
      else
412
15.3k
        if (count) // prepare for next block
413
0
    {
414
0
      offset += count;
415
0
      data.resize(offset + 1024);
416
0
    }
417
15.3k
        else // got all data
418
15.3k
    {
419
15.3k
      data.resize(offset);
420
15.3k
      ok = true;
421
15.3k
    }
422
15.3k
    }
423
15.3k
  }
424
15.3k
    }
425
15.3k
    if (!ok) // error or timeout, gs probably didn't exit yet
426
0
    {
427
0
      kill(pid, SIGTERM);
428
0
    }
429
430
15.3k
    int status = 0;
431
15.3k
    int ret;
432
15.3k
    do {
433
15.3k
      ret = waitpid(pid, &status, 0);
434
15.3k
    } while (ret == -1 && errno == EINTR);
435
15.3k
    if (ret != pid || (status != 0  && status != 256) )
436
0
      ok = false;
437
15.3k
  }
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
15.3k
  close(output[0]);
445
446
15.3k
  QImage img;
447
15.3k
  bool loaded = img.loadFromData( data );
448
449
15.3k
  if (!loaded) {
450
    // Sometimes gs spits some warning messages before the actual image
451
    // try to skip them
452
15.3k
    const QByteArray pngHeader = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
453
15.3k
    const int pngMarkerIndex = data.indexOf(pngHeader);
454
15.3k
    if (pngMarkerIndex > 0) {
455
0
      data = data.mid(pngMarkerIndex);
456
0
      loaded = img.loadFromData( data );
457
0
    }
458
15.3k
  }
459
460
15.3k
  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
15.3k
  if ( oldhandler != SIG_ERR ) signal( SIGTERM, oldhandler );
467
468
15.3k
  if (loaded) {
469
0
    return KIO::ThumbnailResult::pass(img);
470
0
  }
471
472
15.3k
  return KIO::ThumbnailResult::fail();
473
15.3k
}
474
475
void GSCreator::comment(Name name)
476
42.1k
{
477
42.1k
    switch (name) {
478
23
    case EndPreview:
479
1.06k
    case BeginProlog:
480
3.66k
    case Page:
481
3.66k
      endComments = true;
482
3.66k
      break;
483
484
38.4k
    default:
485
38.4k
      break;
486
42.1k
    }
487
42.1k
}
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.1k
{
494
17.1k
  QFile f(filename);
495
17.1k
  if (!f.open(QIODevice::ReadOnly))
496
0
    return false;
497
498
17.1k
  unsigned char test[4];
499
17.1k
  if ( f.read( (char *)test,2)<2 || test[0] != 247 || test[1] != 2  )
500
17.0k
    return false;
501
502
102
  int n = f.size();
503
102
  if ( n < 134 ) // Too short for a dvi file
504
11
    return false;
505
91
  f.seek( n-4 );
506
507
91
  unsigned char trailer[4] = { 0xdf,0xdf,0xdf,0xdf };
508
509
91
  if ( f.read( (char *)test, 4 )<4 || strncmp( (char *)test, (char*) trailer, 4 ) )
510
91
    return false;
511
  // We suppose now that the dvi file is complete and OK
512
0
  return true;
513
91
}
514
515
KIO::ThumbnailResult GSCreator::getEPSIPreview(const QString &path, long start, long
516
             end, int imgwidth, int imgheight)
517
352
{
518
352
  FILE *fp;
519
352
  fp = fopen(QFile::encodeName(path), "r");
520
352
  if (fp == nullptr) return KIO::ThumbnailResult::fail();
521
522
352
  const long bufSize = end - start + 1;
523
524
352
  char *buf = (char *) malloc(bufSize);
525
352
  fseek(fp, start, SEEK_SET);
526
352
  const int previewsize = fread(buf, sizeof(char), bufSize - 1, fp);
527
352
  fclose(fp);
528
352
  buf[bufSize - 1] = 0;
529
352
  if (previewsize != bufSize - 1)
530
0
  {
531
0
    free(buf);
532
0
    return KIO::ThumbnailResult::fail();
533
0
  }
534
535
352
  const QString previewstr = QString::fromLatin1(buf);
536
352
  Q_ASSERT(previewstr.length() == previewsize);
537
352
  free(buf);
538
539
352
  int offset = 0;
540
18.6k
  while ((offset < previewsize) && !(previewstr[offset].isDigit())) offset++;
541
352
  int digits = 0;
542
3.92k
  while ((offset + digits < previewsize) && previewstr[offset + digits].isDigit()) digits++;
543
352
  int width = previewstr.mid(offset, digits).toInt();
544
352
  offset += digits + 1;
545
7.28k
  while ((offset < previewsize) && !(previewstr[offset].isDigit())) offset++;
546
352
  digits = 0;
547
4.50k
  while ((offset + digits < previewsize) && previewstr[offset + digits].isDigit()) digits++;
548
352
  int height = previewstr.mid(offset, digits).toInt();
549
352
  offset += digits + 1;
550
9.00k
  while ((offset < previewsize) && !(previewstr[offset].isDigit())) offset++;
551
352
  digits = 0;
552
3.74k
  while ((offset + digits < previewsize) && previewstr[offset + digits].isDigit()) digits++;
553
352
  int depth = previewstr.mid(offset, digits).toInt();
554
555
  // skip over the rest of the BeginPreview comment
556
21.0k
  while ((offset < previewsize) &&
557
20.9k
         previewstr[offset] != QLatin1Char('\n') &&
558
20.9k
     previewstr[offset] != QLatin1Char('\r')) offset++;
559
7.85k
  while ((offset < previewsize) && previewstr[offset] != QLatin1Char('%')) offset++;
560
561
352
  unsigned int imagedepth;
562
352
  switch (depth) {
563
52
  case 1:
564
74
  case 2:
565
75
  case 4:
566
93
  case 8:
567
93
    imagedepth = 8;
568
93
    break;
569
0
  case 12: // valid, but not (yet) supported
570
259
  default: // illegal value
571
259
    return KIO::ThumbnailResult::fail();
572
352
  }
573
574
93
  unsigned int colors = (1U << depth);
575
93
  QImage img(width, height, QImage::Format_Indexed8);
576
93
  img.setColorCount(colors);
577
578
93
  if (imagedepth <= 8) {
579
4.90k
    for (unsigned int gray = 0; gray < colors; gray++) {
580
4.81k
      unsigned int grayvalue = (255U * (colors - 1 - gray)) /
581
4.81k
  (colors - 1);
582
4.81k
      img.setColor(gray, qRgb(grayvalue, grayvalue, grayvalue));
583
4.81k
    }
584
93
  }
585
586
93
  const unsigned int bits_per_scan_line = width * depth;
587
93
  unsigned int bytes_per_scan_line = bits_per_scan_line / 8;
588
93
  if (bits_per_scan_line % 8) bytes_per_scan_line++;
589
93
  const unsigned int bindatabytes = height * bytes_per_scan_line;
590
93
  QVector<unsigned char> bindata(bindatabytes);
591
592
110
  for (unsigned int i = 0; i < bindatabytes; i++) {
593
57
    if (offset >= previewsize)
594
0
      return KIO::ThumbnailResult::fail();
595
596
205
    while (!isxdigit(previewstr[offset].toLatin1()) &&
597
148
     offset < previewsize)
598
148
      offset++;
599
600
57
    bool ok = false;
601
57
    bindata[i] = static_cast<unsigned char>(previewstr.mid(offset, 2).toUInt(&ok, 16));
602
57
    if (!ok)
603
40
      return KIO::ThumbnailResult::fail();
604
605
17
    offset += 2;
606
17
  }
607
608
28.6M
  for (int scanline = 0; scanline < height; scanline++) {
609
28.6M
    unsigned char *scanlineptr = img.scanLine(scanline);
610
611
28.6M
    for (int pixelindex = 0; pixelindex < width; pixelindex++) {
612
0
      unsigned char pixelvalue = 0;
613
0
      const unsigned int bitoffset =
614
0
        scanline * bytes_per_scan_line * 8U + pixelindex * depth;
615
0
      for (int depthindex = 0; depthindex < depth;
616
0
           depthindex++) {
617
0
        const unsigned int byteindex = (bitoffset + depthindex) / 8U;
618
0
        const unsigned int bitindex =
619
0
          7 - ((bitoffset + depthindex) % 8U);
620
0
        const unsigned char bitvalue =
621
0
          (bindata[byteindex] & static_cast<unsigned char>(1U << bitindex)) >> bitindex;
622
0
        pixelvalue |= (bitvalue << depthindex);
623
0
      }
624
0
      scanlineptr[pixelindex] = pixelvalue;
625
0
    }
626
28.6M
  }
627
628
53
  QImage outimg = img.convertToFormat(QImage::Format_RGB32).scaled(imgwidth, imgheight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
629
630
53
  return !outimg.isNull() ? KIO::ThumbnailResult::pass(outimg) : KIO::ThumbnailResult::fail();
631
93
}
632
633
#include "gscreator.moc"