Home | History | Annotate | Download | only in coders
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                            TTTTT  X   X  TTTTT                              %
      7 %                              T     X X     T                                %
      8 %                              T      X      T                                %
      9 %                              T     X X     T                                %
     10 %                              T    X   X    T                                %
     11 %                                                                             %
     12 %                                                                             %
     13 %                      Render Text Onto A Canvas Image.                       %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 July 1992                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     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.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 */
     38 
     39 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/annotate.h"
     45 #include "MagickCore/attribute.h"
     46 #include "MagickCore/blob.h"
     47 #include "MagickCore/blob-private.h"
     48 #include "MagickCore/cache.h"
     49 #include "MagickCore/color.h"
     50 #include "MagickCore/color-private.h"
     51 #include "MagickCore/colorspace.h"
     52 #include "MagickCore/constitute.h"
     53 #include "MagickCore/draw.h"
     54 #include "MagickCore/exception.h"
     55 #include "MagickCore/exception-private.h"
     56 #include "MagickCore/geometry.h"
     57 #include "MagickCore/image.h"
     58 #include "MagickCore/image-private.h"
     59 #include "MagickCore/list.h"
     60 #include "MagickCore/magick.h"
     61 #include "MagickCore/memory_.h"
     62 #include "MagickCore/monitor.h"
     63 #include "MagickCore/monitor-private.h"
     64 #include "MagickCore/option.h"
     65 #include "MagickCore/pixel-accessor.h"
     66 #include "MagickCore/quantum-private.h"
     67 #include "MagickCore/static.h"
     68 #include "MagickCore/statistic.h"
     69 #include "MagickCore/string_.h"
     70 #include "MagickCore/module.h"
     71 
     72 /*
     74   Forward declarations.
     75 */
     76 static MagickBooleanType
     77   WriteTXTImage(const ImageInfo *,Image *,ExceptionInfo *);
     78 
     79 /*
     81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     82 %                                                                             %
     83 %                                                                             %
     84 %                                                                             %
     85 %   I s T X T                                                                 %
     86 %                                                                             %
     87 %                                                                             %
     88 %                                                                             %
     89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     90 %
     91 %  IsTXT() returns MagickTrue if the image format type, identified by the magick
     92 %  string, is TXT.
     93 %
     94 %  The format of the IsTXT method is:
     95 %
     96 %      MagickBooleanType IsTXT(const unsigned char *magick,const size_t length)
     97 %
     98 %  A description of each parameter follows:
     99 %
    100 %    o magick: compare image format pattern against these bytes.
    101 %
    102 %    o length: Specifies the length of the magick string.
    103 %
    104 */
    105 static MagickBooleanType IsTXT(const unsigned char *magick,const size_t length)
    106 {
    107 #define MagickID  "# ImageMagick pixel enumeration:"
    108 
    109   char
    110     colorspace[MagickPathExtent];
    111 
    112   ssize_t
    113     count;
    114 
    115   unsigned long
    116     columns,
    117     depth,
    118     rows;
    119 
    120   if (length < 40)
    121     return(MagickFalse);
    122   if (LocaleNCompare((const char *) magick,MagickID,strlen(MagickID)) != 0)
    123     return(MagickFalse);
    124   count=(ssize_t) sscanf((const char *) magick+32,"%lu,%lu,%lu,%s",&columns,
    125     &rows,&depth,colorspace);
    126   if (count != 4)
    127     return(MagickFalse);
    128   return(MagickTrue);
    129 }
    130 
    131 /*
    133 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    134 %                                                                             %
    135 %                                                                             %
    136 %                                                                             %
    137 %   R e a d T E X T I m a g e                                                 %
    138 %                                                                             %
    139 %                                                                             %
    140 %                                                                             %
    141 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    142 %
    143 %  ReadTEXTImage() reads a text file and returns it as an image.  It
    144 %  allocates the memory necessary for the new Image structure and returns a
    145 %  pointer to the new image.
    146 %
    147 %  The format of the ReadTEXTImage method is:
    148 %
    149 %      Image *ReadTEXTImage(const ImageInfo *image_info,Image *image,
    150 %        char *text,ExceptionInfo *exception)
    151 %
    152 %  A description of each parameter follows:
    153 %
    154 %    o image_info: the image info.
    155 %
    156 %    o image: the image.
    157 %
    158 %    o text: the text storage buffer.
    159 %
    160 %    o exception: return any errors or warnings in this structure.
    161 %
    162 */
    163 static Image *ReadTEXTImage(const ImageInfo *image_info,
    164   ExceptionInfo *exception)
    165 {
    166   char
    167     filename[MagickPathExtent],
    168     geometry[MagickPathExtent],
    169     *p,
    170     text[MagickPathExtent];
    171 
    172   DrawInfo
    173     *draw_info;
    174 
    175   Image
    176     *image,
    177     *texture;
    178 
    179   MagickBooleanType
    180     status;
    181 
    182   PointInfo
    183     delta;
    184 
    185   RectangleInfo
    186     page;
    187 
    188   ssize_t
    189     offset;
    190 
    191   TypeMetric
    192     metrics;
    193 
    194   /*
    195     Open image file.
    196   */
    197   assert(image_info != (const ImageInfo *) NULL);
    198   assert(image_info->signature == MagickCoreSignature);
    199   if (image_info->debug != MagickFalse)
    200     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    201       image_info->filename);
    202   assert(exception != (ExceptionInfo *) NULL);
    203   assert(exception->signature == MagickCoreSignature);
    204   image=AcquireImage(image_info,exception);
    205   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    206   if (status == MagickFalse)
    207     {
    208       image=DestroyImageList(image);
    209       return((Image *) NULL);
    210     }
    211   (void) ResetMagickMemory(text,0,sizeof(text));
    212   (void) ReadBlobString(image,text);
    213   /*
    214     Set the page geometry.
    215   */
    216   delta.x=DefaultResolution;
    217   delta.y=DefaultResolution;
    218   if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
    219     {
    220       GeometryInfo
    221         geometry_info;
    222 
    223       MagickStatusType
    224         flags;
    225 
    226       flags=ParseGeometry(PSDensityGeometry,&geometry_info);
    227       image->resolution.x=geometry_info.rho;
    228       image->resolution.y=geometry_info.sigma;
    229       if ((flags & SigmaValue) == 0)
    230         image->resolution.y=image->resolution.x;
    231     }
    232   page.width=612;
    233   page.height=792;
    234   page.x=43;
    235   page.y=43;
    236   if (image_info->page != (char *) NULL)
    237     (void) ParseAbsoluteGeometry(image_info->page,&page);
    238   /*
    239     Initialize Image structure.
    240   */
    241   image->columns=(size_t) floor((((double) page.width*image->resolution.x)/
    242     delta.x)+0.5);
    243   image->rows=(size_t) floor((((double) page.height*image->resolution.y)/
    244     delta.y)+0.5);
    245   status=SetImageExtent(image,image->columns,image->rows,exception);
    246   if (status == MagickFalse)
    247     return(DestroyImageList(image));
    248   image->page.x=0;
    249   image->page.y=0;
    250   texture=(Image *) NULL;
    251   if (image_info->texture != (char *) NULL)
    252     {
    253       ImageInfo
    254         *read_info;
    255 
    256       read_info=CloneImageInfo(image_info);
    257       SetImageInfoBlob(read_info,(void *) NULL,0);
    258       (void) CopyMagickString(read_info->filename,image_info->texture,
    259         MagickPathExtent);
    260       texture=ReadImage(read_info,exception);
    261       read_info=DestroyImageInfo(read_info);
    262     }
    263   /*
    264     Annotate the text image.
    265   */
    266   (void) SetImageBackgroundColor(image,exception);
    267   draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
    268   (void) CloneString(&draw_info->text,image_info->filename);
    269   (void) FormatLocaleString(geometry,MagickPathExtent,"%gx%g%+g%+g",(double)
    270     image->columns,(double) image->rows,(double) page.x,(double) page.y);
    271   (void) CloneString(&draw_info->geometry,geometry);
    272   status=GetTypeMetrics(image,draw_info,&metrics,exception);
    273   if (status == MagickFalse)
    274     ThrowReaderException(TypeError,"UnableToGetTypeMetrics");
    275   page.y=(ssize_t) ceil((double) page.y+metrics.ascent-0.5);
    276   (void) FormatLocaleString(geometry,MagickPathExtent,"%gx%g%+g%+g",(double)
    277     image->columns,(double) image->rows,(double) page.x,(double) page.y);
    278   (void) CloneString(&draw_info->geometry,geometry);
    279   (void) CopyMagickString(filename,image_info->filename,MagickPathExtent);
    280   if (*draw_info->text != '\0')
    281     *draw_info->text='\0';
    282   p=text;
    283   for (offset=2*page.y; p != (char *) NULL; )
    284   {
    285     /*
    286       Annotate image with text.
    287     */
    288     (void) ConcatenateString(&draw_info->text,text);
    289     (void) ConcatenateString(&draw_info->text,"\n");
    290     offset+=(ssize_t) (metrics.ascent-metrics.descent);
    291     if (image->previous == (Image *) NULL)
    292       {
    293         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) offset,
    294           image->rows);
    295         if (status == MagickFalse)
    296           break;
    297       }
    298     p=ReadBlobString(image,text);
    299     if ((offset < (ssize_t) image->rows) && (p != (char *) NULL))
    300       continue;
    301     if (texture != (Image *) NULL)
    302       {
    303         MagickProgressMonitor
    304           progress_monitor;
    305 
    306         progress_monitor=SetImageProgressMonitor(image,
    307           (MagickProgressMonitor) NULL,image->client_data);
    308         (void) TextureImage(image,texture,exception);
    309         (void) SetImageProgressMonitor(image,progress_monitor,
    310           image->client_data);
    311       }
    312     (void) AnnotateImage(image,draw_info,exception);
    313     if (p == (char *) NULL)
    314       break;
    315     /*
    316       Page is full-- allocate next image structure.
    317     */
    318     *draw_info->text='\0';
    319     offset=2*page.y;
    320     AcquireNextImage(image_info,image,exception);
    321     if (GetNextImageInList(image) == (Image *) NULL)
    322       {
    323         image=DestroyImageList(image);
    324         return((Image *) NULL);
    325       }
    326     image->next->columns=image->columns;
    327     image->next->rows=image->rows;
    328     image=SyncNextImageInList(image);
    329     (void) CopyMagickString(image->filename,filename,MagickPathExtent);
    330     (void) SetImageBackgroundColor(image,exception);
    331     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
    332       GetBlobSize(image));
    333     if (status == MagickFalse)
    334       break;
    335   }
    336   if (texture != (Image *) NULL)
    337     {
    338       MagickProgressMonitor
    339         progress_monitor;
    340 
    341       progress_monitor=SetImageProgressMonitor(image,
    342         (MagickProgressMonitor) NULL,image->client_data);
    343       (void) TextureImage(image,texture,exception);
    344       (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
    345     }
    346   (void) AnnotateImage(image,draw_info,exception);
    347   if (texture != (Image *) NULL)
    348     texture=DestroyImage(texture);
    349   draw_info=DestroyDrawInfo(draw_info);
    350   (void) CloseBlob(image);
    351   return(GetFirstImageInList(image));
    352 }
    353 
    354 /*
    356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    357 %                                                                             %
    358 %                                                                             %
    359 %                                                                             %
    360 %   R e a d T X T I m a g e                                                   %
    361 %                                                                             %
    362 %                                                                             %
    363 %                                                                             %
    364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    365 %
    366 %  ReadTXTImage() reads a text file and returns it as an image.  It allocates
    367 %  the memory necessary for the new Image structure and returns a pointer to
    368 %  the new image.
    369 %
    370 %  The format of the ReadTXTImage method is:
    371 %
    372 %      Image *ReadTXTImage(const ImageInfo *image_info,ExceptionInfo *exception)
    373 %
    374 %  A description of each parameter follows:
    375 %
    376 %    o image_info: the image info.
    377 %
    378 %    o exception: return any errors or warnings in this structure.
    379 %
    380 */
    381 static Image *ReadTXTImage(const ImageInfo *image_info,ExceptionInfo *exception)
    382 {
    383   char
    384     colorspace[MagickPathExtent],
    385     text[MagickPathExtent];
    386 
    387   Image
    388     *image;
    389 
    390   long
    391     x_offset,
    392     y_offset;
    393 
    394   PixelInfo
    395     pixel;
    396 
    397   MagickBooleanType
    398     status;
    399 
    400   QuantumAny
    401     range;
    402 
    403   register ssize_t
    404     i,
    405     x;
    406 
    407   register Quantum
    408     *q;
    409 
    410   ssize_t
    411     count,
    412     type,
    413     y;
    414 
    415   unsigned long
    416     depth,
    417     height,
    418     max_value,
    419     width;
    420 
    421   /*
    422     Open image file.
    423   */
    424   assert(image_info != (const ImageInfo *) NULL);
    425   assert(image_info->signature == MagickCoreSignature);
    426   if (image_info->debug != MagickFalse)
    427     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    428       image_info->filename);
    429   assert(exception != (ExceptionInfo *) NULL);
    430   assert(exception->signature == MagickCoreSignature);
    431   image=AcquireImage(image_info,exception);
    432   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    433   if (status == MagickFalse)
    434     {
    435       image=DestroyImageList(image);
    436       return((Image *) NULL);
    437     }
    438   (void) ResetMagickMemory(text,0,sizeof(text));
    439   (void) ReadBlobString(image,text);
    440   if (LocaleNCompare((char *) text,MagickID,strlen(MagickID)) != 0)
    441     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    442   do
    443   {
    444     width=0;
    445     height=0;
    446     max_value=0;
    447     *colorspace='\0';
    448     count=(ssize_t) sscanf(text+32,"%lu,%lu,%lu,%s",&width,&height,&max_value,
    449       colorspace);
    450     if ((count != 4) || (width == 0) || (height == 0) || (max_value == 0))
    451       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    452     image->columns=width;
    453     image->rows=height;
    454     for (depth=1; (GetQuantumRange(depth)+1) < max_value; depth++) ;
    455     image->depth=depth;
    456     status=SetImageExtent(image,image->columns,image->rows,exception);
    457     if (status == MagickFalse)
    458       return(DestroyImageList(image));
    459     LocaleLower(colorspace);
    460     i=(ssize_t) strlen(colorspace)-1;
    461     image->alpha_trait=UndefinedPixelTrait;
    462     if ((i > 0) && (colorspace[i] == 'a'))
    463       {
    464         colorspace[i]='\0';
    465         image->alpha_trait=BlendPixelTrait;
    466       }
    467     type=ParseCommandOption(MagickColorspaceOptions,MagickFalse,colorspace);
    468     if (type < 0)
    469       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    470     (void) SetImageBackgroundColor(image,exception);
    471     (void) SetImageColorspace(image,(ColorspaceType) type,exception);
    472     GetPixelInfo(image,&pixel);
    473     range=GetQuantumRange(image->depth);
    474     for (y=0; y < (ssize_t) image->rows; y++)
    475     {
    476       double
    477         alpha,
    478         black,
    479         blue,
    480         green,
    481         red;
    482 
    483       red=0.0;
    484       green=0.0;
    485       blue=0.0;
    486       black=0.0;
    487       alpha=0.0;
    488       for (x=0; x < (ssize_t) image->columns; x++)
    489       {
    490         if (ReadBlobString(image,text) == (char *) NULL)
    491           break;
    492         switch (image->colorspace)
    493         {
    494           case GRAYColorspace:
    495           {
    496             if (image->alpha_trait != UndefinedPixelTrait)
    497               {
    498                 count=(ssize_t) sscanf(text,"%ld,%ld: (%lf%*[%,]%lf%*[%,]",
    499                   &x_offset,&y_offset,&red,&alpha);
    500                 green=red;
    501                 blue=red;
    502                 break;
    503               }
    504             count=(ssize_t) sscanf(text,"%ld,%ld: (%lf%*[%,]",&x_offset,
    505               &y_offset,&red);
    506             green=red;
    507             blue=red;
    508             break;
    509           }
    510           case CMYKColorspace:
    511           {
    512             if (image->alpha_trait != UndefinedPixelTrait)
    513               {
    514                 count=(ssize_t) sscanf(text,
    515                   "%ld,%ld: (%lf%*[%,]%lf%*[%,]%lf%*[%,]%lf%*[%,]%lf%*[%,]",
    516                   &x_offset,&y_offset,&red,&green,&blue,&black,&alpha);
    517                 break;
    518               }
    519             count=(ssize_t) sscanf(text,
    520               "%ld,%ld: (%lf%*[%,]%lf%*[%,]%lf%*[%,]%lf%*[%,]",&x_offset,
    521               &y_offset,&red,&green,&blue,&black);
    522             break;
    523           }
    524           default:
    525           {
    526             if (image->alpha_trait != UndefinedPixelTrait)
    527               {
    528                 count=(ssize_t) sscanf(text,
    529                   "%ld,%ld: (%lf%*[%,]%lf%*[%,]%lf%*[%,]%lf%*[%,]",
    530                   &x_offset,&y_offset,&red,&green,&blue,&alpha);
    531                 break;
    532               }
    533             count=(ssize_t) sscanf(text,
    534               "%ld,%ld: (%lf%*[%,]%lf%*[%,]%lf%*[%,]",&x_offset,
    535               &y_offset,&red,&green,&blue);
    536             break;
    537           }
    538         }
    539         if (strchr(text,'%') != (char *) NULL)
    540           {
    541             red*=0.01*range;
    542             green*=0.01*range;
    543             blue*=0.01*range;
    544             black*=0.01*range;
    545             alpha*=0.01*range;
    546           }
    547         if (image->colorspace == LabColorspace)
    548           {
    549             green+=(range+1)/2.0;
    550             blue+=(range+1)/2.0;
    551           }
    552         pixel.red=(MagickRealType) ScaleAnyToQuantum((QuantumAny) (red+0.5),
    553           range);
    554         pixel.green=(MagickRealType) ScaleAnyToQuantum((QuantumAny) (green+0.5),
    555           range);
    556         pixel.blue=(MagickRealType) ScaleAnyToQuantum((QuantumAny) (blue+0.5),
    557           range);
    558         pixel.black=(MagickRealType) ScaleAnyToQuantum((QuantumAny) (black+0.5),
    559           range);
    560         pixel.alpha=(MagickRealType) ScaleAnyToQuantum((QuantumAny) (alpha+0.5),
    561           range);
    562         q=GetAuthenticPixels(image,(ssize_t) x_offset,(ssize_t) y_offset,1,1,
    563           exception);
    564         if (q == (Quantum *) NULL)
    565           continue;
    566         SetPixelViaPixelInfo(image,&pixel,q);
    567         if (SyncAuthenticPixels(image,exception) == MagickFalse)
    568           break;
    569       }
    570     }
    571     (void) ReadBlobString(image,text);
    572     if (LocaleNCompare((char *) text,MagickID,strlen(MagickID)) == 0)
    573       {
    574         /*
    575           Allocate next image structure.
    576         */
    577         AcquireNextImage(image_info,image,exception);
    578         if (GetNextImageInList(image) == (Image *) NULL)
    579           {
    580             image=DestroyImageList(image);
    581             return((Image *) NULL);
    582           }
    583         image=SyncNextImageInList(image);
    584         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
    585           GetBlobSize(image));
    586         if (status == MagickFalse)
    587           break;
    588       }
    589   } while (LocaleNCompare((char *) text,MagickID,strlen(MagickID)) == 0);
    590   (void) CloseBlob(image);
    591   return(GetFirstImageInList(image));
    592 }
    593 
    594 /*
    596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    597 %                                                                             %
    598 %                                                                             %
    599 %                                                                             %
    600 %   R e g i s t e r T X T I m a g e                                           %
    601 %                                                                             %
    602 %                                                                             %
    603 %                                                                             %
    604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    605 %
    606 %  RegisterTXTImage() adds attributes for the TXT image format to the
    607 %  list of supported formats.  The attributes include the image format
    608 %  tag, a method to read and/or write the format, whether the format
    609 %  supports the saving of more than one frame to the same file or blob,
    610 %  whether the format supports native in-memory I/O, and a brief
    611 %  description of the format.
    612 %
    613 %  The format of the RegisterTXTImage method is:
    614 %
    615 %      size_t RegisterTXTImage(void)
    616 %
    617 */
    618 ModuleExport size_t RegisterTXTImage(void)
    619 {
    620   MagickInfo
    621     *entry;
    622 
    623   entry=AcquireMagickInfo("TXT","SPARSE-COLOR","Sparse Color");
    624   entry->encoder=(EncodeImageHandler *) WriteTXTImage;
    625   entry->flags|=CoderRawSupportFlag;
    626   entry->flags|=CoderEndianSupportFlag;
    627   (void) RegisterMagickInfo(entry);
    628   entry=AcquireMagickInfo("TXT","TEXT","Text");
    629   entry->decoder=(DecodeImageHandler *) ReadTEXTImage;
    630   entry->format_type=ImplicitFormatType;
    631   entry->flags|=CoderRawSupportFlag;
    632   entry->flags|=CoderEndianSupportFlag;
    633   (void) RegisterMagickInfo(entry);
    634   entry=AcquireMagickInfo("TXT","TXT","Text");
    635   entry->decoder=(DecodeImageHandler *) ReadTXTImage;
    636   entry->encoder=(EncodeImageHandler *) WriteTXTImage;
    637   entry->magick=(IsImageFormatHandler *) IsTXT;
    638   (void) RegisterMagickInfo(entry);
    639   return(MagickImageCoderSignature);
    640 }
    641 
    642 /*
    644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    645 %                                                                             %
    646 %                                                                             %
    647 %                                                                             %
    648 %   U n r e g i s t e r T X T I m a g e                                       %
    649 %                                                                             %
    650 %                                                                             %
    651 %                                                                             %
    652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    653 %
    654 %  UnregisterTXTImage() removes format registrations made by the
    655 %  TXT module from the list of supported format.
    656 %
    657 %  The format of the UnregisterTXTImage method is:
    658 %
    659 %      UnregisterTXTImage(void)
    660 %
    661 */
    662 ModuleExport void UnregisterTXTImage(void)
    663 {
    664   (void) UnregisterMagickInfo("SPARSE-COLOR");
    665   (void) UnregisterMagickInfo("TEXT");
    666   (void) UnregisterMagickInfo("TXT");
    667 }
    668 
    669 /*
    671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    672 %                                                                             %
    673 %                                                                             %
    674 %                                                                             %
    675 %   W r i t e T X T I m a g e                                                 %
    676 %                                                                             %
    677 %                                                                             %
    678 %                                                                             %
    679 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    680 %
    681 %  WriteTXTImage writes the pixel values as text numbers.
    682 %
    683 %  The format of the WriteTXTImage method is:
    684 %
    685 %      MagickBooleanType WriteTXTImage(const ImageInfo *image_info,
    686 %        Image *image,ExceptionInfo *exception)
    687 %
    688 %  A description of each parameter follows.
    689 %
    690 %    o image_info: the image info.
    691 %
    692 %    o image:  The image.
    693 %
    694 %    o exception: return any errors or warnings in this structure.
    695 %
    696 */
    697 static MagickBooleanType WriteTXTImage(const ImageInfo *image_info,Image *image,
    698   ExceptionInfo *exception)
    699 {
    700   char
    701     buffer[MagickPathExtent],
    702     colorspace[MagickPathExtent],
    703     tuple[MagickPathExtent];
    704 
    705   MagickBooleanType
    706     status;
    707 
    708   MagickOffsetType
    709     scene;
    710 
    711   PixelInfo
    712     pixel;
    713 
    714   register const Quantum
    715     *p;
    716 
    717   register ssize_t
    718     x;
    719 
    720   ssize_t
    721     y;
    722 
    723   /*
    724     Open output image file.
    725   */
    726   assert(image_info != (const ImageInfo *) NULL);
    727   assert(image_info->signature == MagickCoreSignature);
    728   assert(image != (Image *) NULL);
    729   assert(image->signature == MagickCoreSignature);
    730   if (image->debug != MagickFalse)
    731     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    732   status=OpenBlob(image_info,image,WriteBlobMode,exception);
    733   if (status == MagickFalse)
    734     return(status);
    735   scene=0;
    736   do
    737   {
    738     ComplianceType
    739       compliance;
    740 
    741     const char
    742       *value;
    743 
    744     (void) CopyMagickString(colorspace,CommandOptionToMnemonic(
    745       MagickColorspaceOptions,(ssize_t) image->colorspace),MagickPathExtent);
    746     LocaleLower(colorspace);
    747     image->depth=GetImageQuantumDepth(image,MagickTrue);
    748     if (image->alpha_trait != UndefinedPixelTrait)
    749       (void) ConcatenateMagickString(colorspace,"a",MagickPathExtent);
    750     compliance=NoCompliance;
    751     value=GetImageOption(image_info,"txt:compliance");
    752     if (value != (char *) NULL)
    753       compliance=(ComplianceType) ParseCommandOption(MagickComplianceOptions,
    754         MagickFalse,value);
    755     if (LocaleCompare(image_info->magick,"SPARSE-COLOR") != 0)
    756       {
    757         size_t
    758           depth;
    759 
    760         depth=compliance == SVGCompliance ? image->depth :
    761           MAGICKCORE_QUANTUM_DEPTH;
    762         (void) FormatLocaleString(buffer,MagickPathExtent,
    763           "# ImageMagick pixel enumeration: %.20g,%.20g,%.20g,%s\n",(double)
    764           image->columns,(double) image->rows,(double) ((MagickOffsetType)
    765           GetQuantumRange(depth)),colorspace);
    766         (void) WriteBlobString(image,buffer);
    767       }
    768     GetPixelInfo(image,&pixel);
    769     for (y=0; y < (ssize_t) image->rows; y++)
    770     {
    771       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    772       if (p == (const Quantum *) NULL)
    773         break;
    774       for (x=0; x < (ssize_t) image->columns; x++)
    775       {
    776         GetPixelInfoPixel(image,p,&pixel);
    777         if (pixel.colorspace == LabColorspace)
    778           {
    779             pixel.green-=(QuantumRange+1)/2.0;
    780             pixel.blue-=(QuantumRange+1)/2.0;
    781           }
    782         if (LocaleCompare(image_info->magick,"SPARSE-COLOR") == 0)
    783           {
    784             /*
    785               Sparse-color format.
    786             */
    787             if (GetPixelAlpha(image,p) == (Quantum) OpaqueAlpha)
    788               {
    789                 GetColorTuple(&pixel,MagickFalse,tuple);
    790                 (void) FormatLocaleString(buffer,MagickPathExtent,
    791                   "%.20g,%.20g,",(double) x,(double) y);
    792                 (void) WriteBlobString(image,buffer);
    793                 (void) WriteBlobString(image,tuple);
    794                 (void) WriteBlobString(image," ");
    795               }
    796             p+=GetPixelChannels(image);
    797             continue;
    798           }
    799         (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g,%.20g: ",
    800           (double) x,(double) y);
    801         (void) WriteBlobString(image,buffer);
    802         (void) CopyMagickString(tuple,"(",MagickPathExtent);
    803         if (pixel.colorspace == GRAYColorspace)
    804           ConcatenateColorComponent(&pixel,GrayPixelChannel,compliance,
    805             tuple);
    806         else
    807           {
    808             ConcatenateColorComponent(&pixel,RedPixelChannel,compliance,tuple);
    809             (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
    810             ConcatenateColorComponent(&pixel,GreenPixelChannel,compliance,
    811               tuple);
    812             (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
    813             ConcatenateColorComponent(&pixel,BluePixelChannel,compliance,tuple);
    814           }
    815         if (pixel.colorspace == CMYKColorspace)
    816           {
    817             (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
    818             ConcatenateColorComponent(&pixel,BlackPixelChannel,compliance,
    819               tuple);
    820           }
    821         if (pixel.alpha_trait != UndefinedPixelTrait)
    822           {
    823             (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
    824             ConcatenateColorComponent(&pixel,AlphaPixelChannel,compliance,
    825               tuple);
    826           }
    827         (void) ConcatenateMagickString(tuple,")",MagickPathExtent);
    828         (void) WriteBlobString(image,tuple);
    829         (void) WriteBlobString(image,"  ");
    830         GetColorTuple(&pixel,MagickTrue,tuple);
    831         (void) FormatLocaleString(buffer,MagickPathExtent,"%s",tuple);
    832         (void) WriteBlobString(image,buffer);
    833         (void) WriteBlobString(image,"  ");
    834         (void) QueryColorname(image,&pixel,SVGCompliance,tuple,exception);
    835         (void) WriteBlobString(image,tuple);
    836         (void) WriteBlobString(image,"\n");
    837         p+=GetPixelChannels(image);
    838       }
    839       status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    840         image->rows);
    841       if (status == MagickFalse)
    842         break;
    843     }
    844     if (GetNextImageInList(image) == (Image *) NULL)
    845       break;
    846     image=SyncNextImageInList(image);
    847     status=SetImageProgress(image,SaveImagesTag,scene++,
    848       GetImageListLength(image));
    849     if (status == MagickFalse)
    850       break;
    851   } while (image_info->adjoin != MagickFalse);
    852   (void) CloseBlob(image);
    853   return(MagickTrue);
    854 }
    855