#include "imageheaders.h"
#include "imageutils.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <tiff.h>
#include <tiffio.h>
#include <png.h>
#include <qfile.h>
#include <qdatastream.h>
#include <klocale.h>
#include "../misc/exif.h"

// This is pretty simple. See if there is the required ID's for animated GIFs
// in the first 1024 bytes: either NETSCAPE or ANIMEXTS. The exact placement
// can differ but should always be in the first 1K, (at least it is in all
// my images ;-)
bool isAnimatedGIF(const char *fn)
{
    static char buffer[1024];
    int fd = open(fn, O_RDONLY);
    int i, count;

    if(fd == -1){
        qWarning("Could not open GIF to check animation!");
        return(false);
    }
    count = read(fd, buffer, 1024);
    close(fd);

    for(i=0; i < count; ++i){
        if(buffer[i] == 'N'){
            if(qstrncmp(buffer+i, "NETSCAPE", 8) == 0)
                return(true);
        }
        else if(buffer[i] == 'A'){
            if(qstrncmp(buffer+i, "ANIMEXTS", 8) == 0)
                return(true);
        }
    }
    return(false);
}

// Checks for TIFF thumbnails. TIFF uses virtual folders inside the image.
// The one that has a preview should have a specific tag set but lots of
// camera vendors use nonstandard tags. So we look if there is an image
// smaller than the main image, and use that if it exists.
bool checkTIFFThumbnail(const char *filename, QImage &img)
{
    img.reset();

    TIFF *t = TIFFOpen(filename, "r");
    if(!t)
        return(false);

    int isValidDir = true;
    uint32 width, height;
    uint32 maxWidth=0;
    uint32 minWidth=0;
    tdir_t thumbDir = (tdir_t)-1;
    bool sizeProcessed = false;
    while(isValidDir){
        if(TIFFGetField(t, TIFFTAG_IMAGEWIDTH, &width)){
            if(!sizeProcessed){
                maxWidth = width;
                minWidth = width;
                sizeProcessed = true;
                thumbDir = TIFFCurrentDirectory(t);
            }
            else{
                if(width > maxWidth)
                    maxWidth = width;
                else if(width < minWidth){
                    minWidth = width;
                    thumbDir = TIFFCurrentDirectory(t);
                }
            }
        }
        isValidDir = TIFFReadDirectory(t);
    }
    if(thumbDir != ((tdir_t)-1)){
        if(minWidth != maxWidth)
            qWarning("Found small TIFF subimage, orig width: %d, small: %d",
                     (unsigned int)maxWidth, (unsigned int)minWidth);
        if(TIFFSetDirectory(t, thumbDir)){
            TIFFGetField(t, TIFFTAG_IMAGELENGTH, &height);
            img.create(minWidth, height, 32);
            if(!TIFFReadRGBAImage(t, minWidth, height, (uint32*)
                                  img.bits(), 0))
                img.reset();
            else{
                uint32 r, b;
                unsigned int i, x, count, pixel, mid, *data, *midData;
                count = img.width()*img.height();
                data = (unsigned int *)img.bits();
                for(i=0; i < count; ++i){
                    pixel = data[i];
                    r = (0x00FF0000 & data[i]) >> 16;
                    b = (0x000000FF & data[i]) << 16;
                    pixel &= 0xFF00FF00;
                    pixel += r+b;
                    data[i] = pixel;
                }
                mid = height >> 1;
                for(i=0; i < mid; ++i){
                    data = (unsigned int *)img.scanLine(i);
                    midData = (unsigned int *)img.scanLine(height-(i+1));
                    for(x=0; x < minWidth; ++x){
                        pixel = data[x];
                        data[x] = midData[x];
                        midData[x] = pixel;
                    }
                }
                if(minWidth != maxWidth)
                    qWarning("Returning TIFF subimage, size %d,%d", img.width(),
                             img.height());
            }
        }
    }
    TIFFClose(t);
    return(!img.isNull());
}

