diff --git a/src/add-ons/print/drivers/pdf/source/ImageCache.cpp b/src/add-ons/print/drivers/pdf/source/ImageCache.cpp new file mode 100644 index 0000000000..b2959311d0 --- /dev/null +++ b/src/add-ons/print/drivers/pdf/source/ImageCache.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageCache.h" + + +// Implementation of Image + +Image::Image(int imageID, const char* fileName, int width, int height, color_space colorSpace, int mask) + : fImageID(imageID) + , fFileName(fileName) + , fWidth(width) + , fHeight(height) + , fColorSpace(colorSpace) + , fMask(mask) +{ +} + +bool Image::Equals(BBitmap* bitmap) const { + BBitmap* bm = BTranslationUtils::GetBitmapFile(FileName()); + bool equals = false; + if (bm) { + bm->Lock(); + bitmap->Lock(); + + equals = bm->BitsLength() == bitmap->BitsLength() && + bm->ColorSpace() == bitmap->ColorSpace() && + memcmp(bm->Bits(), bitmap->Bits(), bm->BitsLength()) == 0; + + bitmap->Unlock(); + bm->Unlock(); + delete bm; + } + return equals; +} + + +// Implementation of ImageReference + +ImageReference::ImageReference(Image* image) + : fImage(image) +{ +} + + +// Implementation of ImageCache + +const char* kTemporaryPath = "/tmp/PDFWriter"; +const char* kImageCachePath = "/tmp/PDFWriter/Cache"; +const char* kImagePathPrefix = "/tmp/PDFWriter/Cache/Image"; + +ImageCache::ImageCache() + : fNextID(0) +{ + mkdir(kTemporaryPath, 0777); + mkdir(kImageCachePath, 0777); +} + +ImageCache::~ImageCache() { + rmdir(kImageCachePath); +} + +void ImageCache::Flush(PDF* pdf) { + for (int32 i = 0; i < fCache.CountItems(); i ++) { + CachedImage* image = fCache.ItemAt(i); + if (dynamic_cast(image) != NULL) { + PDF_close_image(pdf, image->ImageID()); + unlink(image->FileName()); + } + } +} + +int ImageCache::GetImage(PDF* pdf, BBitmap* bitmap, int mask) { + int id = fNextID ++; + CachedImage* image = fCache.ItemAt(id); + // In 2. pass for each bitmap an entry exists + if (image != NULL) { + return image->ImageID(); + } + // In 1. pass we create an entry for each bitmap + image = Find(bitmap, mask); + // Bitmap not in cache + if (image == NULL) { + image = Store(pdf, id, bitmap, mask); + } else { + Image* im = dynamic_cast(image); + ASSERT(im != NULL); + image = new ImageReference(im); + } + + if (image == NULL) return -1; // error occured + + fCache.AddItem(image); + ASSERT(fCache.CountItems() == fNextID); + return image->ImageID(); +} + +CachedImage* ImageCache::Find(BBitmap* bitmap, int mask) { + int w, h; + color_space cs; + bitmap->Lock(); + w = bitmap->Bounds().IntegerWidth()+1; + h = bitmap->Bounds().IntegerHeight()+1; + cs = bitmap->ColorSpace(); + bitmap->Unlock(); + + for (int32 i = 0; i < fCache.CountItems(); i ++) { + CachedImage* image = fCache.ItemAt(i); + if (dynamic_cast(image) != NULL) continue; + if (w != image->Width() || h != image->Height() || + cs != image->ColorSpace() || + mask != image->Mask()) continue; + if (image->Equals(bitmap)) { + return image; + } + } + return NULL; +} + +CachedImage* ImageCache::Store(PDF* pdf, int id, BBitmap* bitmap, int mask) { + BString fileName(kImagePathPrefix); + fileName << id << ".png"; + int w, h; + color_space cs; + bitmap->Lock(); + w = bitmap->Bounds().IntegerWidth()+1; + h = bitmap->Bounds().IntegerHeight()+1; + cs = bitmap->ColorSpace(); + bitmap->Unlock(); + + if (!StoreBitmap(fileName.String(), bitmap)) return NULL; + + int image; + image = PDF_open_image_file(pdf, "png", fileName.String(), + mask == -1 ? "" : "masked", mask == -1 ? 0 : mask); + if (image < 0) return NULL; + + return new Image(image, fileName.String(), w, h, cs, mask); +} + +bool ImageCache::StoreBitmap(const char* fileName, BBitmap* bitmap) { + bool ok; + BTranslatorRoster *roster = BTranslatorRoster::Default(); + BBitmapStream stream(bitmap); // init with contents of bitmap + BFile file(fileName, B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE); + ok = roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT) == B_OK; + BBitmap *bm = NULL; stream.DetachBitmap(&bm); // otherwise bitmap destructor crashes here! + ASSERT(bm == bitmap); + return ok; +} \ No newline at end of file diff --git a/src/add-ons/print/drivers/pdf/source/ImageCache.h b/src/add-ons/print/drivers/pdf/source/ImageCache.h new file mode 100644 index 0000000000..94596dc1fe --- /dev/null +++ b/src/add-ons/print/drivers/pdf/source/ImageCache.h @@ -0,0 +1,83 @@ +#ifndef _IMAGE_CACHE_H +#define _IMAGE_CACHE_H + +#include +#include +#include + +#include "pdflib.h" +#include "Utils.h" + +/* + +*/ + +class CachedImage { +public: + CachedImage() {}; + virtual ~CachedImage() {}; + virtual int ImageID() const = 0; + virtual const char* FileName() const = 0; + virtual int Width() const = 0; + virtual int Height() const = 0; + virtual color_space ColorSpace() const = 0; + virtual int Mask() const = 0; + virtual bool Equals(BBitmap* bitmap) const = 0; +}; + +class Image : public CachedImage { +public: + Image(int imageID, const char* fileName, int width, int height, color_space colorSpace, int mask); + + int ImageID() const { return fImageID; }; + const char* FileName() const { return fFileName.String(); }; + int Width() const { return fWidth; }; + int Height() const { return fHeight; }; + color_space ColorSpace() const { return fColorSpace; }; + int Mask() const { return fMask; }; + bool Equals(BBitmap* bitmap) const; + +private: + int fImageID; + BString fFileName; + int fWidth, fHeight; + color_space fColorSpace; + int fMask; +}; + +class ImageReference : public CachedImage { +public: + ImageReference(Image* image); + + int ImageID() const { return fImage->ImageID(); }; + const char* FileName() const { return fImage->FileName(); }; + int Width() const { return fImage->Width(); }; + int Height() const { return fImage->Height(); }; + color_space ColorSpace() const { return fImage->ColorSpace(); }; + int Mask() const { return fImage->Mask(); }; + bool Equals(BBitmap* bitmap) const { return fImage->Equals(bitmap); } + +private: + Image* fImage; +}; + +class ImageCache { +public: + ImageCache(); + ~ImageCache(); + void Flush(PDF* pdf); + + void ResetID() { fNextID = 0; } + int GetImage(PDF* pdf, BBitmap* bitmap, int mask); + +private: + CachedImage* Find(BBitmap* bitmap, int mask); + CachedImage* Store(PDF* pdf, int id, BBitmap* bitmap, int mask); + bool StoreBitmap(const char* fileName, BBitmap* bitmap); + + int fNextID; + TList fCache; +}; + +#endif + diff --git a/src/add-ons/print/drivers/pdf/source/PDFWriter.cpp b/src/add-ons/print/drivers/pdf/source/PDFWriter.cpp index 24d11e5e1a..03ee208ecd 100644 --- a/src/add-ons/print/drivers/pdf/source/PDFWriter.cpp +++ b/src/add-ons/print/drivers/pdf/source/PDFWriter.cpp @@ -117,10 +117,12 @@ PDFWriter::PrintPage(int32 pageNumber, int32 pageCount) fPage = pageNumber; if (pageNumber == 1) { - if (MakesPattern()) + if (MakesPattern()) { REPORT(kDebug, fPage, ">>>>> Collecting patterns..."); - else if (MakesPDF()) + } else if (MakesPDF()) { REPORT(kDebug, fPage, ">>>>> Generating PDF..."); + fImageCache.ResetID(); + } } paperRect = JobMsg()->FindRect("paper_rect"); @@ -229,7 +231,8 @@ PDFWriter::EndJob() fprintf(fLog, ": %s\n", rr->Desc()); } #endif - + fImageCache.Flush(fPdf); + PDF_close(fPdf); REPORT(kDebug, 0, ">>>> PDF_close"); @@ -262,12 +265,14 @@ PDFWriter::InitWriter() fStateDepth = 0; // pdflib scope: object +/* const char* license_key; if (JobMsg()->FindString("pdflib_license_key", &license_key) == B_OK && license_key[0] != 0) { REPORT(kDebug, 0, "license key found %s!", license_key); PDF_set_parameter(fPdf, "license", license_key); } +*/ fPDFVersion = kPDF13; const char * compatibility; @@ -278,6 +283,7 @@ PDFWriter::InitWriter() else if (strcmp(compatibility, "1.5") == 0) fPDFVersion = kPDF15; } +/* // set user/master password BString master_password, user_password; if (JobMsg()->FindString("master_password", &master_password) == B_OK && @@ -293,6 +299,7 @@ PDFWriter::InitWriter() permissions.Length() > 0) { PDF_set_parameter(fPdf, "permissions", permissions.String()); } +*/ REPORT(kDebug, 0, ">>>> PDF_open_mem"); PDF_open_mem(fPdf, _WriteData); // use callback to stream PDF document data to printer transport @@ -791,14 +798,18 @@ PDFWriter::CreatePattern() int pattern = PDF_begin_pattern(fPdf, 8, 8, 8, 8, 1); if (pattern == -1) { REPORT(kError, fPage, "CreatePattern could not create pattern"); +#if !USE_IMAGE_CACHE PDF_close_image(fPdf, image); +#endif if (mask != -1) PDF_close_image(fPdf, mask); return; } PDF_setcolor(fPdf, "both", "rgb", 0, 0, 1, 0); PDF_place_image(fPdf, image, 0, 0, 1); PDF_end_pattern(fPdf); +#if !USE_IMAGE_CACHE PDF_close_image(fPdf, image); +#endif if (mask != -1) PDF_close_image(fPdf, mask); #endif @@ -1612,8 +1623,13 @@ PDFWriter::GetImages(BRect src, int32 /*width*/, int32 /*height*/, int32 bytesPe } if (mask) { - *maskId = PDF_open_image(fPdf, "raw", "memory", (const char *) mask, length, width, height, 1, bpc, "mask"); +// *maskId = PDF_open_image(fPdf, "raw", "memory", (const char *) mask, length, width, height, 1, bpc, "mask"); + BString options; + PDF_create_pvf(fPdf, "mask", 0, mask, length, NULL); + options << "width " << width << " height " << height << " components 1 bpc " << bpc; + *maskId = PDF_load_image(fPdf, "raw", "mask", 0, options.String()); delete []mask; + PDF_delete_pvf(fPdf, "mask", 0); } BBitmap * bm = ConvertBitmap(src, bytesPerRow, pixelFormat, flags, data); @@ -1623,6 +1639,10 @@ PDFWriter::GetImages(BRect src, int32 /*width*/, int32 /*height*/, int32 bytesPe return false; } +#if USE_IMAGE_CACHE + *image = fImageCache.GetImage(fPdf, bm, *maskId); + delete bm; +#else char *pdfLibFormat = "png"; char *bitmapFileName = "/tmp/pdfwriter.png"; const uint32 beosFormat = B_PNG_FORMAT; @@ -1637,6 +1657,7 @@ PDFWriter::GetImages(BRect src, int32 /*width*/, int32 /*height*/, int32 bytesPe *image = PDF_open_image_file(fPdf, pdfLibFormat, bitmapFileName, *maskId == -1 ? "" : "masked", *maskId == -1 ? 0 : *maskId); +#endif return *image >= 0; } @@ -2111,7 +2132,6 @@ PDFWriter::DrawPixels(BRect src, BRect dest, int32 width, int32 height, int32 by width, height, bytesPerRow, pixelFormat, flags, data); SetColor(); - if (!MakesPDF()) return; if (IsClipping()) { REPORT(kError, fPage, "DrawPixels for clipping not implemented yet!"); @@ -2123,6 +2143,7 @@ PDFWriter::DrawPixels(BRect src, BRect dest, int32 width, int32 height, int32 by if (!GetImages(src, width, height, bytesPerRow, pixelFormat, flags, data, &maskId, &image)) { return; } + if (!MakesPDF()) return; const float scaleX = (dest.Width()+1) / (src.Width()+1); const float scaleY = (dest.Height()+1) / (src.Height()+1); @@ -2146,7 +2167,9 @@ PDFWriter::DrawPixels(BRect src, BRect dest, int32 width, int32 height, int32 by if ( image >= 0 ) { PDF_place_image(fPdf, image, x, y, scale(1.0)); +#if !USE_IMAGE_CACHE PDF_close_image(fPdf, image); +#endif } else REPORT(kError, fPage, "PDF_open_image_file failed!"); diff --git a/src/add-ons/print/drivers/pdf/source/PDFWriter.h b/src/add-ons/print/drivers/pdf/source/PDFWriter.h index 945d134a93..79abb230fc 100644 --- a/src/add-ons/print/drivers/pdf/source/PDFWriter.h +++ b/src/add-ons/print/drivers/pdf/source/PDFWriter.h @@ -45,9 +45,12 @@ THE SOFTWARE. #include "SubPath.h" #include "Utils.h" #include "Link.h" +#include "ImageCache.h" #include "pdflib.h" +#define USE_IMAGE_CACHE 1 + #define RAD2DEGREE(r) (180.0 * r / PI) #define DEGREE2RAD(d) (PI * d / 180.0) @@ -310,6 +313,7 @@ class PDFWriter : public PrinterDriver, public PictureIterator TList fPatterns; TList fTransparencyCache; TList fTransparencyStack; + ImageCache fImageCache; int64 fEmbedMaxFontSize; BScreen *fScreen; Fonts *fFonts;