This source file includes following definitions.
- IsDJVU
- pump_data
- pump_data_until_message
- message_tag_name
- process_message
- get_page_image
- get_page_line
- MagickMax
- ReadOneDJVUImage
- djvu_close_lc
- ReadDJVUImage
- RegisterDJVUImage
- UnregisterDJVUImage
#include "magick/studio.h"
#include "magick/blob.h"
#include "magick/blob-private.h"
#include "magick/cache.h"
#include "magick/colormap.h"
#include "magick/constitute.h"
#include "magick/exception.h"
#include "magick/exception-private.h"
#include "magick/list.h"
#include "magick/magick.h"
#include "magick/memory_.h"
#include "magick/monitor.h"
#include "magick/monitor-private.h"
#include "magick/pixel-accessor.h"
#include "magick/quantum-private.h"
#include "magick/static.h"
#include "magick/string_.h"
#include "magick/module.h"
#if defined(MAGICKCORE_DJVU_DELEGATE)
#include <libdjvu/ddjvuapi.h>
#endif
static MagickBooleanType IsDJVU(const unsigned char *magick,const size_t length)
{
if (length < 8)
return(MagickFalse);
if (memcmp(magick,"AT&TFORM",8) == 0)
return(MagickTrue);
return(MagickFalse);
}
#if defined(MAGICKCORE_DJVU_DELEGATE)
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
typedef struct _LoadContext
LoadContext;
struct _LoadContext
{
ddjvu_context_t* context;
ddjvu_document_t *document;
ddjvu_page_t *page;
int streamid;
int pages;
Image *image;
};
#define BLOCKSIZE 65536
#if 0
static void
pump_data(Image *image, LoadContext* lc)
{
int blocksize = BLOCKSIZE;
char data[BLOCKSIZE];
int size;
while ((size = (size_t) ReadBlob(image,(size_t) blocksize,data)) == blocksize) {
ddjvu_stream_write(lc->document, lc->streamid, data, size);
}
if (size)
ddjvu_stream_write(lc->document, lc->streamid, data, size);
ddjvu_stream_close(lc->document, lc->streamid, 0);
}
#endif
static ddjvu_message_t*
pump_data_until_message(LoadContext *lc,Image *image)
{
size_t blocksize = BLOCKSIZE;
unsigned char data[BLOCKSIZE];
size_t size;
ddjvu_message_t *message;
size=0;
while (!(message = ddjvu_message_peek(lc->context))
&& (size = (size_t) ReadBlob(image,(size_t) blocksize,data)) == blocksize) {
ddjvu_stream_write(lc->document, lc->streamid, (char *) data, size);
}
if (message)
return message;
if (size)
ddjvu_stream_write(lc->document, lc->streamid, (char *) data, size);
ddjvu_stream_close(lc->document, lc->streamid, 0);
return NULL;
}
#define DEBUG 0
#if DEBUG
static const char*
message_tag_name(ddjvu_message_tag_t tag)
{
static char* names[] =
{
"ERROR",
"INFO",
"NEWSTREAM",
"DOCINFO",
"PAGEINFO",
"RELAYOUT",
"REDISPLAY",
"CHUNK",
"THUMBNAIL",
"PROGRESS",
};
if (tag <= DDJVU_PROGRESS)
return names[tag];
else {
return 0;
}
}
#endif
int
process_message(ddjvu_message_t *message)
{
#if 0
ddjvu_context_t* context= message->m_any.context;
#endif
if (! message)
return(-1);
#if DEBUG
printf("*** %s: %s.\n",__FUNCTION__, message_tag_name(message->m_any.tag));
#endif
switch (message->m_any.tag){
case DDJVU_DOCINFO:
{
ddjvu_document_t* document= message->m_any.document;
LoadContext *lc = (LoadContext *) ddjvu_document_get_user_data(document);
lc->pages = ddjvu_document_get_pagenum(document);
#if DEBUG
printf("the doc has %d pages\n", ddjvu_document_get_pagenum(document));
#endif
break;
}
case DDJVU_CHUNK:
#if DEBUG
printf("the name of the chunk is: %s\n", message->m_chunk.chunkid);
#endif
break;
case DDJVU_RELAYOUT:
case DDJVU_PAGEINFO:
{
#if 0
ddjvu_page_t* page = message->m_any.page;
page_info* info = ddjvu_page_get_user_data(page);
printf("page decoding status: %d %s%s%s\n",
ddjvu_page_decoding_status(page),
status_color, status_name(ddjvu_page_decoding_status(page)), color_reset);
printf("the page LAYOUT changed: width x height: %d x %d @ %d dpi. Version %d, type %d\n",
ddjvu_page_get_width(page),
ddjvu_page_get_height(page),
ddjvu_page_get_resolution(page),
ddjvu_page_get_version(page),
ddjvu_page_get_type(page));
info->info = 1;
#endif
break;
}
case DDJVU_REDISPLAY:
{
#if 0
ddjvu_page_t* page = message->m_any.page;
page_info* info = ddjvu_page_get_user_data(page);
printf("the page can/should be REDISPLAYED\n");
info->display = 1;
#endif
break;
}
case DDJVU_PROGRESS:
#if DEBUG
printf("PROGRESS:\n");
#endif
break;
case DDJVU_ERROR:
printf("simply ERROR!\n message:\t%s\nfunction:\t%s(file %s)\nlineno:\t%d\n",
message->m_error.message,
message->m_error.function,
message->m_error.filename,
message->m_error.lineno);
break;
case DDJVU_INFO:
#if DEBUG
printf("INFO: %s!\n", message->m_info.message);
#endif
break;
default:
printf("unexpected\n");
};
return(message->m_any.tag);
}
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#define RGB 1
static void
get_page_image(LoadContext *lc, ddjvu_page_t *page, int x, int y, int w, int h, const ImageInfo *image_info ) {
ddjvu_format_t
*format;
ddjvu_page_type_t
type;
Image
*image;
int
ret,
stride;
unsigned char
*q;
ddjvu_rect_t rect;
rect.x = x;
rect.y = y;
rect.w = (unsigned int) w;
rect.h = (unsigned int) h;
image = lc->image;
type = ddjvu_page_get_type(lc->page);
stride = (type == DDJVU_PAGETYPE_BITONAL)?
(image->columns + 7)/8 : image->columns *3;
q = (unsigned char *) AcquireQuantumMemory(image->rows,stride);
if (q == (unsigned char *) NULL)
return;
format = ddjvu_format_create(
(type == DDJVU_PAGETYPE_BITONAL)?DDJVU_FORMAT_LSBTOMSB : DDJVU_FORMAT_RGB24,
0, NULL);
#if 0
if (format == NULL)
{
abort();
}
#endif
ddjvu_format_set_row_order(format, 1);
ddjvu_format_set_y_direction(format, 1);
ret = ddjvu_page_render(page,
DDJVU_RENDER_COLOR,
&rect,
&rect,
format,
stride,
(char*)q);
(void) ret;
ddjvu_format_release(format);
if (type == DDJVU_PAGETYPE_BITONAL) {
#if DEBUG
printf("%s: expanding BITONAL page/image\n", __FUNCTION__);
#endif
register IndexPacket *indexes;
size_t bit, byte;
for (y=0; y < (ssize_t) image->rows; y++)
{
PixelPacket * o = QueueAuthenticPixels(image,0,y,image->columns,1,&image->exception);
if (o == (PixelPacket *) NULL)
break;
indexes=GetAuthenticIndexQueue(image);
bit=0;
byte=0;
for (x= 0; x < (ssize_t) image->columns; x++)
{
if (bit == 0) byte= (size_t) q[(y * stride) + (x / 8)];
if (indexes != (IndexPacket *) NULL)
SetPixelIndex(indexes+x,(IndexPacket) (((byte & 0x01) != 0) ? 0x00 : 0x01));
bit++;
if (bit == 8)
bit=0;
byte>>=1;
}
if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
break;
}
if (image->ping == MagickFalse)
SyncImage(image);
} else {
#if DEBUG
printf("%s: expanding PHOTO page/image\n", __FUNCTION__);
#endif
ssize_t i;
#if 0
char* r;
#else
register PixelPacket *r;
unsigned char *s;
#endif
s=q;
for (i = 0;i< (ssize_t) image->rows; i++)
{
#if DEBUG
if (i % 1000 == 0) printf("%d\n",i);
#endif
r = QueueAuthenticPixels(image,0,i,image->columns,1,&image->exception);
if (r == (PixelPacket *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
SetPixelRed(r,ScaleCharToQuantum(*s++));
SetPixelGreen(r,ScaleCharToQuantum(*s++));
SetPixelBlue(r,ScaleCharToQuantum(*s++));
r++;
}
(void) SyncAuthenticPixels(image,&image->exception);
}
}
q=(unsigned char *) RelinquishMagickMemory(q);
}
#if defined(MAGICKCORE_DJVU_DELEGATE)
#if 0
static int
get_page_line(LoadContext *lc, int row, QuantumInfo* quantum_info)
{
ddjvu_format_t
*format;
int
ret;
size_t
stride;
unsigned char
*q;
ddjvu_rect_t rect, pagerect;
rect.x = 0;
rect.y = row;
rect.w = lc->image->columns;
rect.h = 1;
pagerect.x = 0;
pagerect.y = 0;
pagerect.w = lc->image->columns;
pagerect.h = lc->image->rows;
format = ddjvu_format_create(
#if RGB
DDJVU_FORMAT_RGB24
#else
DDJVU_FORMAT_GREY8
#endif
,
0, NULL);
ddjvu_format_set_row_order(format, 1);
ddjvu_format_set_y_direction(format, 1);
stride=1;
#if RGB
stride=3;
#endif
q = (unsigned char *) AcquireQuantumMemory(lc->image->columns,stride);
ret = ddjvu_page_render(lc->page,
DDJVU_RENDER_COLOR,
&pagerect,
&rect,
format,
pagerect.w * 3,
(char*)q);
ImportQuantumPixels(lc->image,
(CacheView *) NULL,
quantum_info,
#if RGB
RGBQuantum
#else
GrayQuantum
#endif
,q,&lc->image->exception);
q=(unsigned char *) RelinquishMagickMemory(q);
ddjvu_format_release(format);
return ret;
}
#endif
#endif
static inline double MagickMax(const double x,const double y)
{
if (x > y)
return(x);
return(y);
}
static Image *ReadOneDJVUImage(LoadContext* lc,const int pagenum,
const ImageInfo *image_info,ExceptionInfo *exception)
{
ddjvu_page_type_t
type;
ddjvu_pageinfo_t info;
ddjvu_message_t *message;
Image *image;
int logging;
int tag;
MagickBooleanType status;
image = lc->image;
logging=LogMagickEvent(CoderEvent,GetMagickModule(), " enter ReadOneDJVUImage()");
(void) logging;
#if DEBUG
printf("==== Loading the page %d\n", pagenum);
#endif
lc->page = ddjvu_page_create_by_pageno(lc->document, pagenum);
tag=(-1);
do {
while ((message = ddjvu_message_peek(lc->context)))
{
tag=process_message(message);
if (tag == 0) break;
ddjvu_message_pop(lc->context);
}
message = pump_data_until_message(lc,image);
if (message)
do {
tag=process_message(message);
if (tag == 0) break;
ddjvu_message_pop(lc->context);
} while ((message = ddjvu_message_peek(lc->context)));
} while (!ddjvu_page_decoding_done(lc->page));
ddjvu_document_get_pageinfo(lc->document, pagenum, &info);
image->x_resolution = (float) info.dpi;
image->y_resolution =(float) info.dpi;
if (image_info->density != (char *) NULL)
{
int
flags;
GeometryInfo
geometry_info;
flags=ParseGeometry(image_info->density,&geometry_info);
image->x_resolution=geometry_info.rho;
image->y_resolution=geometry_info.sigma;
if ((flags & SigmaValue) == 0)
image->y_resolution=image->x_resolution;
info.width=(int) (info.width*image->x_resolution/info.dpi);
info.height=(int) (info.height*image->y_resolution/info.dpi);
info.dpi=(int) MagickMax(image->x_resolution,image->y_resolution);
}
type = ddjvu_page_get_type(lc->page);
image->columns=(size_t) info.width;
image->rows=(size_t) info.height;
if (type == DDJVU_PAGETYPE_BITONAL){
image->colorspace = GRAYColorspace;
image->storage_class = PseudoClass;
image->depth = 8UL;
image->colors= 2;
if (AcquireImageColormap(image,image->colors) == MagickFalse)
ThrowReaderException(ResourceLimitError,
"MemoryAllocationFailed");
} else {
image->colorspace = RGBColorspace;
image->storage_class = DirectClass;
image->depth = 8UL;
image->matte = MagickTrue;
}
status=SetImageExtent(image,image->columns,image->rows);
if (status == MagickFalse)
{
InheritException(exception,&image->exception);
return(DestroyImageList(image));
}
#if DEBUG
printf("now filling %.20g x %.20g\n",(double) image->columns,(double)
image->rows);
#endif
#if 1
get_page_image(lc, lc->page, 0, 0, info.width, info.height, image_info);
#else
int i;
for (i = 0;i< image->rows; i++)
{
printf("%d\n",i);
q = QueueAuthenticPixels(image,0,i,image->columns,1);
get_page_line(lc, i, quantum_info);
SyncAuthenticPixels(image);
}
#endif
#if DEBUG
printf("END: finished filling %.20g x %.20g\n",(double) image->columns,
(double) image->rows);
#endif
if (!image->ping)
SyncImage(image);
if (lc->page) {
ddjvu_page_release(lc->page);
lc->page = NULL;
}
if (tag == 0)
image=DestroyImage(image);
return image;
}
#if 0
if (AcquireImageColormap(image,2) == MagickFalse)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
image->colormap[0].red=QuantumRange;
image->colormap[0].green=QuantumRange;
image->colormap[0].blue=QuantumRange;
image->colormap[1].red=0;
image->colormap[1].green=0;
image->colormap[1].blue=0;
#endif
static void djvu_close_lc(LoadContext* lc)
{
if (lc->document)
ddjvu_document_release(lc->document);
if (lc->context)
ddjvu_context_release(lc->context);
if (lc->page)
ddjvu_page_release(lc->page);
RelinquishMagickMemory(lc);
}
static Image *ReadDJVUImage(const ImageInfo *image_info,
ExceptionInfo *exception)
{
const char
*url;
ddjvu_message_t
*message;
Image
*image,
*images;
int
logging,
use_cache;
LoadContext
*lc;
MagickBooleanType
status;
register ssize_t
i;
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickSignature);
if (image_info->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", image_info->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
logging = LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadDJVUImage()");
(void) logging;
image = AcquireImage(image_info);
lc = (LoadContext *) NULL;
status = OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == MagickFalse)
ThrowReaderException(FileOpenError,"UnableToOpenFile");
#if 0
count = ReadBlob(image,8,(unsigned char *) magic_number);
if (memcmp(magic_number,"AT&TFORM",8) != 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
#endif
lc = (LoadContext *) AcquireMagickMemory(sizeof(*lc));
if (lc == NULL)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
(void) ResetMagickMemory(lc,0,sizeof(LoadContext));
lc->image = image;
lc->pages = 0;
lc->context = ddjvu_context_create("ImageMagick djvu loader");
ddjvu_cache_set_size(lc->context, 1);
use_cache = 0;
url="http://www.imagemagick.org/fake.djvu";
lc->document = ddjvu_document_create(lc->context, url, use_cache);
ddjvu_document_set_user_data(lc->document, lc);
message = ddjvu_message_wait(lc->context);
if (message->m_any.tag != DDJVU_NEWSTREAM) {
ddjvu_document_release(lc->document);
ddjvu_context_release(lc->context);
RelinquishMagickMemory(lc);
ThrowReaderException(ResourceLimitError,"Djvu initial message: unexpected type");
return NULL;
};
lc->streamid = message->m_newstream.streamid;
ddjvu_message_pop(lc->context);
message = pump_data_until_message(lc,image);
if (message) do {
process_message(message);
ddjvu_message_pop(lc->context);
} while ((message = ddjvu_message_peek(lc->context)));
while (lc->pages == 0) {
message = ddjvu_message_wait(lc->context);
process_message(message);
ddjvu_message_pop(lc->context);
}
images=NewImageList();
i=0;
if (image_info->number_scenes != 0)
i=image_info->scene;
for ( ; i < (ssize_t) lc->pages; i++)
{
image=ReadOneDJVUImage(lc,i,image_info,exception);
if (image == (Image *) NULL)
break;
image->scene=i;
AppendImageToList(&images,CloneImageList(image,exception));
images->extent=GetBlobSize(image);
if (image_info->number_scenes != 0)
if (image->scene >= (image_info->scene+image_info->number_scenes-1))
break;
}
djvu_close_lc(lc);
(void) CloseBlob(images);
if (image != (Image *) NULL)
image=DestroyImageList(image);
#if 0
if ((image->page.width == 0) && (image->page.height == 0))
{
image->page.width = image->columns+image->page.x;
image->page.height = image->rows+image->page.y;
}
if (image->columns == 0 || image->rows == 0)
{
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
"exit ReadDJVUImage() with error.");
ThrowReaderException(CorruptImageError,"CorruptImage");
}
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadDJVUImage()");
#endif
return(GetFirstImageInList(images));
}
#endif
ModuleExport size_t RegisterDJVUImage(void)
{
char
version[MaxTextExtent];
MagickInfo
*entry;
static const char
*DJVUNote =
{
"See http://www.djvuzone.org/ for details about the DJVU format. The\n"
"DJVU 1.2 specification is available there and at\n"
"ftp://swrinde.nde.swri.edu/pub/djvu/documents/."
};
*version='\0';
#if defined(DJVU_LIBDJVU_VER_STRING)
(void) ConcatenateMagickString(version,"libdjvu ",MaxTextExtent);
(void) ConcatenateMagickString(version,DJVU_LIBDJVU_VER_STRING,MaxTextExtent);
#endif
entry=SetMagickInfo("DJVU");
#if defined(MAGICKCORE_DJVU_DELEGATE)
entry->decoder=(DecodeImageHandler *) ReadDJVUImage;
#endif
entry->raw=MagickTrue;
entry->magick=(IsImageFormatHandler *) IsDJVU;
entry->adjoin=MagickFalse;
entry->thread_support=NoThreadSupport;
entry->description=AcquireString("Deja vu");
entry->module=AcquireString("DJVU");
if (*version != '\0')
entry->version=AcquireString(version);
entry->note=AcquireString(DJVUNote);
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
ModuleExport void UnregisterDJVUImage(void)
{
(void) UnregisterMagickInfo("DJVU");
}