bool appendTooltipData(const char *filename, QString &imageStr,
                       QString &cameraStr, QString &commentStr,
                       bool isRTF)
{
    const char *ext = extension(filename);
    if(qstricmp(ext, "png") == 0)
        return(appendPNGTooltipData(filename, imageStr, commentStr, isRTF));
    else if(qstricmp(ext, "gif") == 0)
        return(appendGIFTooltipData(filename, imageStr, isRTF));
    else if(qstricmp(ext, "jpg") == 0 || qstricmp(ext, "jpeg") == 0)
        return(appendJPEGTooltipData(filename, imageStr, cameraStr,
                                     commentStr, isRTF));
    else if(qstricmp(ext, "tif") == 0 || qstricmp(ext, "tiff") == 0)
        return(appendTIFFTooltipData(filename, imageStr, cameraStr,
                                     commentStr, isRTF));
    else if(qstricmp(ext, "bmp") == 0)
        return(appendBMPTooltipData(filename, imageStr, isRTF));

    return(false);
}

bool appendGIFTooltipData(const char *filename, QString &imageStr,
                         bool isRTF)
{
    static char buffer[1024];
    int i;
    uint16 w, h;
    QString nl(isRTF ? "<BR>" : "\n");

    // Using QFile and streams is overkill, but I like the free conversions
    // for the size >:) We just use open() and read() for checking animation.
    QFile f(filename);
    if(!f.open(IO_ReadOnly))
        return(false);
    QDataStream stream(&f);
    stream.setByteOrder(QDataStream::LittleEndian);

    stream.readRawBytes(buffer, 3);
    if(qstrncmp(buffer, "GIF", 3) != 0){
        f.close();
        return(false);
    }

    stream.readRawBytes(buffer, 3);
    buffer[3] = '\0';
    stream >> w;
    stream >> h;

    imageStr += i18n("Image size: %1x%2").arg(w).arg(h)
        + nl;
    imageStr += i18n("Version: %1").arg(buffer) + nl;

    stream.readRawBytes(buffer, 1024);
    f.close();
    imageStr += i18n("Animated: ");
    for(i=0; i < 1024; ++i){
        if(buffer[i] == 'N'){
            if(qstrncmp(buffer+i, "NETSCAPE", 8) == 0){
                imageStr += i18n("yes") + nl;
                return(true);
            }
        }
        else if(buffer[i] == 'A'){
            if(qstrncmp(buffer+i, "ANIMEXTS", 8) == 0){
                imageStr += i18n("yes") + nl;
                return(true);
            }
        }
    }
    imageStr += i18n("no") + nl;
    return(true);
}

bool appendBMPTooltipData(const char *filename, QString &imageStr,
                          bool isRTF)
{
    static char buffer[2];
    QString typeStr, compStr;
    QString nl(isRTF ? "<BR>" : "\n");

    QFile f(filename);
    if(!f.open(IO_ReadOnly))
        return(false);
    QDataStream stream(&f);
    stream.setByteOrder(QDataStream::LittleEndian);
    stream.readRawBytes(buffer, 2);
    if(qstrncmp(buffer, "BM", 2) == 0)
        typeStr = i18n("Windows BMP");
    else if(qstrncmp(buffer, "BA", 2) == 0)
        typeStr = i18n("OS/2 Bitmap");
    else if(qstrncmp(buffer, "CI", 2) == 0 ||
            qstrncmp(buffer, "IC", 2) == 0)
        typeStr = i18n("OS/2 Icon");
    else if(qstrncmp(buffer, "CP", 2) == 0 ||
            qstrncmp(buffer, "PT", 2) == 0)
        typeStr = i18n("OS/2 Pointer");
    else{
        f.close();
        return(false);
    }

    uint32 temp, w, h, compression;
    uint16 planes, bits;

    stream >> temp; // skip this stuff
    stream >> temp;
    stream >> temp;
    stream >> temp;
    stream >> w;
    stream >> h;
    stream >> planes;
    stream >> bits;
    stream >> compression;
    if(compression == 0)
        compStr = i18n("None");
    else if(compression == 1)
        compStr = i18n("RLE 8bpp");
    else if(compression == 2)
        compStr = i18n("RLE 4bpp");
    else if(compression == 3)
        compStr = i18n("Bitfields");
    else
        compStr = i18n("Unknown");

    imageStr += i18n("Image size: %1x%2").arg((unsigned int)w).arg((unsigned int)h)
        + nl;
    imageStr += i18n("Type: %1").arg(typeStr) + ", ";
    imageStr += i18n("Compression: %1").arg(compStr) + nl;
    f.close();
    return(true);
}

