Version: 3.5.3 (using KDE KDE 3.5.3) Installed from: Unspecified Linux OS: Linux Lowres faxes (98 dpi vert. resolution) are displayed vertically queezed, similar to the behavior kimgio tiff module displays them. While this is bad for the latter, it's unacceptable for a decent fax viewer. While at it, there are issues with LZW compressed fax images, like this one: ~> tiffinfo gavi.TIF TIFF Directory at offset 0x6ffe (28670) Subfile Type: (0 = 0x0) Image Width: 1728 Image Length: 2272 Resolution: 200, 200 pixels/inch Bits/Sample: 1 Compression Scheme: LZW Photometric Interpretation: min-is-black Thresholding: bilevel art scan Samples/Pixel: 1 Rows/Strip: 56 Planar Configuration: single image plane Predictor: none 1 (0x1) kfaxview displays an error, when trying to dplay this fax: Due to patent reasons LZW (Lempel-Ziv & Welch) compressed Fax files cannot be loaded yet. :-(. All apps, which base their tiff decoding on libtiff, like konqueror(kimgio) or qfaxreader[.sf.net] get it displayed fine, but kfaxview bases its decoding on code from an app called faxview, which was last touched 1995. Indeed, up to 2004, there were patent claims from unisys for LZW. The reason, why I would like to get this fixed is, that kimgio is able to display the first page from a fax file only, while qfaxreader has currently issues with printing :-(..
SVN commit 578144 by whuss: Support LZW compressed fax files. This adds a dependency to libtiff. BUG: 131263 M +5 -1 Makefile.am A configure.in.bot M +2 -2 fax/Makefile.am A fax/configure.in.in M +82 -37 fax/faxrenderer.cpp M +6 -5 fax/faxrenderer.h --- branches/work/kviewshell-0.7/kviewshell/plugins/Makefile.am #578143:578144 @@ -2,4 +2,8 @@ KVS_PDF_SUBDIR=pdf endif -SUBDIRS = djvu dvi fax $(KVS_PDF_SUBDIR) ps +if include_TIFF +KVS_FAX_SUBDIR=fax +endif + +SUBDIRS = djvu dvi $(KVS_FAX_SUBDIR) $(KVS_PDF_SUBDIR) ps --- branches/work/kviewshell-0.7/kviewshell/plugins/fax/Makefile.am #578143:578144 @@ -17,11 +17,11 @@ kde_module_LTLIBRARIES = kfaxviewpart.la kfaxviewpart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module kfaxviewpart_la_LIBADD = -lkdeprint -lkparts $(top_builddir)/kviewshell/shell/libkmultipage.la \ - libkfaximage/libkfaximage.la + libkfaximage/libkfaximage.la $(LIBTIFF) kfaxviewpart_la_SOURCES = faxmultipage.cpp faxrenderer.cpp faxprintsettings.cpp pluginsdir = $(kde_datadir) -#plugins_DATA = +#plugins_DATA = partdesktopdir = $(kde_servicesdir) partdesktop_DATA = kvs_fax_part.desktop --- branches/work/kviewshell-0.7/kviewshell/plugins/fax/faxrenderer.cpp #578143:578144 @@ -33,7 +33,7 @@ //#define KF_DEBUG FaxRenderer::FaxRenderer(KMultiPage* _multiPage) - : DocumentRenderer(_multiPage) + : DocumentRenderer(_multiPage), fax(0) { #ifdef KF_DEBUG kdError(kvs::fax) << "FaxRenderer( parent=" << par << " )" << endl; @@ -79,7 +79,9 @@ double resolution = id.resolution; - QImage img = fax.page(id.pageNumber - 1); + QImage img = getRawImage(id.pageNumber); + if (img.isNull()) + return 0; SimplePageSize psize = pageSizes[id.pageNumber - 1]; if (psize.isValid()) @@ -123,11 +125,17 @@ QMutexLocker locker(&mutex); // If fname is the empty string, then this means: "close". - if (fname.isEmpty()) { + if (fname.isEmpty()) + { + if (fax) + { + TIFFClose(fax); + fax = 0; + } kdDebug(kvs::fax) << "FaxRenderer::setFile( ... ) called with empty filename. Closing the file." << endl; return true; } - + // Paranoid saftey checks: make sure the file actually exists, and // that it is a file, not a directory. Otherwise, show an error // message and exit.. @@ -143,51 +151,44 @@ // Now we assume that the file is fine and load the file into the // fax member. We abort on error and give an error message. - bool ok = fax.loadImage(filename); + fax = TIFFOpen(QFile::encodeName( filename ), "r"); + if (!fax) + { + KMessageBox::error(parentWidget, + i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>").arg(filename), + i18n("File Error")); - // It can happen that fax.loadImage() returns with 'ok == true', but - // still the file could NOT be loaded. This happens, e.g. for TIFF - // file that do NOT contain FAX, but other image formats. We handle - // that case here also. - if ( (!ok) || (fax.numPages() == 0)) { - // Unfortunately, it can happen that fax.loadImage() fails WITHOUT - // leaving an error message in fax.errorString(). We try to handle - // this case gracefully. - if (fax.errorString().isEmpty()) - KMessageBox::error( parentWidget, - i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>").arg(filename), - i18n("File Error")); - else - KMessageBox::detailedError( parentWidget, - i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>").arg(filename), - fax.errorString(), - i18n("File Error")); clear(); return false; } // Set the number of pages page sizes - numPages = fax.numPages(); + tdir_t dirs = TIFFNumberOfDirectories(fax); + numPages = dirs; // Set the page size for the first page in the pageSizes array. // The rest of the page sizes will be calculated on demand by the drawPage function. pageSizes.resize(numPages); Length w,h; - if (numPages != 0) { - for(Q_UINT16 pg=0; pg < numPages; pg++) { - QSize pageSize = fax.page_size(pg); - QPoint dpi = fax.page_dpi(pg); - double dpix = dpi.x(); - double dpiy = dpi.y(); + if (numPages != 0) + { + for(tdir_t pg = 0; pg < dirs; pg++) + { + if (!TIFFSetDirectory(fax, pg)) + continue; - if (dpix*dpiy < 1.0) { - kdError(kvs::fax) << "File invalid resolutions, dpi x = " << dpix << ", dpi y = " << dpiy << ". This information will be ignored and 75 DPI assumed." << endl; - dpix = dpiy = 75.0; - } + QPoint dpi = getDPI(pg + 1); - w.setLength_in_inch(pageSize.width() / dpix); - h.setLength_in_inch(pageSize.height() / dpiy); + Q_UINT32 width = 0; + Q_UINT32 height = 0; + + if (TIFFGetField(fax, TIFFTAG_IMAGEWIDTH, &width) != 1 || + TIFFGetField(fax, TIFFTAG_IMAGELENGTH, &height) != 1 ) + continue; + + w.setLength_in_inch(width / (double)dpi.x()); + h.setLength_in_inch(height / (double)dpi.y()); pageSizes[pg].setPageSize(w, h); } } @@ -199,13 +200,57 @@ QImage FaxRenderer::getRawImage(PageNumber page) { - return fax.page(page - 1); + if (!TIFFSetDirectory(fax, page - 1)) + return QImage(); + + Q_UINT32 width = 0; + Q_UINT32 height = 0; + + if (TIFFGetField(fax, TIFFTAG_IMAGEWIDTH, &width) != 1 || + TIFFGetField(fax, TIFFTAG_IMAGELENGTH, &height) != 1 ) + return QImage(); + + QImage img(width, height, 32); + Q_UINT32* data = (Q_UINT32*)img.bits(); + + if (TIFFReadRGBAImageOriented(fax, width, height, data, ORIENTATION_TOPLEFT) != 0) + { + Q_UINT32 size = width * height; + for (Q_UINT32 i = 0; i < size; ++i) + { + Q_UINT32 red = (data[i] & 0x00FF0000) >> 16; + Q_UINT32 blue = (data[i] & 0x000000FF) << 16; + data[i] = (data[i] & 0xFF00FF00) + red + blue; + } + } + else + { + return QImage(); + } + + return img; } QPoint FaxRenderer::getDPI(PageNumber page) { - return fax.page_dpi(page - 1); + if (!TIFFSetDirectory(fax, page-1)) + return QPoint(0, 0); + + float dpix = 0.0; + float dpiy = 0.0; + if (TIFFGetField(fax, TIFFTAG_XRESOLUTION, &dpix) != 1 || + TIFFGetField(fax, TIFFTAG_YRESOLUTION, &dpiy) != 1) + return QPoint(0, 0); + + kdDebug(kvs::fax) << "resolutions, dpi x = " << dpix << ", dpi y = " << dpiy << "." << endl; + + if (dpix <= 1 || dpiy <= 1) { + kdError(kvs::fax) << "File invalid resolutions, dpi x = " << dpix << ", dpi y = " << dpiy << ". This information will be ignored and 75 DPI assumed." << endl; + dpix = dpiy = 75; + } + + return QPoint((int)dpix, (int)dpiy); } #include "faxrenderer.moc" --- branches/work/kviewshell-0.7/kviewshell/plugins/fax/faxrenderer.h #578143:578144 @@ -23,12 +23,13 @@ #include "documentRenderer.h" -#include "kfaximage.h" +#include <tiffio.h> + class documentPage; /*! \brief Well-documented minimal implementation of a documentRenderer for reading FAX files - + This class provides a well-documented reference implementation of a documentRenderer, suitable as a starting point for a real-world implementation. This class is responsible for document loading and @@ -65,7 +66,7 @@ is contained in the class "KFaxImage", to keep this reference implementation short. - @param fname the name of the file that should be opened. + @param fname the name of the file that should be opened. */ virtual bool setFile(const QString& fname, const KURL &); @@ -84,8 +85,8 @@ QPoint getDPI(PageNumber page); private: - /** This class holds the fax file */ - KFaxImage fax; + /** This pointer holds the fax file */ + TIFF* fax; }; #endif
SVN commit 599807 by whuss: port of commit 578144: Support LZW compressed fax files. This adds a dependency to libtiff. CCBUG: 131263 M +7 -1 CMakeLists.txt M +2 -4 fax/CMakeLists.txt M +91 -46 fax/faxrenderer.cpp M +6 -5 fax/faxrenderer.h --- trunk/KDE/kdegraphics/kviewshell/plugins/CMakeLists.txt #599806:599807 @@ -9,8 +9,14 @@ if(FreeType2_FOUND) add_subdirectory( dvi ) endif(FreeType2_FOUND) -add_subdirectory( fax ) +macro_optional_find_package(TIFF) +if(TIFF_FOUND) + add_subdirectory( fax ) +else(TIFF_FOUND) + message(STATUS "libtiff was not found. The KViewShell FAX-plugin will not be build.") +endif(TIFF_FOUND) + macro_optional_find_package(PopplerQt4) if (POPPLER_QT4_FOUND) add_subdirectory( pdf ) --- trunk/KDE/kdegraphics/kviewshell/plugins/fax/CMakeLists.txt #599806:599807 @@ -1,9 +1,7 @@ -add_subdirectory( libkfaximage ) +include_directories( ${CMAKE_SOURCE_DIR}/kviewshell/shell ) -include_directories( ${CMAKE_SOURCE_DIR}/kviewshell/shell ${CMAKE_CURRENT_SOURCE_DIR}/libkfaximage ) - ########### next target ############### set(kfaxviewpart_PART_SRCS faxmultipage.cpp faxrenderer.cpp faxprintsettings.cpp ) @@ -14,7 +12,7 @@ kde4_install_libtool_file( ${PLUGIN_INSTALL_DIR} kfaxviewpart ) -target_link_libraries(kfaxviewpart ${KDE4_KDEPRINT_LIBS} ${KDE4_KPARTS_LIBS} kmultipage kfaximage ) +target_link_libraries(kfaxviewpart ${KDE4_KDEPRINT_LIBS} ${KDE4_KPARTS_LIBS} ${TIFF_LIBRARIES} kmultipage ) install(TARGETS kfaxviewpart DESTINATION ${PLUGIN_INSTALL_DIR} ) --- trunk/KDE/kdegraphics/kviewshell/plugins/fax/faxrenderer.cpp #599806:599807 @@ -33,7 +33,7 @@ //#define KF_DEBUG FaxRenderer::FaxRenderer(KMultiPage* _multiPage) - : DocumentRenderer(_multiPage) + : DocumentRenderer(_multiPage), fax(0) { #ifdef KF_DEBUG kError(kvs::fax) << "FaxRenderer( parent=" << par << " )" << endl; @@ -79,7 +79,9 @@ double resolution = id.resolution; - QImage img = fax.page(id.pageNumber - 1); + QImage img = getRawImage(id.pageNumber); + if (img.isNull()) + return 0; SimplePageSize psize = pageSizes[id.pageNumber - 1]; if (psize.isValid()) @@ -113,17 +115,6 @@ } -QImage FaxRenderer::getRawImage(PageNumber page) -{ - return fax.page(page - 1); -} - -QPoint FaxRenderer::getDPI(PageNumber page) -{ - return fax.page_dpi(page - 1); -} - - bool FaxRenderer::setFile(const QString &fname, const KUrl &) { #ifdef KF_DEBUG @@ -134,11 +125,17 @@ QMutexLocker locker(&mutex); // If fname is the empty string, then this means: "close". - if (fname.isEmpty()) { + if (fname.isEmpty()) + { + if (fax) + { + TIFFClose(fax); + fax = 0; + } kDebug(kvs::fax) << "FaxRenderer::setFile( ... ) called with empty filename. Closing the file." << endl; return true; } - + // Paranoid saftey checks: make sure the file actually exists, and // that it is a file, not a directory. Otherwise, show an error // message and exit.. @@ -154,51 +151,44 @@ // Now we assume that the file is fine and load the file into the // fax member. We abort on error and give an error message. - bool ok = fax.loadImage(filename); + fax = TIFFOpen(QFile::encodeName( filename ), "r"); + if (!fax) + { + KMessageBox::error(parentWidget, + i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>").arg(filename), + i18n("File Error")); - // It can happen that fax.loadImage() returns with 'ok == true', but - // still the file could NOT be loaded. This happens, e.g. for TIFF - // file that do NOT contain FAX, but other image formats. We handle - // that case here also. - if ( (!ok) || (fax.numPages() == 0)) { - // Unfortunately, it can happen that fax.loadImage() fails WITHOUT - // leaving an error message in fax.errorString(). We try to handle - // this case gracefully. - if (fax.errorString().isEmpty()) - KMessageBox::error( parentWidget, - i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>", filename), - i18n("File Error")); - else - KMessageBox::detailedError( parentWidget, - i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>", filename), - fax.errorString(), - i18n("File Error")); clear(); return false; } // Set the number of pages page sizes - numPages = fax.numPages(); + tdir_t dirs = TIFFNumberOfDirectories(fax); + numPages = dirs; // Set the page size for the first page in the pageSizes array. // The rest of the page sizes will be calculated on demand by the drawPage function. pageSizes.resize(numPages); Length w,h; - if (numPages != 0) { - for(quint16 pg=0; pg < numPages; pg++) { - QSize pageSize = fax.page_size(pg); - QPoint dpi = fax.page_dpi(pg); - double dpix = dpi.x(); - double dpiy = dpi.y(); + if (numPages != 0) + { + for(tdir_t pg = 0; pg < dirs; pg++) + { + if (!TIFFSetDirectory(fax, pg)) + continue; - if (dpix*dpiy < 1.0) { - kError(kvs::fax) << "File invalid resolutions, dpi x = " << dpix << ", dpi y = " << dpiy << ". This information will be ignored and 75 DPI assumed." << endl; - dpix = dpiy = 75.0; - } + QPoint dpi = getDPI(pg + 1); - w.setLength_in_inch(pageSize.width() / dpix); - h.setLength_in_inch(pageSize.height() / dpiy); + quint32 width = 0; + quint32 height = 0; + + if (TIFFGetField(fax, TIFFTAG_IMAGEWIDTH, &width) != 1 || + TIFFGetField(fax, TIFFTAG_IMAGELENGTH, &height) != 1 ) + continue; + + w.setLength_in_inch(width / (double)dpi.x()); + h.setLength_in_inch(height / (double)dpi.y()); pageSizes[pg].setPageSize(w, h); } } @@ -208,4 +198,59 @@ } +QImage FaxRenderer::getRawImage(PageNumber page) +{ + if (!TIFFSetDirectory(fax, page - 1)) + return QImage(); + + quint32 width = 0; + quint32 height = 0; + + if (TIFFGetField(fax, TIFFTAG_IMAGEWIDTH, &width) != 1 || + TIFFGetField(fax, TIFFTAG_IMAGELENGTH, &height) != 1 ) + return QImage(); + + QImage img(width, height, 32); + quint32* data = (quint32*)img.bits(); + + if (TIFFReadRGBAImageOriented(fax, width, height, data, ORIENTATION_TOPLEFT) != 0) + { + quint32 size = width * height; + for (quint32 i = 0; i < size; ++i) + { + Q_UINT32 red = (data[i] & 0x00FF0000) >> 16; + Q_UINT32 blue = (data[i] & 0x000000FF) << 16; + data[i] = (data[i] & 0xFF00FF00) + red + blue; + } + } + else + { + return QImage(); + } + + return img; +} + + +QPoint FaxRenderer::getDPI(PageNumber page) +{ + if (!TIFFSetDirectory(fax, page-1)) + return QPoint(0, 0); + + float dpix = 0.0; + float dpiy = 0.0; + if (TIFFGetField(fax, TIFFTAG_XRESOLUTION, &dpix) != 1 || + TIFFGetField(fax, TIFFTAG_YRESOLUTION, &dpiy) != 1) + return QPoint(0, 0); + + kDebug(kvs::fax) << "resolutions, dpi x = " << dpix << ", dpi y = " << dpiy << "." << endl; + + if (dpix <= 1 || dpiy <= 1) { + kError(kvs::fax) << "File invalid resolutions, dpi x = " << dpix << ", dpi y = " << dpiy << ". This information will be ignored and 75 DPI assumed." << endl; + dpix = dpiy = 75; + } + + return QPoint((int)dpix, (int)dpiy); +} + #include "faxrenderer.moc" --- trunk/KDE/kdegraphics/kviewshell/plugins/fax/faxrenderer.h #599806:599807 @@ -24,12 +24,13 @@ #include <qimage.h> #include "documentRenderer.h" -#include "kfaximage.h" +#include <tiffio.h> + class documentPage; /*! \brief Well-documented minimal implementation of a documentRenderer for reading FAX files - + This class provides a well-documented reference implementation of a documentRenderer, suitable as a starting point for a real-world implementation. This class is responsible for document loading and @@ -66,7 +67,7 @@ is contained in the class "KFaxImage", to keep this reference implementation short. - @param fname the name of the file that should be opened. + @param fname the name of the file that should be opened. */ virtual bool setFile(const QString& fname, const KUrl &); @@ -85,8 +86,8 @@ QPoint getDPI(PageNumber page); private: - /** This class holds the fax file */ - KFaxImage fax; + /** This pointer holds the fax file */ + TIFF* fax; }; #endif