@@ -497,16 +497,14 @@ QImage ThumbnailCreators::appimageThumbnailCreator(const QString &filePath, Thum
497497 qCDebug (logDFMBase) << " thumbnail: creating AppImage thumbnail for:" << filePath << " size:" << size;
498498
499499 // 1. Check if AppImage exists
500- if (!QFile::exists (filePath)) {
500+ auto info = InfoFactory::create<FileInfo>(QUrl::fromLocalFile (filePath));
501+ if (!info || !info->exists ()) {
501502 qCWarning (logDFMBase) << " thumbnail: AppImage file not found:" << filePath;
502503 return QImage ();
503504 }
504505
505506 // 2. Check if file is AppImage type and has executable permission
506- auto info = InfoFactory::create<FileInfo>(QUrl::fromLocalFile (filePath),
507- Global::CreateFileInfoType::kCreateFileInfoSync );
508- if (!info
509- || info->nameOf (NameInfoType::kMimeTypeName ) != Global::Mime::kTypeAppAppimage
507+ if (info->nameOf (NameInfoType::kMimeTypeName ) != Global::Mime::kTypeAppAppimage
510508 || !info->isAttributes (FileInfo::FileIsType::kIsExecutable )) {
511509 qCWarning (logDFMBase) << " thumbnail: file is not a valid AppImage or lacks executable permission:" << filePath
512510 << " mimeType:" << (info ? info->nameOf (NameInfoType::kMimeTypeName ) : " null" )
@@ -520,11 +518,10 @@ QImage ThumbnailCreators::appimageThumbnailCreator(const QString &filePath, Thum
520518
521519 // Read .DirIcon file from AppImage without extraction
522520 bool success = appimage_read_file_into_buffer_following_symlinks (
523- filePath.toUtf8 ().constData (),
524- " .DirIcon" ,
525- &iconBuffer,
526- &iconSize
527- );
521+ filePath.toUtf8 ().constData (),
522+ " .DirIcon" ,
523+ &iconBuffer,
524+ &iconSize);
528525
529526 // Use RAII to ensure buffer cleanup
530527 QScopedPointer<char , QScopedPointerPodDeleter> bufferCleanup (iconBuffer);
@@ -557,18 +554,19 @@ QImage ThumbnailCreators::pptxThumbnailCreator(const QString &filePath, Thumbnai
557554{
558555 qCInfo (logDFMBase) << " thumbnail: creating PPTX thumbnail for:" << filePath << " size:" << size;
559556
560- // 1. Verify file exists
561- if (!QFile:: exists (filePath )) {
557+ auto info = InfoFactory::create<FileInfo>( QUrl::fromLocalFile (filePath));
558+ if (!info || !info-> exists ()) {
562559 qCWarning (logDFMBase) << " thumbnail: PPTX file not found:" << filePath;
563560 return QImage ();
564561 }
565562
566563 // 2. Verify file type
567- auto info = InfoFactory::create<FileInfo>(QUrl::fromLocalFile (filePath),
568- Global::CreateFileInfoType::kCreateFileInfoSync );
569- if (!info || info->nameOf (NameInfoType::kMimeTypeName ) != Global::Mime::kTypeAppPptx ) {
564+ const QMimeType &mime = info->fileMimeType ();
565+ QStringList candidateTypes { info->nameOf (NameInfoType::kMimeTypeName ) };
566+ candidateTypes.append (mime.parentMimeTypes ());
567+ if (!candidateTypes.contains (Global::Mime::kTypeAppPptx )) {
570568 qCWarning (logDFMBase) << " thumbnail: file is not a valid PPTX file:" << filePath
571- << " mimeType :" << (info ? info-> nameOf (NameInfoType:: kMimeTypeName ) : " null " ) ;
569+ << " mimeTypes :" << candidateTypes ;
572570 return QImage ();
573571 }
574572
@@ -618,3 +616,121 @@ QImage ThumbnailCreators::pptxThumbnailCreator(const QString &filePath, Thumbnai
618616 qCDebug (logDFMBase) << " thumbnail: no thumbnail available for PPTX file:" << filePath;
619617 return QImage ();
620618}
619+
620+ QImage ThumbnailCreators::uabThumbnailCreator (const QString &filePath, ThumbnailSize size)
621+ {
622+ qCDebug (logDFMBase) << " thumbnail: creating UAB thumbnail for:" << filePath << " size:" << size;
623+
624+ // 1. Validate file existence
625+ auto info = InfoFactory::create<FileInfo>(QUrl::fromLocalFile (filePath));
626+ if (!info || !info->exists ()) {
627+ qCWarning (logDFMBase) << " thumbnail: UAB file not found:" << filePath;
628+ return QImage ();
629+ }
630+
631+ // 2. Verify file type
632+ if (!info || info->nameOf (NameInfoType::kMimeTypeName ) != Global::Mime::kTypeAppUab ) {
633+ qCWarning (logDFMBase) << " thumbnail: file is not a valid UAB file:" << filePath
634+ << " mimeType:" << (info ? info->nameOf (NameInfoType::kMimeTypeName ) : " null" );
635+ return QImage ();
636+ }
637+
638+ // 3. Query ELF section information using readelf (read-only, no file modification)
639+ QProcess readelfProcess;
640+ readelfProcess.start (" readelf" , { " -W" , " -S" , filePath });
641+
642+ if (!readelfProcess.waitForFinished (5000 )) {
643+ qCWarning (logDFMBase) << " thumbnail: readelf timeout for UAB file:" << filePath;
644+ readelfProcess.kill ();
645+ return QImage ();
646+ }
647+
648+ if (readelfProcess.exitCode () != 0 ) {
649+ qCWarning (logDFMBase) << " thumbnail: readelf failed for UAB file:" << filePath
650+ << " stderr:" << readelfProcess.readAllStandardError ();
651+ return QImage ();
652+ }
653+
654+ // 4. Parse readelf output to locate linglong.icon section
655+ QString readelfOutput = readelfProcess.readAllStandardOutput ();
656+ QStringList lines = readelfOutput.split (' \n ' );
657+
658+ qint64 sectionOffset = -1 ;
659+ qint64 sectionSize = -1 ;
660+ bool sectionFound = false ;
661+
662+ for (const QString &line : lines) {
663+ if (line.contains (" linglong.icon" )) {
664+ // Parse section header line format:
665+ // [Nr] Name Type Address Off Size ES Flg Lk Inf Al
666+ // [26] linglong.icon PROGBITS 0000000000000000 002e30 004e20 00 0 0 1
667+ QString normalizedLine = line.simplified ();
668+ QStringList parts = normalizedLine.split (' ' , Qt::SkipEmptyParts);
669+
670+ // Validate column count (need at least 7 columns)
671+ if (parts.size () >= 7 ) {
672+ bool offsetOk = false , sizeOk = false ;
673+ sectionOffset = parts[4 ].toLongLong (&offsetOk, 16 ); // Column 5: Offset (hex)
674+ sectionSize = parts[5 ].toLongLong (&sizeOk, 16 ); // Column 6: Size (hex)
675+
676+ if (offsetOk && sizeOk && sectionOffset > 0 && sectionSize > 0 ) {
677+ sectionFound = true ;
678+ qCDebug (logDFMBase) << " thumbnail: found linglong.icon section - offset:"
679+ << QString (" 0x%1" ).arg (sectionOffset, 0 , 16 )
680+ << " size:" << sectionSize << " bytes" ;
681+ break ;
682+ }
683+ }
684+ }
685+ }
686+
687+ if (!sectionFound) {
688+ qCWarning (logDFMBase) << " thumbnail: linglong.icon section not found in UAB file:" << filePath;
689+ return QImage ();
690+ }
691+
692+ // 5. Extract section data using QFile (read-only mode, no file modification)
693+ QFile uabFile (filePath);
694+ if (!uabFile.open (QIODevice::ReadOnly)) {
695+ qCWarning (logDFMBase) << " thumbnail: failed to open UAB file for reading:" << filePath
696+ << " error:" << uabFile.errorString ();
697+ return QImage ();
698+ }
699+
700+ if (!uabFile.seek (sectionOffset)) {
701+ qCWarning (logDFMBase) << " thumbnail: failed to seek to section offset:" << sectionOffset
702+ << " in file:" << filePath
703+ << " error:" << uabFile.errorString ();
704+ uabFile.close ();
705+ return QImage ();
706+ }
707+
708+ QByteArray iconData = uabFile.read (sectionSize);
709+ uabFile.close (); // Close immediately to release file handle
710+
711+ if (iconData.size () != sectionSize) {
712+ qCWarning (logDFMBase) << " thumbnail: incomplete section data read - expected:" << sectionSize
713+ << " bytes, got:" << iconData.size () << " bytes for:" << filePath;
714+ return QImage ();
715+ }
716+
717+ qCDebug (logDFMBase) << " thumbnail: successfully extracted" << iconData.size ()
718+ << " bytes of icon data from UAB file:" << filePath;
719+
720+ // 6. Load image directly from extracted data (no temporary file needed)
721+ QImage icon;
722+ if (!icon.loadFromData (iconData)) {
723+ qCWarning (logDFMBase) << " thumbnail: failed to decode image from extracted icon data for:" << filePath;
724+ return QImage ();
725+ }
726+
727+ // 7. Scale image to requested thumbnail size
728+ if (icon.width () > size || icon.height () > size) {
729+ icon = icon.scaled (size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
730+ qCDebug (logDFMBase) << " thumbnail: scaled UAB icon from"
731+ << icon.size () << " to fit size:" << size;
732+ }
733+
734+ qCDebug (logDFMBase) << " thumbnail: UAB thumbnail created successfully for:" << filePath;
735+ return icon;
736+ }
0 commit comments