bool appendPNGTooltipData(const char *filename, QString &imageStr,
                          QString &commentStr, bool isRTF)
{
    int num_text=0;
    png_textp text_ptr;
    png_structp png_ptr;
    png_infop info_ptr;
    //unsigned int sig_read = 0;
    png_uint_32 width, height;
    int bit_depth, color_type, interlace_type;
    FILE *fp;
    QString nl(isRTF ? "<BR>" : "\n");

    if((fp = fopen(filename, "rb")) == NULL)
        return(false);

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if(!png_ptr){
        fclose(fp);
        return(false);
    }

    info_ptr = png_create_info_struct(png_ptr);
    if(!info_ptr){
      fclose(fp);
      png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
      return(-1);
    }

    if(setjmp(png_jmpbuf(png_ptr))){
      png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
      fclose(fp);
      return(-1);
    }

    png_init_io(png_ptr, fp);
    png_read_info(png_ptr, info_ptr);
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
       &interlace_type, NULL, NULL);

    imageStr += i18n("Image size: %1x%2").arg((unsigned int)width).arg((unsigned int)height) +
        nl;

    QString tmpStr;
    switch(color_type){
    case PNG_COLOR_TYPE_GRAY:
        tmpStr = i18n("Grayscale");
        break;
    case PNG_COLOR_TYPE_PALETTE:
        tmpStr = i18n("Palette");
        break;
    case PNG_COLOR_TYPE_RGB:
        tmpStr = i18n("RGB");
        break;
    case PNG_COLOR_TYPE_RGB_ALPHA:
        tmpStr = i18n("RGB w/alpha");
        break;
    case PNG_COLOR_TYPE_GRAY_ALPHA:
        tmpStr = i18n("Grayscale w/alpha");
        break;
    default:
        tmpStr = i18n("Unknown");
        break;
    }
    imageStr +=   i18n("Color type: %1").arg(tmpStr) + ", "
                + i18n("Bit Depth: %1").arg(bit_depth) + nl;

    png_get_text(png_ptr,info_ptr, &text_ptr, &num_text);
    while(num_text--){
        commentStr += QString(text_ptr->text) + nl;
        text_ptr++;
    }
    // finished
    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
    fclose(fp);
    return(true);
}

