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