2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
20 % Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/colormap.h"
47 #include "MagickCore/constitute.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/list.h"
51 #include "MagickCore/magick.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/monitor.h"
54 #include "MagickCore/monitor-private.h"
55 #include "MagickCore/pixel-accessor.h"
56 #include "MagickCore/quantum-private.h"
57 #include "MagickCore/static.h"
58 #include "MagickCore/string_.h"
59 #include "MagickCore/module.h"
60 #if defined(MAGICKCORE_DJVU_DELEGATE)
61 #include <libdjvu/ddjvuapi.h>
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 % IsDJVU() returns MagickTrue if the image format type, identified by the
76 % magick string, is DJVU.
78 % The format of the IsDJVU method is:
80 % MagickBooleanType IsDJVU(const unsigned char *magick,const size_t length)
82 % A description of each parameter follows:
84 % o magick: compare image format pattern against these bytes.
86 % o length: Specifies the length of the magick string.
89 static MagickBooleanType IsDJVU(const unsigned char *magick,const size_t length)
93 if (memcmp(magick,"AT&TFORM",8) == 0)
98 #if defined(MAGICKCORE_DJVU_DELEGATE)
100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104 % R e a d D J V U I m a g e %
108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110 % ReadDJVUImage() reads DJVU image and returns it. It allocates the memory
111 % necessary for the new Image structure and returns a pointer to the new
112 % image or set of images.
114 % The format of the ReadDJVUImage method is:
116 % Image *ReadDJVUImage(const ImageInfo *image_info,
117 % ExceptionInfo *exception)
119 % A description of each parameter follows:
121 % o image_info: the image info.
123 % o exception: return any errors or warnings in this structure.
127 #if defined(__cplusplus) || defined(c_plusplus)
131 typedef struct _LoadContext
136 ddjvu_context_t* context;
137 ddjvu_document_t *document;
144 #define BLOCKSIZE 65536
147 pump_data(Image *image, LoadContext* lc)
149 int blocksize = BLOCKSIZE;
150 char data[BLOCKSIZE];
153 /* i might check for a condition! */
154 while ((size = (size_t) ReadBlob(image,(size_t) blocksize,data)) == blocksize) {
155 ddjvu_stream_write(lc->document, lc->streamid, data, size);
158 ddjvu_stream_write(lc->document, lc->streamid, data, size);
159 ddjvu_stream_close(lc->document, lc->streamid, 0);
163 /* returns NULL only after all is delivered! */
164 static ddjvu_message_t*
165 pump_data_until_message(LoadContext *lc,Image *image) /* ddjvu_context_t *context, type ddjvu_document_type_t */
167 size_t blocksize = BLOCKSIZE;
168 unsigned char data[BLOCKSIZE];
170 ddjvu_message_t *message;
172 /* i might check for a condition! */
174 while (!(message = ddjvu_message_peek(lc->context))
175 && (size = (size_t) ReadBlob(image,(size_t) blocksize,data)) == blocksize) {
176 ddjvu_stream_write(lc->document, lc->streamid, (char *) data, size);
181 ddjvu_stream_write(lc->document, lc->streamid, (char *) data, size);
182 ddjvu_stream_close(lc->document, lc->streamid, 0);
189 message_tag_name(ddjvu_message_tag_t tag)
191 static char* names[] =
204 if (tag <= DDJVU_PROGRESS)
213 /* write out nice info on the message,
214 * and store in *user* data the info on progress.
217 process_message(ddjvu_message_t *message)
221 ddjvu_context_t* context= message->m_any.context;
227 printf("*** %s: %s.\n",__FUNCTION__, message_tag_name(message->m_any.tag));
231 switch (message->m_any.tag){
234 ddjvu_document_t* document= message->m_any.document;
235 /* ddjvu_document_decoding_status is set by libdjvu! */
236 /* we have some info on the document */
237 LoadContext *lc = (LoadContext *) ddjvu_document_get_user_data(document);
238 lc->pages = ddjvu_document_get_pagenum(document);
240 printf("the doc has %d pages\n", ddjvu_document_get_pagenum(document));
246 printf("the name of the chunk is: %s\n", message->m_chunk.chunkid);
255 ddjvu_page_t* page = message->m_any.page;
256 page_info* info = ddjvu_page_get_user_data(page);
258 printf("page decoding status: %d %s%s%s\n",
259 ddjvu_page_decoding_status(page),
260 status_color, status_name(ddjvu_page_decoding_status(page)), color_reset);
262 printf("the page LAYOUT changed: width x height: %d x %d @ %d dpi. Version %d, type %d\n",
263 // printf("page info:\n width x height: %d x %d @ %d dpi, version %d, type %d\n",
264 ddjvu_page_get_width(page),
265 ddjvu_page_get_height(page),
266 ddjvu_page_get_resolution(page),
267 ddjvu_page_get_version(page),
268 /* DDJVU_PAGETYPE_BITONAL */
269 ddjvu_page_get_type(page));
276 case DDJVU_REDISPLAY:
280 ddjvu_page_t* page = message->m_any.page;
281 page_info* info = ddjvu_page_get_user_data(page);
283 printf("the page can/should be REDISPLAYED\n");
291 printf("PROGRESS:\n");
295 printf("simply ERROR!\n message:\t%s\nfunction:\t%s(file %s)\nlineno:\t%d\n",
296 message->m_error.message,
297 message->m_error.function,
298 message->m_error.filename,
299 message->m_error.lineno);
303 printf("INFO: %s!\n", message->m_info.message);
307 printf("unexpected\n");
309 return(message->m_any.tag);
313 #if defined(__cplusplus) || defined(c_plusplus)
321 * DjVu advertised readiness to provide bitmap: So get it!
322 * we use the RGB format!
325 get_page_image(LoadContext *lc, ddjvu_page_t *page, int x, int y, int w, int h, ExceptionInfo *exception ) {
345 rect.w = (unsigned int) w; /* /10 */
346 rect.h = (unsigned int) h; /* /10 */
349 type = ddjvu_page_get_type(lc->page);
351 /* stride of this temporary buffer: */
352 stride = (type == DDJVU_PAGETYPE_BITONAL)?
353 (image->columns + 7)/8 : image->columns *3;
355 q = (unsigned char *) AcquireQuantumMemory(image->rows,stride);
356 if (q == (unsigned char *) NULL)
359 format = ddjvu_format_create(
360 (type == DDJVU_PAGETYPE_BITONAL)?DDJVU_FORMAT_LSBTOMSB : DDJVU_FORMAT_RGB24,
361 /* DDJVU_FORMAT_RGB24
362 * DDJVU_FORMAT_RGBMASK32*/
363 /* DDJVU_FORMAT_RGBMASK32 */
367 /* fixme: ThrowReaderException is a macro, which uses `exception' variable */
371 /* ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); */
375 ddjvu_format_set_row_order(format, 1);
376 ddjvu_format_set_y_direction(format, 1);
378 ret = ddjvu_page_render(page,
379 DDJVU_RENDER_COLOR, /* ddjvu_render_mode_t */
386 ddjvu_format_release(format);
389 if (type == DDJVU_PAGETYPE_BITONAL) {
392 printf("%s: expanding BITONAL page/image\n", __FUNCTION__);
396 for (y=0; y < (ssize_t) image->rows; y++)
398 Quantum * o = QueueAuthenticPixels(image,0,y,image->columns,1,exception);
399 if (o == (Quantum *) NULL)
404 /* fixme: the non-aligned, last =<7 bits ! that's ok!!!*/
405 for (x= 0; x < (ssize_t) image->columns; x++)
407 if (bit == 0) byte= (size_t) q[(y * stride) + (x / 8)];
409 SetPixelIndex(image,(Quantum) (((byte & 0x01) != 0) ? 0x00 : 0x01),o);
414 o+=GetPixelChannels(image);
416 if (SyncAuthenticPixels(image,exception) == MagickFalse)
420 SyncImage(image,exception);
423 printf("%s: expanding PHOTO page/image\n", __FUNCTION__);
425 /* now transfer line-wise: */
435 for (i = 0;i< (ssize_t) image->rows; i++)
438 if (i % 1000 == 0) printf("%d\n",i);
440 r = QueueAuthenticPixels(image,0,i,image->columns,1,exception);
441 if (r == (Quantum *) NULL)
443 for (x=0; x < (ssize_t) image->columns; x++)
445 SetPixelRed(image,ScaleCharToQuantum(*s++),r);
446 SetPixelGreen(image,ScaleCharToQuantum(*s++),r);
447 SetPixelBlue(image,ScaleCharToQuantum(*s++),r);
448 r+=GetPixelChannels(image);
451 SyncAuthenticPixels(image,exception);
454 q=(unsigned char *) RelinquishMagickMemory(q);
458 #if defined(MAGICKCORE_DJVU_DELEGATE)
462 get_page_line(LoadContext *lc, int row, QuantumInfo* quantum_info)
476 ddjvu_rect_t rect, pagerect;
479 rect.w = lc->image->columns; /* /10 */
480 rect.h = 1; /* /10 */
484 pagerect.w = lc->image->columns;
485 pagerect.h = lc->image->rows;
488 format = ddjvu_format_create(
496 ddjvu_format_set_row_order(format, 1);
497 ddjvu_format_set_y_direction(format, 1);
503 q = (unsigned char *) AcquireQuantumMemory(lc->image->columns,stride);
505 ret = ddjvu_page_render(lc->page,
506 DDJVU_RENDER_COLOR, /* ddjvu_render_mode_t */
510 pagerect.w * 3, /* ?? */
513 ImportQuantumPixels(lc->image,
521 ,q,&lc->image->exception);
522 q=(unsigned char *) RelinquishMagickMemory(q);
523 ddjvu_format_release(format);
530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534 % R e a d O n e D J V U I m a g e %
538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
540 % ReadOneDJVUImage() reads a Portable Network Graphics (DJVU) image file
541 % (minus the 8-byte signature) and returns it. It allocates the memory
542 % necessary for the new Image structure and returns a pointer to the new
545 % The format of the ReadOneDJVUImage method is:
547 % Image *ReadOneDJVUImage(MngInfo *mng_info, const ImageInfo *image_info,
548 % ExceptionInfo *exception)
550 % A description of each parameter follows:
552 % o mng_info: Specifies a pointer to a MngInfo structure.
554 % o image_info: the image info.
556 % o exception: return any errors or warnings in this structure.
560 static inline double MagickMax(const double x,const double y)
567 static Image *ReadOneDJVUImage(LoadContext* lc,const int pagenum,
568 const ImageInfo *image_info,ExceptionInfo *exception)
573 ddjvu_pageinfo_t info;
574 ddjvu_message_t *message;
579 /* so, we know that the page is there! Get its dimension, and */
581 /* Read one DJVU image */
584 /* register Quantum *q; */
586 logging=LogMagickEvent(CoderEvent,GetMagickModule(), " enter ReadOneDJVUImage()");
590 printf("==== Loading the page %d\n", pagenum);
592 lc->page = ddjvu_page_create_by_pageno(lc->document, pagenum); /* 0? */
594 /* pump data untill the page is ready for rendering. */
597 while ((message = ddjvu_message_peek(lc->context)))
599 tag=process_message(message);
601 ddjvu_message_pop(lc->context);
603 /* fixme: maybe exit? */
604 /* if (lc->error) break; */
606 message = pump_data_until_message(lc,image);
609 tag=process_message(message);
611 ddjvu_message_pop(lc->context);
612 } while ((message = ddjvu_message_peek(lc->context)));
613 } while (!ddjvu_page_decoding_done(lc->page));
615 ddjvu_document_get_pageinfo(lc->document, pagenum, &info);
617 image->resolution.x = (float) info.dpi;
618 image->resolution.y =(float) info.dpi;
619 if (image_info->density != (char *) NULL)
628 Set rendering resolution.
630 flags=ParseGeometry(image_info->density,&geometry_info);
631 image->resolution.x=geometry_info.rho;
632 image->resolution.y=geometry_info.sigma;
633 if ((flags & SigmaValue) == 0)
634 image->resolution.y=image->resolution.x;
635 info.width*=image->resolution.x/info.dpi;
636 info.height*=image->resolution.y/info.dpi;
637 info.dpi=(ssize_t) MagickMax(image->resolution.x,image->resolution.y);
639 type = ddjvu_page_get_type(lc->page);
641 /* double -> float! */
642 /* image->gamma = (float)ddjvu_page_get_gamma(lc->page); */
644 /* mmc: set image->depth */
645 /* mmc: This from the type */
647 image->columns=(size_t) info.width;
648 image->rows=(size_t) info.height;
650 /* mmc: bitonal should be palettized, and compressed! */
651 if (type == DDJVU_PAGETYPE_BITONAL){
652 image->colorspace = GRAYColorspace;
653 image->storage_class = PseudoClass;
654 image->depth = 8UL; /* i only support that? */
656 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
657 ThrowReaderException(ResourceLimitError,
658 "MemoryAllocationFailed");
660 image->colorspace = RGBColorspace;
661 image->storage_class = DirectClass;
662 /* fixme: MAGICKCORE_QUANTUM_DEPTH ?*/
663 image->depth = 8UL; /* i only support that? */
665 image->alpha_trait = BlendPixelTrait;
666 /* is this useful? */
669 printf("now filling %.20g x %.20g\n",(double) image->columns,(double)
676 /* q = QueueAuthenticPixels(image,0,0,image->columns,image->rows); */
677 get_page_image(lc, lc->page, 0, 0, info.width, info.height, exception);
680 for (i = 0;i< image->rows; i++)
683 q = QueueAuthenticPixels(image,0,i,image->columns,1);
684 get_page_line(lc, i, quantum_info);
685 SyncAuthenticPixels(image);
688 #endif /* per_line */
692 printf("END: finished filling %.20g x %.20g\n",(double) image->columns,
693 (double) image->rows);
697 SyncImage(image,exception);
698 /* mmc: ??? Convert PNM pixels to runlength-encoded MIFF packets. */
699 /* image->colors = */
701 /* how is the line padding / stride? */
704 ddjvu_page_release(lc->page);
708 /* image->page.y=mng_info->y_off[mng_info->object_id]; */
710 image=DestroyImage(image);
712 /* end of reading one DJVU page/image */
717 if (AcquireImageColormap(image,2,exception) == MagickFalse)
718 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
720 Monochrome colormap. mmc: this the default!
722 image->colormap[0].red=QuantumRange;
723 image->colormap[0].green=QuantumRange;
724 image->colormap[0].blue=QuantumRange;
725 image->colormap[1].red=0;
726 image->colormap[1].green=0;
727 image->colormap[1].blue=0;
730 static void djvu_close_lc(LoadContext* lc)
733 ddjvu_document_release(lc->document);
735 ddjvu_context_release(lc->context);
737 ddjvu_page_release(lc->page);
738 RelinquishMagickMemory(lc);
741 static Image *ReadDJVUImage(const ImageInfo *image_info,
742 ExceptionInfo *exception)
770 assert(image_info != (const ImageInfo *) NULL);
771 assert(image_info->signature == MagickSignature);
774 if (image_info->debug != MagickFalse)
775 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", image_info->filename);
777 assert(exception != (ExceptionInfo *) NULL);
778 assert(exception->signature == MagickSignature);
781 logging = LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadDJVUImage()");
784 image = AcquireImage(image_info,exception); /* mmc: ?? */
787 lc = (LoadContext *) NULL;
788 status = OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
789 if (status == MagickFalse)
790 ThrowReaderException(FileOpenError,"UnableToOpenFile");
792 Verify DJVU signature.
795 count = ReadBlob(image,8,(unsigned char *) magic_number);
797 /* IsDJVU(const unsigned char *magick,const size_t length) */
798 if (memcmp(magic_number,"AT&TFORM",8) != 0)
799 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
804 * Allocate a LoadContext structure.
806 lc = (LoadContext *) AcquireMagickMemory(sizeof(*lc));
808 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
812 * Initialize members of the MngInfo structure.
814 (void) ResetMagickMemory(lc,0,sizeof(LoadContext));
818 lc->context = ddjvu_context_create("ImageMagick djvu loader"); /* g_program_name */
820 ddjvu_cache_set_size(lc->context, 1); /* right? */
822 /* document: here we don't have a filename, but, for the sake of generality, a FILE* ! */
823 url="http://www.imagemagick.org/fake.djvu";
824 lc->document = ddjvu_document_create(lc->context, url, use_cache); /* don't cache */
825 ddjvu_document_set_user_data(lc->document, lc);
828 /* now we wait the message-request for data: */
829 message = ddjvu_message_wait(lc->context);
831 if (message->m_any.tag != DDJVU_NEWSTREAM) {
832 /* fixme: the djvu context, document! */
834 ddjvu_document_release(lc->document);
835 ddjvu_context_release(lc->context);
837 RelinquishMagickMemory(lc);
839 ThrowReaderException(ResourceLimitError,"Djvu initial message: unexpected type");
840 return NULL; /* error! */
843 lc->streamid = message->m_newstream.streamid;
844 ddjvu_message_pop(lc->context);
846 message = pump_data_until_message(lc,image);
847 /* now process the messages: */
851 process_message(message);
852 ddjvu_message_pop(lc->context);
853 } while ((message = ddjvu_message_peek(lc->context)));
855 /* fixme: i hope we have not read any messages pertinent(?) related to the page itself! */
857 while (lc->pages == 0) {
858 message = ddjvu_message_wait(lc->context);
859 process_message(message);
860 ddjvu_message_pop(lc->context);
863 images=NewImageList();
865 if (image_info->number_scenes != 0)
867 for ( ; i < (ssize_t) lc->pages; i++)
869 image=ReadOneDJVUImage(lc,i,image_info,exception);
870 if (image == (Image *) NULL)
873 AppendImageToList(&images,CloneImageList(image,exception));
874 images->extent=GetBlobSize(image);
875 if (image_info->number_scenes != 0)
876 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
880 (void) CloseBlob(images);
881 if (image != (Image *) NULL)
882 image=DestroyImageList(image);
885 if ((image->page.width == 0) && (image->page.height == 0))
887 image->page.width = image->columns+image->page.x;
888 image->page.height = image->rows+image->page.y;
890 if (image->columns == 0 || image->rows == 0)
892 if (logging != MagickFalse)
893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
894 "exit ReadDJVUImage() with error.");
895 ThrowReaderException(CorruptImageError,"CorruptImage");
898 if (logging != MagickFalse)
899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadDJVUImage()");
903 return(GetFirstImageInList(images));
908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
912 % R e g i s t e r D J V U I m a g e %
916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
918 % RegisterDJVUImage() adds attributes for the DJVU image format to
919 % the list of supported formats. The attributes include the image format
920 % tag, a method to read and/or write the format, whether the format
921 % supports the saving of more than one frame to the same file or blob,
922 % whether the format supports native in-memory I/O, and a brief
923 % description of the format.
925 % The format of the RegisterDJVUImage method is:
927 % size_t RegisterDJVUImage(void)
930 ModuleExport size_t RegisterDJVUImage(void)
933 version[MaxTextExtent];
941 "See http://www.djvuzone.org/ for details about the DJVU format. The\n"
942 "DJVU 1.2 specification is available there and at\n"
943 "ftp://swrinde.nde.swri.edu/pub/djvu/documents/."
947 #if defined(DJVU_LIBDJVU_VER_STRING)
948 (void) ConcatenateMagickString(version,"libdjvu ",MaxTextExtent);
949 (void) ConcatenateMagickString(version,DJVU_LIBDJVU_VER_STRING,MaxTextExtent);
951 entry=SetMagickInfo("DJVU");
952 #if defined(MAGICKCORE_DJVU_DELEGATE)
953 entry->decoder=(DecodeImageHandler *) ReadDJVUImage;
955 entry->raw=MagickTrue;
956 entry->magick=(IsImageFormatHandler *) IsDJVU;
957 entry->adjoin=MagickFalse;
958 entry->thread_support=MagickTrue;
959 entry->description=AcquireString("Déjà vu");
960 entry->module=AcquireString("DJVU");
961 if (*version != '\0')
962 entry->version=AcquireString(version);
963 entry->note=AcquireString(DJVUNote);
964 (void) RegisterMagickInfo(entry);
965 return(MagickImageCoderSignature);
969 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
973 % U n r e g i s t e r D J V U I m a g e %
977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
979 % UnregisterDJVUImage() removes format registrations made by the
980 % DJVU module from the list of supported formats.
982 % The format of the UnregisterDJVUImage method is:
984 % UnregisterDJVUImage(void)
987 ModuleExport void UnregisterDJVUImage(void)
989 (void) UnregisterMagickInfo("DJVU");