bool appendJPEGTooltipData(const char *filename, QString &imageStr,
                           QString &cameraStr, QString &commentStr,
                           bool isRTF)
{
    MyExifData ImageInfo;
    bool needsNewline;
    QString tmpStr;
    QString nl(isRTF ? "<BR>" : "\n");
    if(!ImageInfo.scan(filename)){
        qWarning("Could not scan JPEG file %s!", filename);
        return(false);
    }

    //
    // camera info
    //
    needsNewline = false;
    tmpStr = ImageInfo.getCameraModel();
    if(!tmpStr.isEmpty()){
        cameraStr += i18n("Camera Model: %1").arg(tmpStr);
        needsNewline = true;
    }
    tmpStr = ImageInfo.getCameraMake();
    if(!tmpStr.isEmpty()){
        needsNewline = true;
        if(cameraStr.isEmpty())
            cameraStr += i18n("Camera Make: %1").arg(tmpStr);
        else
            cameraStr += ", " + i18n("Make: %1").arg(tmpStr);
    }
    if(needsNewline)
        cameraStr += nl;

    tmpStr = ImageInfo.getDateTime();
    if(!tmpStr.isEmpty())
        cameraStr += i18n("Photo taken on: %1").arg(tmpStr) + nl;

    if(ImageInfo.getFlashUsed())
        cameraStr += i18n("Flash used") + nl;
    if(ImageInfo.getFocalLength()){
	    cameraStr += i18n("Focal length: %1").arg(QString().sprintf("%4.1f", ImageInfo.getFocalLength()));
        if(ImageInfo.getCCDWidth()){
            cameraStr += ", " + i18n("35mm equivalent: %1").arg(ImageInfo.getFocalLength() / ImageInfo.getCCDWidth()*35 + 0.5);
        }
        cameraStr += nl;
    }
    if(ImageInfo.getCCDWidth()){
        cameraStr += i18n("CCD width: %1").arg(QString().sprintf("%4.2f", ImageInfo.getCCDWidth())) + nl;
    }
    float exposureTime = ImageInfo.getExposureTime();
    if(exposureTime){
        if(exposureTime > 0 && exposureTime <= 0.5){
            cameraStr += i18n("Exposure time: (1/%1)").arg((int)(0.5 + 1/exposureTime)) + nl;
        }
    }
    if(ImageInfo.getApertureFNumber()){
        cameraStr += i18n("Aperture: f/%1").arg(QString().sprintf("%3.1f", (double)ImageInfo.getApertureFNumber())) +
            nl;
    }
    if(ImageInfo.getDistance()){
        if(ImageInfo.getDistance() < 0)
            cameraStr += i18n("Focus distance: infinite");
        else
            cameraStr += i18n("Focus distance: %1m").arg(QString().sprintf("%5.2fm",(double)ImageInfo.getDistance()));
        cameraStr += nl;
    }
    if(ImageInfo.getExposureBias()){
        cameraStr += i18n("Exposure bias: %1").arg(QString().sprintf("%4.2f", (double)ImageInfo.getExposureBias())) +
            nl;
    }
    if(ImageInfo.getWhitebalance() != -1){
        cameraStr += i18n("White balance: ");
        switch(ImageInfo.getWhitebalance()){
	case 0:
	    cameraStr += i18n("Unknown");
	    break;
	case 1:
	    cameraStr += i18n("Daylight");
	    break;
	case 2:
	    cameraStr += i18n("Fluorescent");
	    break;
	case 3:
	    //tag=i18n("incandescent");
	    cameraStr += i18n("Tungsten");
	    break;
	case 17:
	    cameraStr += i18n("Standard light A");
	    break;
	case 18:
	    cameraStr += i18n("Standard light B");
	    break;
	case 19:
	    cameraStr += i18n("Standard light C");
	    break;
	case 20:
	    cameraStr += i18n("D55");
	    break;
	case 21:
	    cameraStr += i18n("D65");
	    break;
	case 22:
	    cameraStr += i18n("D75");
	    break;
	case 255:
	    cameraStr += i18n("Other");
	    break;
	default:
            //23 to 254 = reserved
	    cameraStr += i18n("Unknown");
        }
        cameraStr += nl;
    }

    if(ImageInfo.getMeteringMode() != -1){
        cameraStr += i18n("Metering mode: ");
        switch(ImageInfo.getMeteringMode()) {
	case 0:
	    cameraStr += i18n("Unknown");
	    break;
	case 1:
	    cameraStr += i18n("Average");
	    break;
	case 2:
	    cameraStr += i18n("Center weighted average");
	    break;
	case 3:
	    cameraStr += i18n("Spot");
	    break;
	case 4:
	    cameraStr += i18n("MultiSpot");
	    break;
	case 5:
	    cameraStr += i18n("Pattern");
	    break;
	case 6:
	    cameraStr += i18n("Partial");
	    break;
	case 255:
	    cameraStr += i18n("Other");
	    break;
	default:
	    // 7 to 254 = reserved
	    cameraStr += i18n("Unknown");
        }
        cameraStr += nl;
    }

    if(ImageInfo.getExposureProgram()){
        cameraStr += i18n("Exposure: ");
        switch(ImageInfo.getExposureProgram()) {
	case 0:
	    cameraStr += i18n("Not defined");
	    break;
	case 1:
	    cameraStr += i18n("Manual");
	    break;
	case 2:
	    cameraStr += i18n("Normal program");
	    break;
	case 3:
	    cameraStr += i18n("Aperture priority");
	    break;
	case 4:
	    cameraStr += i18n("Shutter priority");
	    break;
	case 5:
	    cameraStr += i18n("Creative program (biased to fast shutter)");
	    break;
	case 6:
	    cameraStr += i18n("Action program (biased to fast shutter)");
	    break;
	case 7:
	    cameraStr += i18n("Portrait mode (closeup photos)");
	    break;
	case 8:
	    cameraStr += i18n("Landscape mode (landscape photos)");
	    break;
	default:
	    // 9 to 255 = reserved
	    cameraStr += i18n("Unknown");
        }
        cameraStr += nl;
    }
    if(ImageInfo.getISOequivalent()){
        cameraStr += i18n("ISO equivalent: %1").arg(QString().sprintf("%2d", (int)ImageInfo.getISOequivalent())) +
            nl;
    }

    //
    // image info
    //

    imageStr +=  i18n("Image size: %1x%2").arg(ImageInfo.getWidth()).arg(ImageInfo.getHeight()) + nl;
    if(ImageInfo.getOrientation()){
        // FIXME: format values
        imageStr += i18n("Orientation: %1").arg(ImageInfo.getOrientation()) + nl;
    }
    imageStr += i18n("Color mode: %1").arg(ImageInfo.getIsColor() ?
        i18n("Color") : i18n("Black and white")) + nl;

    if(ImageInfo.getCompressionLevel()){
        imageStr += i18n("Quality: ");
        switch(ImageInfo.getCompressionLevel()){
        case 1:
            imageStr += i18n("Basic");
            break;
	case 2:
	    imageStr += i18n("Normal");
	    break;
        case 4:
	    imageStr += i18n("Fine");
	    break;
        default:
	    imageStr += i18n("Unknown");
        }
        imageStr += nl;
    }

    //
    // comment info
    //

    tmpStr = ImageInfo.getComment();
    QString userStr = ImageInfo.getUserComment();
    if(!tmpStr.isEmpty() && !userStr.isEmpty()){
        commentStr += i18n("Image comment: ") + nl + tmpStr + nl;
        commentStr += i18n("User comment: ") + nl + userStr + nl;
    }
    else if(!userStr.isEmpty())
        commentStr += userStr + nl;
    else if(!tmpStr.isEmpty())
        commentStr += tmpStr + nl;

    return(true);
}

bool appendTIFFTooltipData(const char *filename, QString &imageStr,
                           QString &scannerStr, QString &commentStr,
                           bool isRTF)
{
    TIFF *t = TIFFOpen(filename, "r");
    if(!t)
        return(false);

    uint32 param32;
    uint16 pages=0, bps=0, spp=0, alpha=0, resunit=0, color=0, compression=0,
        tmp=0;
    float xres=0.0, yres=0.0;
    char *str;
    QString nl(isRTF ? "<BR>" : "\n");

    //
    // image info
    //
    TIFFGetField(t, TIFFTAG_IMAGEWIDTH, &param32);
    imageStr += i18n("Image size: %1x").arg(param32);
    TIFFGetField(t, TIFFTAG_IMAGELENGTH, &param32);
    imageStr += QString().setNum(param32) + nl;

    TIFFGetField(t, TIFFTAG_XRESOLUTION, &xres);
    TIFFGetField(t, TIFFTAG_YRESOLUTION, &yres);
    TIFFGetField(t, TIFFTAG_MATTEING, &alpha);
    TIFFGetField(t, TIFFTAG_PHOTOMETRIC, &color);
    TIFFGetField(t, TIFFTAG_PAGENUMBER, &tmp, &pages);
    TIFFGetFieldDefaulted(t, TIFFTAG_BITSPERSAMPLE, &bps);
    TIFFGetFieldDefaulted(t, TIFFTAG_SAMPLESPERPIXEL, &spp);
    TIFFGetFieldDefaulted(t, TIFFTAG_RESOLUTIONUNIT, &resunit);
    TIFFGetFieldDefaulted(t, TIFFTAG_COMPRESSION, &compression);

    imageStr += i18n("Color mode: ");
    switch(color){
    case PHOTOMETRIC_MINISWHITE:
    case PHOTOMETRIC_MINISBLACK:
        imageStr += i18n("Black and white");
        break;
    case PHOTOMETRIC_RGB:{
        if(alpha)
            imageStr += i18n("RGBA");
        else
            imageStr += i18n("RGB");
        break;
    }
    case PHOTOMETRIC_PALETTE:
        imageStr += i18n("Palette");
        break;
    case PHOTOMETRIC_MASK:
        imageStr += i18n("Alpha mask");
        break;
    case PHOTOMETRIC_SEPARATED:
        imageStr += i18n("Separated color");
        break;
    case PHOTOMETRIC_YCBCR:
        imageStr += i18n("YCbCr CCIR 60");
        break;
    case PHOTOMETRIC_CIELAB:
        imageStr += i18n("CIE Lab");
        break;
    case PHOTOMETRIC_LOGL:
        imageStr += i18n("CIE LogL");
        break;
    case PHOTOMETRIC_LOGLUV:
        imageStr += i18n("CIE LogLuv");
        break;
    default:
        imageStr += i18n("Unknown");
        break;
    }
    imageStr += ", " + i18n("Bits Per Pixel: %1").arg(bps*spp)
        + nl;

    if(resunit != RESUNIT_NONE)
    {
        if(resunit == RESUNIT_CENTIMETER){
            xres *= 2.54; yres *= 2.54;
        }
	imageStr += i18n("Resolution : %1x%2").arg((int)xres).arg((int)yres)+ nl;
    }

    imageStr += i18n("Compression: ");
    switch(compression){
    case COMPRESSION_NONE:
        imageStr += i18n("None");
        break;
    case COMPRESSION_CCITTRLE:
        imageStr += i18n("CCITT modified Huffman RLE");
        break;
    case COMPRESSION_CCITTFAX3:
        imageStr += i18n("CCITT G3 fax");
        break;
    case COMPRESSION_CCITTFAX4:
        imageStr += i18n("CCITT G4 fax");
        break;
    case COMPRESSION_LZW:
        imageStr += i18n("LZW");
        break;
    case COMPRESSION_OJPEG:
        imageStr += i18n("JPEG");
        break;
    case COMPRESSION_JPEG:
        imageStr += i18n("JPEG DCT");
        break;
    case COMPRESSION_NEXT:
        imageStr += i18n("NeXT 2-bit RLE");
        break;
    case COMPRESSION_CCITTRLEW:
        imageStr += i18n("#1 RLE word alignment");
        break;
    case COMPRESSION_PACKBITS:
        imageStr += i18n("Macintosh RLE");
        break;
    case COMPRESSION_THUNDERSCAN:
        imageStr += i18n("Thunderscan RLE");
        break;
    case COMPRESSION_IT8CTPAD:
        imageStr += i18n("IT8 padded CT");
        break;
    case COMPRESSION_IT8LW:
        imageStr += i18n("IT8 linework RLE");
        break;
    case COMPRESSION_IT8MP:
        imageStr += i18n("IT8 monochrome picture");
        break;
    case COMPRESSION_IT8BL:
        imageStr += i18n("IT8 binary lineart");
        break;
    case COMPRESSION_PIXARFILM:
        imageStr += i18n("Pixar 10bit LZW");
        break;
    case COMPRESSION_PIXARLOG:
        imageStr += i18n("Pixar 11bit Zip");
        break;
    case COMPRESSION_DEFLATE:
        imageStr += i18n("Deflate");
        break;
    case COMPRESSION_ADOBE_DEFLATE:
        imageStr += i18n("Adobe Deflate");
        break;
    case COMPRESSION_DCS:
        imageStr += i18n("Kodak DCS");
        break;
    case COMPRESSION_JBIG:
        imageStr += i18n("ISO JBIG");
        break;
    case COMPRESSION_SGILOG:
        imageStr += i18n("SGI Log Luminance RLE");
        break;
    case COMPRESSION_SGILOG24:
        imageStr += i18n("SGI Log 24bit packed");
        break;
    default:
        imageStr += i18n("Unknown");
        break;
    }
    imageStr += nl;

    if(pages && (compression==COMPRESSION_CCITTFAX3 ||
                 compression==COMPRESSION_CCITTFAX4))
        imageStr += i18n("Pages: %1").arg(pages) + nl;

    //
    // scanner info
    //
    str = NULL; TIFFGetField(t, TIFFTAG_MAKE, &str);
    if(str)
        scannerStr += i18n("Scanner make: %1").arg(str) + nl;
    str = NULL; TIFFGetField(t, TIFFTAG_MODEL, &str);
    if(str)
        scannerStr += i18n("Scanner model: %1").arg(str) + nl;
    str = NULL; TIFFGetField(t, TIFFTAG_SOFTWARE, &str);
    if(str)
        scannerStr += i18n("Software: %1").arg(str) + nl;
    str = NULL; TIFFGetField(t, TIFFTAG_DATETIME, &str);
    if(str)
        scannerStr += i18n("Image taken on: %1").arg(str) + nl;

    //
    // comment info
    //
    str = NULL; TIFFGetField(t, TIFFTAG_ARTIST, &str);
    if(str)
        commentStr += i18n("Artist: %1").arg(str) + nl;
    str = NULL; TIFFGetField(t, TIFFTAG_COPYRIGHT, &str);
    if(str)
        commentStr += i18n("Copyright: %1").arg(str) + nl;
    str = NULL; TIFFGetField(t, TIFFTAG_IMAGEDESCRIPTION, &str);
    if(str)
        commentStr += i18n("Description: %1").arg(str) + nl;

    TIFFClose(t);
    return(true);
}


