Coverage Report

Created: 2026-05-24 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/coders/txt.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                            TTTTT  X   X  TTTTT                              %
15
%                              T     X X     T                                %
16
%                              T      X      T                                %
17
%                              T     X X     T                                %
18
%                              T    X   X    T                                %
19
%                                                                             %
20
%                                                                             %
21
%                      Render Text Onto A Canvas Image.                       %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1992                                   %
27
%                              Jaroslav Fojtik                                %
28
%                                2009 - 2010                                  %
29
%                                                                             %
30
%                                                                             %
31
%                                                                             %
32
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33
%
34
% Patterns accepted:
35
%     0,0: 129,129,129,255
36
%     0,0: 129,129,129
37
%     0,0: (129,129,129) #818181
38
%     0,0: (3411594072,2774050136,1883729991) #CB58CB58A558A55870477047
39
%     0,0: (129,129,129,  0)  #81818100  rgba(129,129,129,0)
40
*/
41

42
/*
43
  Include declarations.
44
*/
45
#include "magick/studio.h"
46
#include "magick/blob.h"
47
#include "magick/pixel_cache.h"
48
#include "magick/color.h"
49
#include "magick/constitute.h"
50
#include "magick/magick.h"
51
#include "magick/monitor.h"
52
#include "magick/render.h"
53
#include "magick/texture.h"
54
#include "magick/utility.h"
55
#include "magick/static.h"
56

57
typedef enum
58
{
59
  NO_TXT = 0,
60
  IMAGEMAGICK_TXT = 1,
61
  TXT_GM8B_HEX,
62
  TXT_GM8B_PLAIN,
63
  TXT_GM8B_PLAIN2,
64
  TXT_GM16B_HEX,
65
  TXT_GM16B_PLAIN,
66
  TXT_GM32B_HEX,
67
  TXT_GM32B_PLAIN,
68
  IMAGEMAGICK_TXT_Q = 17,
69
  TXT_GM8B_HEX_Q,
70
  TXT_GM8B_PLAIN_Q,
71
  TXT_GM8B_PLAIN2_Q,
72
  TXT_GM16B_HEX_Q,
73
  TXT_GM16B_PLAIN_Q,
74
  TXT_GM32B_HEX_Q,
75
  TXT_GM32B_PLAIN_Q
76
} TXT_TYPE;
77
78
/*
79
  Forward declarations.
80
*/
81
static unsigned int
82
  WriteTXTImage(const ImageInfo *,Image *);
83

84
85
/** Reads up to end of line. */
86
static void readln(Image *image, int *pch)
87
104k
{
88
104k
  int
89
104k
    ch=0;
90
91
104k
  if (pch)
92
104k
    ch=*pch;
93
0
  else
94
0
    ch=' ';
95
96
40.4M
  while (ch != 10 /* LF */ && ch != 13 /* CR */ && ch != EOF)
97
40.3M
    {
98
40.3M
      ch = ReadBlobByte(image);
99
40.3M
    }
100
104k
  if (pch)
101
104k
    *pch=ch;
102
104k
}
103
104
static long ReadInt(Image *image, int *pch)
105
537k
{
106
537k
  int
107
537k
    ch;
108
109
537k
  long
110
537k
    n;
111
112
537k
  unsigned int
113
537k
    digits;
114
115
537k
  n=0;
116
537k
  if (pch)
117
537k
    ch=*pch;
118
0
  else
119
0
    ch=' ';
120
121
2.49M
  while (isspace(ch) || ch == 0)
122
1.96M
    {
123
1.96M
      ch = ReadBlobByte(image);
124
1.96M
      if (ch == EOF)
125
11.5k
        return (0);
126
1.96M
    }
127
128
526k
  digits=0;
129
1.00M
  while ((digits < 10) && isdigit(ch))
130
482k
    {
131
482k
      n=10*n+(ch-'0');
132
482k
      ch = ReadBlobByte(image);
133
482k
      if (ch == EOF)
134
1.86k
        return (n);
135
480k
      ch &= 0xff;
136
480k
      digits++;
137
480k
    }
138
139
524k
  if (pch)
140
524k
    *pch=ch;
141
  /*  else
142
      ungetc(ch,F); */
143
144
524k
  return (n);
145
526k
}
146
147
148
/*
149
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150
%                                                                             %
151
%                                                                             %
152
%                                                                             %
153
%   I s T X T                                                                 %
154
%                                                                             %
155
%                                                                             %
156
%                                                                             %
157
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158
%
159
%  Method IsTXT returns an enumerated value which indicates the raw TXT
160
%  image encoding subformat based on the file or blob header.  If the data
161
%  is not an ASCII encoded raw image, then the value NO_TXT is returned.
162
%
163
%  The format of the IsTXT method is:
164
%
165
%      TXT_TYPE IsTXT(const unsigned char *magick,const size_t length)
166
%
167
%  A description of each parameter follows:
168
%
169
%    o status:  Method IsTXT returns a value from the TXT_TYPE enumeration.
170
%
171
%    o magick: This string is generally the first few bytes of an image file
172
%      or blob.
173
%
174
%    o length: Specifies the length of the magick string.
175
%
176
%
177
*/
178
static TXT_TYPE IsTXT(const unsigned char *magick,const size_t length)
179
6.57k
{
180
6.57k
  if (length < 20)
181
62
    return (NO_TXT);
182
183
6.51k
  {
184
6.51k
    unsigned long
185
6.51k
      column,
186
6.51k
      row;
187
188
6.51k
    unsigned int
189
6.51k
      red,
190
6.51k
      green,
191
6.51k
      blue,
192
6.51k
      opacity,
193
6.51k
      hex_red,
194
6.51k
      hex_green,
195
6.51k
      hex_blue,
196
6.51k
      hex_opacity;
197
198
6.51k
    int
199
6.51k
      count;
200
201
6.51k
    char
202
6.51k
      buffer[MaxTextExtent];
203
204
6.51k
    (void) memset((void *)buffer,0,MaxTextExtent);
205
6.51k
    (void) memcpy((void *)buffer,(const void *)magick,Min(MaxTextExtent-1,length));
206
207
6.51k
    if (!strncmp(buffer,"# ImageMagick pixel enumeration:",32))
208
359
      return IMAGEMAGICK_TXT;
209
210
6.15k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u) #%02X%02X%02X",
211
6.15k
                 &column, &row, &red, &green, &blue, &hex_red, &hex_green,
212
6.15k
                 &hex_blue);
213
6.15k
    if ((count == 8) && (column == 0) && (row == 0) && (red == hex_red) &&
214
374
        (green == hex_green) && (blue == hex_blue))
215
2
      return (TXT_GM8B_HEX);
216
217
6.15k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u) #%04X%04X%04X",
218
6.15k
                 &column, &row, &red, &green, &blue, &hex_red, &hex_green,
219
6.15k
                 &hex_blue);
220
6.15k
    if ((count == 8) && (column == 0) && (row == 0) && (red == hex_red) &&
221
292
        (green == hex_green) && (blue == hex_blue))
222
3
      return (TXT_GM16B_HEX);
223
224
6.15k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u) #%08X%08X%08X",
225
6.15k
                 &column, &row, &red, &green, &blue, &hex_red, &hex_green,
226
6.15k
                 &hex_blue);
227
6.15k
    if ((count == 8) && (column == 0) && (row == 0) && (red == hex_red) &&
228
287
        (green == hex_green) && (blue == hex_blue))
229
2
      return (TXT_GM32B_HEX);
230
231
6.15k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u) #%02X%02X%02X%02X",
232
6.15k
                 &column, &row, &red, &green, &blue, &opacity, &hex_red,
233
6.15k
                 &hex_green, &hex_blue, &hex_opacity);
234
6.15k
    if ((count == 10) && (column == 0) && (row == 0) && (red == hex_red) &&
235
691
        (green == hex_green) && (blue == hex_blue) && (opacity == hex_opacity))
236
3
      return (TXT_GM8B_HEX_Q);
237
238
6.14k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u) #%04X%04X%04X%04X",
239
6.14k
                 &column, &row, &red, &green, &blue, &opacity, &hex_red,
240
6.14k
                 &hex_green, &hex_blue, &hex_opacity);
241
6.14k
    if ((count == 10) && (column == 0) && (row == 0) && (red == hex_red) &&
242
433
        (green == hex_green) && (blue == hex_blue) && (opacity == hex_opacity))
243
2
      return (TXT_GM16B_HEX_Q);
244
245
6.14k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u) #%08X%08X%08X%08X",
246
6.14k
                 &column, &row, &red, &green, &blue, &opacity, &hex_red,
247
6.14k
                 &hex_green, &hex_blue, &hex_opacity);
248
6.14k
    if ((count == 10) && (column == 0) && (row == 0) && (red == hex_red) &&
249
395
        (green == hex_green) && (blue == hex_blue) && (opacity == hex_opacity))
250
2
      return (TXT_GM32B_HEX_Q);
251
252
6.14k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u)",
253
6.14k
                 &column, &row, &red, &green, &blue, &opacity);
254
6.14k
    if (count==6)
255
1.83k
      return TXT_GM8B_PLAIN_Q;
256
257
4.31k
    count=sscanf(buffer,"%lu,%lu: %u, %u, %u, %u",
258
4.31k
                 &column, &row, &red, &green, &blue, &opacity);
259
4.31k
    if (count==6)
260
1.48k
      return TXT_GM8B_PLAIN2_Q;
261
262
2.83k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u)",
263
2.83k
                 &column, &row, &red, &green, &blue);
264
2.83k
    if (count==5)
265
2.08k
      return TXT_GM8B_PLAIN;
266
267
750
    count=sscanf(buffer,"%lu,%lu: %u, %u, %u",
268
750
                 &column, &row, &red, &green, &blue);
269
750
    if (count==5)
270
470
      return TXT_GM8B_PLAIN2;
271
272
750
  }
273
280
  return (NO_TXT);
274
750
}
275

276
/*
277
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278
%                                                                             %
279
%                                                                             %
280
%                                                                             %
281
%   R e a d T X T I m a g e                                                   %
282
%                                                                             %
283
%                                                                             %
284
%                                                                             %
285
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286
%
287
%  Method ReadTXTImage reads a text file and returns it as an image.  It
288
%  allocates the memory necessary for the new Image structure and returns a
289
%  pointer to the new image.
290
%
291
%  The format of the ReadTXTImage method is:
292
%
293
%      Image *ReadTXTImage(const ImageInfo *image_info,ExceptionInfo *exception)
294
%
295
%  A description of each parameter follows:
296
%
297
%    o image:  Method ReadTXTImage returns a pointer to the image after
298
%      reading. A null image is returned if there is a memory shortage or if
299
%      the image cannot be read.
300
%
301
%    o image_info: Specifies a pointer to a ImageInfo structure.
302
%
303
%    o exception: return any errors or warnings in this structure.
304
%
305
%
306
*/
307
static Image *ReadTXTImage(const ImageInfo *image_info,ExceptionInfo *exception)
308
6.59k
{
309
6.59k
  char
310
6.59k
    *p,
311
6.59k
    text[MaxTextExtent];
312
313
6.59k
  Image
314
6.59k
    *image;
315
316
6.59k
  TXT_TYPE
317
6.59k
    txt_subformat;
318
319
6.59k
  MagickPassFail
320
6.59k
    status;
321
322
6.59k
  MagickBool
323
6.59k
    logging;
324
325
  /*
326
    Open image file.
327
  */
328
6.59k
  assert(image_info != (const ImageInfo *) NULL);
329
6.59k
  assert(image_info->signature == MagickSignature);
330
6.59k
  assert(exception != (ExceptionInfo *) NULL);
331
6.59k
  assert(exception->signature == MagickSignature);
332
333
6.59k
  logging = IsEventLogged(CoderEvent);
334
6.59k
  image=AllocateImage(image_info);
335
6.59k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
336
6.59k
  if (status == False)
337
6.59k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
338
339
6.59k
  p = ReadBlobString(image,text);
340
6.59k
  if (p == NULL)
341
6.57k
    ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
342
6.57k
  txt_subformat = IsTXT((unsigned char *)p,strlen(p));
343
344
6.57k
  if (txt_subformat != NO_TXT)
345
6.23k
    {
346
6.23k
#define ThrowNOTXTReaderException(code_,reason_,image_)                 \
347
6.23k
      do {                                                              \
348
1.31k
        MagickFreeResourceLimitedMemory(unsigned char *,BImgBuff);      \
349
1.31k
        ThrowReaderException(code_,reason_,image_);                     \
350
0
      } while (0);
351
352
6.23k
      unsigned
353
6.23k
        x,
354
6.23k
        y;
355
356
6.23k
      unsigned
357
6.23k
        x_min,
358
6.23k
        x_max,
359
6.23k
        y_curr;
360
361
6.23k
      int
362
6.23k
        ch;
363
364
6.23k
      unsigned long
365
6.23k
        max,
366
6.23k
        i;
367
368
6.23k
      char
369
6.23k
        NumOfPlanes;
370
371
6.23k
      unsigned char
372
6.23k
        *BImgBuff=0;
373
374
6.23k
      magick_uint16_t
375
6.23k
        *WImgBuff;
376
377
6.23k
      magick_uint32_t
378
6.23k
        *DImgBuff;
379
380
6.23k
      magick_uint32_t
381
6.23k
        R,
382
6.23k
        G,
383
6.23k
        B,
384
6.23k
        A;
385
386
6.23k
      PixelPacket
387
6.23k
        *q;
388
389
6.23k
      ExtendedSignedIntegralType NextImagePos = 0;
390
391
6.23k
      ImportPixelAreaOptions
392
6.23k
        import_options;
393
394
6.23k
      if (logging)
395
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
396
0
                              "File RAW TXT type: %d", (int) txt_subformat);
397
398
6.38k
      do {
399
6.38k
        (void) SeekBlob(image,NextImagePos,SEEK_SET);
400
401
6.38k
        if(NextImagePos!=0)
402
145
          {
403
145
            if (image_info->subrange != 0)
404
145
              if (image->scene >= (image_info->subimage+image_info->subrange-1))
405
145
                break;
406
407
            /* Allocate next image structure. */
408
0
            AllocateNextImage(image_info,image);
409
0
            if(image->next == (Image *)NULL) break;
410
0
            image = SyncNextImageInList(image);
411
0
            image->columns = image->rows = 0;
412
0
            image->colors=0;
413
0
          }
414
415
6.23k
        A=0;
416
6.23k
        x=0;
417
6.23k
        y=0;
418
6.23k
        max=0;
419
420
6.23k
        switch(txt_subformat)
421
6.23k
          {
422
2
          case TXT_GM8B_HEX:
423
5
          case TXT_GM8B_HEX_Q:
424
5
            max=255;
425
5
            break;
426
3
          case TXT_GM16B_HEX:
427
5
          case TXT_GM16B_HEX_Q:
428
5
            max=65535;
429
5
            break;
430
2
          case TXT_GM32B_HEX:
431
4
          case TXT_GM32B_HEX_Q:
432
4
            max=65536;
433
4
            break;
434
6.22k
          default:
435
6.22k
            break;
436
6.23k
          }
437
438
        /*
439
          Set opacity flag.
440
        */
441
6.23k
        image->matte=MagickFalse;
442
6.23k
        if (txt_subformat >= IMAGEMAGICK_TXT_Q)
443
3.31k
          image->matte=MagickTrue;
444
445
6.23k
        if (!strncmp(p,"# ImageMagick pixel enumeration:",32))
446
359
          {
447
359
            if (sscanf(p+32,"%u,%u,%u",&x_min,&y_curr,&x_max) == 3) /* x_max-x_min+1 x_max=0 x_min=1 */
448
284
              {
449
284
                if ((x_max == 0) || (x_max < x_min))
450
278
                  ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
451
278
                if (strstr(p+32,",rgb")!=NULL)
452
122
                  {
453
122
                    x = x_min-1;
454
122
                    y = y_curr-1;
455
122
                    max = x_max;
456
122
                  }
457
278
                if (strstr(p+32,",rgba")!=NULL)
458
9
                  {
459
9
                    txt_subformat = IMAGEMAGICK_TXT_Q;
460
9
                  }
461
278
              }
462
359
          }
463
464
6.23k
        ch=0;
465
6.23k
        if (x == 0 && y == 0)
466
45.0k
          while (!EOFBlob(image))       /* auto detect sizes and num of planes */
467
39.8k
            {
468
93.2k
              while (!(ch >= '0' && ch <= '9'))
469
53.7k
                {
470
                  /* go to the begin of number */
471
53.7k
                  ch = ReadBlobByte(image);
472
53.7k
                  if (ch == EOF)
473
275
                    goto EndReading;
474
53.4k
                  if (ch == '#')
475
3.98k
                    {
476
3.98k
                      readln(image,&ch);
477
3.98k
                      continue;
478
3.98k
                    }
479
49.5k
                  if (ch == 0 || ch > 128 ||
480
49.4k
                      (ch >= 'a' && ch <= 'z') ||
481
49.4k
                      (ch >= 'A' && ch <= 'Z'))
482
51
                    {
483
113
                    TXT_FAIL:                   /* not a text data */
484
113
                      ThrowNOTXTReaderException(CoderError,ImageTypeNotSupported,image);
485
0
                    }
486
49.5k
                }
487
              /* x,y: (R,G,B) */
488
39.4k
              x_min = ReadInt(image,&ch);               /* x */
489
39.4k
              if (x_min > x)
490
2.90k
                x = x_min;
491
492
4.58M
              while (ch != ',')
493
4.55M
                {
494
4.55M
                  ch = ReadBlobByte(image);
495
4.55M
                  if (ch==EOF)
496
839
                    break;
497
4.54M
                  if (ch == 10 || ch == 13)
498
7
                    goto TXT_FAIL;
499
4.54M
                }
500
39.4k
              ch=0;
501
39.4k
              i=ReadInt(image,&ch);             /* y */
502
503
              /* Check for next image start. */
504
39.4k
              if(x_min==0 && i==0 && x>0 && y>0)
505
517
                goto EndReading;
506
507
38.9k
              if (i > y)
508
3.03k
                y=i;
509
510
4.68M
              while (ch != ':')
511
4.64M
                {
512
4.64M
                  ch = ReadBlobByte(image);
513
4.64M
                  if (ch == 10 || ch == 13)
514
8
                    goto TXT_FAIL;
515
4.64M
                  if (ch == EOF)
516
1.13k
                    break;
517
4.64M
                }
518
38.9k
              if (txt_subformat != TXT_GM8B_PLAIN2_Q)
519
842k
                while (ch != '(')
520
821k
                  {
521
821k
                    ch = ReadBlobByte(image);
522
821k
                    if (ch == 10 || ch == 13)
523
13
                      goto TXT_FAIL;
524
821k
                    if (ch == EOF)
525
602
                      break;
526
821k
                  }
527
38.9k
              ch=0;
528
38.9k
              R = ReadInt(image,&ch);           /* R */
529
38.9k
              if (R > max)
530
4.59k
                max=R;
531
532
1.35M
              while (ch != ',')
533
1.31M
                {
534
1.31M
                  ch = ReadBlobByte(image);
535
1.31M
                  if (ch == 10 || ch == 13)
536
8
                    goto TXT_FAIL;
537
1.31M
                  if (ch == EOF)
538
1.37k
                    break;
539
1.31M
                }
540
38.9k
              ch=0;
541
38.9k
              G = ReadInt(image,&ch);           /* G */
542
38.9k
              if (G > max)
543
3.06k
                max=G;
544
545
581k
              while (ch != ',')
546
544k
                {
547
544k
                  ch = ReadBlobByte(image);
548
544k
                  if (ch == 10 || ch == 13)
549
10
                    goto TXT_FAIL;
550
544k
                  if (ch == EOF)
551
1.50k
                    break;
552
544k
                }
553
38.9k
              ch=0;
554
38.9k
              B = ReadInt(image,&ch);           /* B */
555
38.9k
              if (B > max)
556
2.13k
                max=B;
557
558
38.9k
              if (txt_subformat >= IMAGEMAGICK_TXT_Q)
559
20.2k
                {
560
990k
                  while (ch != ',')
561
971k
                    {
562
971k
                      ch = ReadBlobByte(image);
563
971k
                      if (ch == 10 || ch == 13)
564
8
                        goto TXT_FAIL;
565
971k
                      if (ch == EOF)
566
876
                        break;
567
971k
                    }
568
20.2k
                  ch=0;
569
20.2k
                  A = ReadInt(image,&ch);               /* A */
570
20.2k
                  if (A > max)
571
1.00k
                    max=A;
572
20.2k
                }
573
574
38.9k
              if (txt_subformat != TXT_GM8B_PLAIN2_Q)
575
510k
                while (ch != ')')
576
489k
                  {
577
489k
                    ch = ReadBlobByte(image);
578
489k
                    if (ch == 10 || ch == 13)
579
8
                      goto TXT_FAIL;
580
489k
                    if (ch == EOF)
581
1.13k
                      break;
582
489k
                  }
583
584
38.8k
              readln(image,&ch);
585
38.8k
            }
586
587
6.11k
      EndReading:
588
6.11k
        x_min = 1;
589
6.11k
        x_max = 0;
590
6.11k
        y_curr = 0;
591
592
6.11k
        NumOfPlanes=8;
593
        /*   if (max>=    2) NumOfPlanes=2; */
594
        /*   if (max>=    4) NumOfPlanes=4; */
595
        /*   if (max>=   16) NumOfPlanes=8; */
596
6.11k
        if (max >=  256)
597
1.95k
          NumOfPlanes=16;
598
6.11k
        if (max >=65536)
599
1.21k
          NumOfPlanes=32;
600
601
6.11k
        if (logging)
602
0
          (void)LogMagickEvent(CoderEvent,GetMagickModule(),
603
0
                               "Image detected[%lu] [%u * %u]: %d", image->scene, x, y, NumOfPlanes);
604
605
6.11k
        image->depth = Min(QuantumDepth,NumOfPlanes);
606
6.11k
        ImportPixelAreaOptionsInit(&import_options);
607
6.11k
        import_options.endian = NativeEndian;
608
609
6.11k
        image->columns = x+1;
610
6.11k
        image->rows = y+1;
611
6.11k
        if (logging)
612
0
          (void)LogMagickEvent(CoderEvent,GetMagickModule(),
613
0
                               "Image Geometry: %lux%lu", image->columns, image->rows);
614
615
6.11k
        if (CheckImagePixelLimits(image, exception) != MagickPass)
616
4.94k
          ThrowNOTXTReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
617
618
        /*
619
          Validate that file size is sufficient for claimed image properties
620
        */
621
4.94k
        if (BlobIsSeekable(image))
622
4.94k
          {
623
4.94k
            magick_off_t
624
4.94k
              file_size;
625
626
4.94k
            if ((file_size=GetBlobSize(image)) != 0)
627
4.94k
              {
628
4.94k
                double
629
4.94k
                  ratio;
630
631
4.94k
                double
632
4.94k
                  packet_size=40.0;  /* Max characters in a row */
633
634
4.94k
                double
635
4.94k
                  number_pixels=(double) image->columns*image->rows;
636
637
4.94k
                ratio = (((double) number_pixels*packet_size)/file_size);
638
639
4.94k
                if (ratio > 2.0)
640
411
                  {
641
411
                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642
411
                                          "Unreasonable file size "
643
411
                                          "(ratio of pixels to file size %g)",
644
411
                                          ratio);
645
411
                    ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,
646
411
                                         image);
647
0
                  }
648
4.94k
              }
649
4.94k
          }
650
651
        /* Assure that all image pixels are initialized to black */
652
4.53k
        SetImage(image,OpaqueOpacity);
653
654
4.53k
        BImgBuff = MagickAllocateResourceLimitedClearedArray(unsigned char *,
655
4.53k
                                                             ((size_t)x+1),
656
4.53k
                                                             ((size_t)((image->matte) ? 4 : 3)
657
4.53k
                                                              * NumOfPlanes/8));
658
4.53k
        WImgBuff = (magick_uint16_t *)BImgBuff;
659
4.53k
        DImgBuff = (magick_uint32_t *)BImgBuff;
660
4.53k
        if (BImgBuff == NULL)
661
4.53k
          ThrowNOTXTReaderException(ResourceLimitError,MemoryAllocationFailed,image);
662
663
664
4.53k
        (void) SeekBlob(image,NextImagePos,SEEK_SET);
665
4.53k
        NextImagePos = 0;
666
667
        /*
668
          Load picture data
669
        */
670
62.3k
        while (!EOFBlob(image))
671
58.4k
          {
672
58.4k
            x=0;
673
134k
            while (!(ch >= '0' && ch <= '9'))
674
76.4k
              {
675
                /* move to the beginning of number */
676
76.4k
                if (EOFBlob(image))
677
263
                  goto FINISH;
678
76.1k
                ch = ReadBlobByte(image);
679
76.1k
                if (ch == '#')
680
3.36k
                  {
681
3.36k
                    readln(image,&ch);
682
3.36k
                    continue;
683
3.36k
                  }
684
72.7k
                if (ch == 0 || ch > 128 || /* Duplicate image check for data with fixed geometry previous check is skipped. */
685
72.7k
                    (ch >= 'a' && ch <= 'z') ||
686
72.7k
                    (ch >= 'A' && ch <= 'Z'))
687
30
                  {                                 /* not a text data */
688
30
                    ThrowNOTXTReaderException(CoderError,ImageTypeNotSupported,image);
689
0
                  }
690
72.7k
              }
691
692
58.1k
            x = ReadInt(image,&ch);             /* x */
693
694
3.82M
            while (ch != ',')
695
3.77M
              {
696
3.77M
                ch = ReadBlobByte(image);
697
3.77M
                if (ch == EOF)
698
648
                  break;
699
3.77M
              }
700
58.1k
            ch = 0;
701
58.1k
            y = ReadInt(image,&ch);             /* y */
702
703
            /* New image detected. */
704
58.1k
            if(x==0 && y==0 && y_curr>0 && x_max>0)
705
159
              {
706
159
                NextImagePos = TellBlob(image) - 4;
707
159
                break;
708
159
              }
709
710
3.14M
            while (ch != ':')
711
3.09M
              {
712
3.09M
                ch = ReadBlobByte(image);
713
3.09M
                if (ch == EOF)
714
1.02k
                  break;
715
3.09M
              }
716
3.53M
            while (ch != '(')
717
3.47M
              {
718
3.47M
                ch = ReadBlobByte(image);
719
3.47M
                if (ch == EOF)
720
1.23k
                  break;
721
3.47M
              }
722
58.0k
            ch=0;
723
58.0k
            R = ReadInt(image,&ch);             /* R */
724
725
828k
            while (ch != ',')
726
771k
              {
727
771k
                ch = ReadBlobByte(image);
728
771k
                if (ch == EOF)
729
1.32k
                  break;
730
771k
              }
731
58.0k
            ch=0;
732
58.0k
            G = ReadInt(image,&ch);             /* G */
733
734
1.13M
            while (ch != ',')
735
1.08M
              {
736
1.08M
                ch = ReadBlobByte(image);
737
1.08M
                if (ch == EOF)
738
1.42k
                  break;
739
1.08M
              }
740
58.0k
            ch=0;
741
58.0k
            B = ReadInt(image,&ch);             /* B */
742
743
58.0k
            if (image->matte)
744
31.5k
              {
745
1.90M
                while (ch != ',')
746
1.87M
                  {
747
1.87M
                    ch = ReadBlobByte(image);
748
1.87M
                    if (ch == EOF)
749
822
                      break;
750
1.87M
                  }
751
31.5k
                ch=0;
752
31.5k
                A = ReadInt(image,&ch);         /* A */
753
31.5k
                if (A > max)
754
231
                  max=A;
755
31.5k
              }
756
757
5.18M
            while (ch != ')')
758
5.13M
              {
759
5.13M
                ch = ReadBlobByte(image);
760
5.13M
                if (ch == EOF)
761
1.89k
                  break;
762
5.13M
              }
763
764
765
            /* A new line has been detected */
766
58.0k
            if ((y != y_curr) && (x_max >= x_min))
767
22.4k
              {
768
22.4k
                q = SetImagePixels(image,x_min,y_curr,x_max-x_min+1,1);
769
22.4k
                if (q == (PixelPacket *)NULL)
770
219
                  break;
771
772
22.2k
                if (image->matte)
773
13.2k
                  (void)ImportImagePixelArea(image,RGBAQuantum,NumOfPlanes,
774
13.2k
                                             BImgBuff + (size_t)4*x_min*(NumOfPlanes/8),
775
13.2k
                                             &import_options,0);
776
9.02k
                else
777
9.02k
                  (void)ImportImagePixelArea(image,RGBQuantum,NumOfPlanes,
778
9.02k
                                             BImgBuff + (size_t)3*x_min*(NumOfPlanes/8),
779
9.02k
                                             &import_options,0);
780
22.2k
                if (!SyncImagePixels(image))
781
0
                  break;
782
783
22.2k
                x_min = 1;
784
22.2k
                x_max = 0;
785
22.2k
                y_curr=y;
786
22.2k
              }
787
788
57.8k
            if (x < image->columns)
789
54.4k
              {
790
54.4k
                if (image->matte)
791
29.5k
                  {
792
29.5k
                    switch(NumOfPlanes)
793
29.5k
                      {
794
9.61k
                      case 8:
795
9.61k
                        BImgBuff[0+4*x] = R;
796
9.61k
                        BImgBuff[1+4*x] = G;
797
9.61k
                        BImgBuff[2+4*x] = B;
798
9.61k
                        BImgBuff[3+4*x] = 255U-A;
799
9.61k
                        break;
800
8.08k
                      case 16:
801
8.08k
                        WImgBuff[0+4*x] = R;
802
8.08k
                        WImgBuff[1+4*x] = G;
803
8.08k
                        WImgBuff[2+4*x] = B;
804
8.08k
                        WImgBuff[3+4*x] = 65535U-A;
805
8.08k
                        break;
806
11.8k
                      case 32:
807
11.8k
                        DImgBuff[0+4*x] = R;
808
11.8k
                        DImgBuff[1+4*x] = G;
809
11.8k
                        DImgBuff[2+4*x] = B;
810
11.8k
                        DImgBuff[3+4*x] = 4294967295U-A;
811
11.8k
                        break;
812
29.5k
                      }
813
29.5k
                  }
814
24.9k
                else
815
24.9k
                  {
816
24.9k
                    switch(NumOfPlanes)
817
24.9k
                      {
818
10.1k
                      case 8:
819
10.1k
                        BImgBuff[0+3*x] = R;
820
10.1k
                        BImgBuff[1+3*x] = G;
821
10.1k
                        BImgBuff[2+3*x] = B;
822
10.1k
                        break;
823
4.48k
                      case 16:
824
4.48k
                        WImgBuff[0+3*x] = R;
825
4.48k
                        WImgBuff[1+3*x] = G;
826
4.48k
                        WImgBuff[2+3*x] = B;
827
4.48k
                        break;
828
10.2k
                      case 32:
829
10.2k
                        DImgBuff[0+3*x] = R;
830
10.2k
                        DImgBuff[1+3*x] = G;
831
10.2k
                        DImgBuff[2+3*x] = B;
832
10.2k
                        break;
833
24.9k
                      }
834
24.9k
                  }
835
54.4k
                if (x_min > x_max)
836
26.6k
                  {
837
26.6k
                    x_max=x_min=x;
838
26.6k
                  }
839
27.8k
                else
840
27.8k
                  {
841
27.8k
                    if (x < x_min)
842
1.77k
                      x_min=x;
843
27.8k
                    if (x > x_max)
844
2.01k
                      x_max=x;
845
27.8k
                  }
846
54.4k
              }
847
848
57.8k
            readln(image,&ch);
849
57.8k
          }
850
851
4.50k
      FINISH:
852
4.50k
        if (x_min <= x_max)
853
4.32k
          {
854
4.32k
            q = SetImagePixels(image,x_min,y_curr,x_max-x_min+1,1);
855
4.32k
            if (q != (PixelPacket *)NULL)
856
3.76k
              {
857
3.76k
                if (image->matte)
858
1.99k
                  (void)ImportImagePixelArea(image, RGBAQuantum, NumOfPlanes,
859
1.99k
                                             BImgBuff + (size_t)4*x_min*(NumOfPlanes/8),
860
1.99k
                                             &import_options, 0);
861
1.77k
                else
862
1.77k
                  (void)ImportImagePixelArea(image, RGBQuantum, NumOfPlanes,
863
1.77k
                                             BImgBuff + (size_t)3*x_min*(NumOfPlanes/8),
864
1.77k
                                             &import_options, 0);
865
3.76k
                if (!SyncImagePixels(image))
866
0
                  {
867
0
                    if (logging)
868
0
                      (void)LogMagickEvent(CoderEvent,GetMagickModule(),
869
0
                                           "  TXT failed to sync image pixels for a row %u", y_curr);
870
0
                  }
871
872
3.76k
              }
873
4.32k
          }
874
        /* Note that DImgBuff and WImgBuff point to BImgBuff */
875
4.50k
        MagickFreeResourceLimitedMemory(unsigned char *,BImgBuff);
876
4.50k
      } while(!EOFBlob(image) && NextImagePos>0);
877
878
4.50k
      goto FINISH_TXT;
879
6.23k
    }
880
881
342
  {
882
    /*
883
      Render arbitrary ASCII text as image.
884
    */
885
342
    char
886
342
      filename[MaxTextExtent],
887
342
      geometry[MaxTextExtent];
888
889
342
    double
890
342
      dx_resolution,
891
342
      dy_resolution;
892
893
342
    Image
894
342
      *texture = (Image *) NULL;
895
896
342
    long
897
342
      count,
898
342
      line_num,
899
342
      lines_per_page,
900
342
      margins,
901
342
      page_num,
902
342
      pixels_per_line;
903
904
342
    DrawInfo
905
342
      *draw_info = (DrawInfo *) NULL;
906
907
342
    RectangleInfo
908
342
      page;
909
910
342
    TypeMetric
911
342
      metrics;
912
913
    /*
914
      Set the page geometry.
915
    */
916
342
    dx_resolution=72.0;
917
342
    dy_resolution=72.0;
918
342
    if ((image->x_resolution == 0.0) || (image->y_resolution == 0.0))
919
342
      {
920
342
        char
921
342
          density[MaxTextExtent];
922
923
342
        (void) strlcpy(density,PSDensityGeometry,sizeof(density));
924
342
        count=GetMagickDimension(density,&image->x_resolution,
925
342
                                 &image->y_resolution,NULL,NULL);
926
342
        if (count != 2)
927
0
          image->y_resolution=image->x_resolution;
928
342
      }
929
342
    SetGeometry(image,&page);
930
342
    page.width=612;
931
342
    page.height=792;
932
342
    (void) GetGeometry("612x792+43+43",&page.x,&page.y,&page.width,&page.height);
933
342
    if (image_info->page != (char *) NULL)
934
0
      (void) GetGeometry(image_info->page,&page.x,&page.y,&page.width,
935
0
                         &page.height);
936
342
    if (logging)
937
0
      (void)LogMagickEvent(CoderEvent,GetMagickModule(),
938
0
                           "Page Geometry: %lux%lu%+ld%+ld",
939
0
                           page.width,page.height,page.x,page.y);
940
    /*
941
      Initialize Image structure.
942
    */
943
342
    image->columns=(unsigned long)
944
342
      ceil(((page.width*image->x_resolution)/dx_resolution)-0.5);
945
342
    image->rows=(unsigned long)
946
342
      ceil(((page.height*image->y_resolution)/dy_resolution)-0.5);
947
948
342
    if (CheckImagePixelLimits(image, exception) != MagickPass)
949
342
      ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
950
951
342
    texture=(Image *) NULL;
952
342
    if (image_info->texture != (char *) NULL)
953
0
      {
954
0
        ImageInfo
955
0
          *clone_info;
956
957
0
        clone_info=CloneImageInfo(image_info);
958
0
        clone_info->blob=(_BlobInfoPtr_) NULL;
959
0
        clone_info->length=0;
960
0
        (void) strlcpy(clone_info->filename,image_info->texture,MaxTextExtent);
961
0
        texture=ReadImage(clone_info,exception);
962
0
        DestroyImageInfo(clone_info);
963
0
      }
964
    /*
965
      Annotate the text image.
966
    */
967
342
    (void) SetImageEx(image,OpaqueOpacity,exception);
968
342
    draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
969
342
    draw_info->fill=image_info->pen;
970
342
    (void) CloneString(&draw_info->text,image_info->filename);
971
342
    MagickFormatString(geometry,sizeof(geometry),"0x0%+ld%+ld",page.x,page.y);
972
342
    (void) CloneString(&draw_info->geometry,geometry);
973
342
    status=GetTypeMetrics(image,draw_info,&metrics);
974
342
    if (status == False)
975
342
      {
976
342
        if (texture != (Image *) NULL)
977
0
          DestroyImage(texture);
978
342
        DestroyDrawInfo(draw_info);
979
342
        ThrowReaderException(TypeError,UnableToGetTypeMetrics,image);
980
0
      }
981
0
    if (logging)
982
0
      (void)LogMagickEvent(CoderEvent,GetMagickModule(),
983
0
                           "Type metrics: ascent=%g descent=%g"
984
0
                           " height=%g max_advance=%g",
985
0
                           metrics.ascent,metrics.descent,
986
0
                           metrics.height,metrics.max_advance);
987
0
    pixels_per_line=(long) (metrics.ascent-metrics.descent);
988
0
    margins=2*page.y;
989
0
    lines_per_page=((image->rows+1)-margins)/pixels_per_line+1;
990
0
    if (logging)
991
0
      (void)LogMagickEvent(CoderEvent,GetMagickModule(),
992
0
                           "Pixels per line: %ld",
993
0
                           pixels_per_line);
994
0
    if (logging)
995
0
      (void)LogMagickEvent(CoderEvent,GetMagickModule(),
996
0
                           "Lines per page: %ld",
997
0
                           lines_per_page);
998
0
    (void) strlcpy(filename,image_info->filename,MaxTextExtent);
999
0
    if (draw_info->text != NULL)
1000
0
      *draw_info->text='\0';
1001
1002
0
    page_num=1;
1003
0
    line_num=0;
1004
0
    while (p != (char *) NULL)
1005
0
      {
1006
        /*
1007
          Annotate image with text.
1008
1009
          Text lines are concatenated so that a full page is
1010
          rendered at a time via AnnotateImage().
1011
        */
1012
0
        (void) ConcatenateString(&draw_info->text,text);
1013
0
        (void) ConcatenateString(&draw_info->text,"\n");
1014
0
        line_num++;
1015
1016
0
        if (image->previous == (Image *) NULL)
1017
0
          if (QuantumTick(line_num,lines_per_page))
1018
0
            if (!MagickMonitorFormatted(line_num,lines_per_page,&image->exception,
1019
0
                                        LoadImageText,image->filename,
1020
0
                                        image->columns,image->rows))
1021
0
              break;
1022
1023
0
        p=ReadBlobString(image,text);
1024
0
        if ((line_num < lines_per_page) && (p != (char *) NULL))
1025
0
          continue;
1026
0
        if (texture != (Image *) NULL)
1027
0
          {
1028
0
            MonitorHandler
1029
0
              handler;
1030
1031
0
            handler=SetMonitorHandler((MonitorHandler) NULL);
1032
0
            (void) TextureImage(image,texture);
1033
0
            (void) SetMonitorHandler(handler);
1034
0
          }
1035
0
        (void) AnnotateImage(image,draw_info);
1036
0
        if (p == (char *) NULL)
1037
0
          break;
1038
1039
0
        StopTimer(&image->timer);
1040
1041
0
        if (logging)
1042
0
          (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1043
0
                               "page %ld scene %ld ",page_num, image->scene);
1044
1045
0
        if ((image_info->subimage != 0) || (image_info->subrange != 0))
1046
0
          if (image->scene >= (image_info->subimage+image_info->subrange-1))
1047
0
            break;
1048
1049
        /*
1050
          Page is full-- allocate next image structure.
1051
        */
1052
0
        *draw_info->text='\0';
1053
0
        page_num++;
1054
0
        line_num=0;
1055
0
        AllocateNextImage(image_info,image);
1056
0
        if (image->next == (Image *) NULL)
1057
0
          {
1058
0
            DestroyDrawInfo(draw_info);
1059
0
            DestroyImageList(image);
1060
0
            return ((Image *) NULL);
1061
0
          }
1062
0
        image->next->columns=image->columns;
1063
0
        image->next->rows=image->rows;
1064
0
        image=SyncNextImageInList(image);
1065
0
        (void) strlcpy(image->filename,filename,MaxTextExtent);
1066
0
        (void) SetImage(image,OpaqueOpacity);
1067
0
        if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),exception,
1068
0
                                    LoadImagesText,image->filename))
1069
0
          break;
1070
0
      }
1071
1072
0
    if (texture != (Image *) NULL)
1073
0
      {
1074
0
        MonitorHandler
1075
0
          handler;
1076
1077
0
        handler=SetMonitorHandler((MonitorHandler) NULL);
1078
0
        (void) TextureImage(image,texture);
1079
0
        (void) SetMonitorHandler(handler);
1080
0
      }
1081
0
    (void) AnnotateImage(image,draw_info);
1082
0
    if (texture != (Image *) NULL)
1083
0
      DestroyImage(texture);
1084
0
    DestroyDrawInfo(draw_info);
1085
0
  }
1086
1087
4.50k
 FINISH_TXT:
1088
4.50k
  CloseBlob(image);
1089
4.50k
  {
1090
4.50k
    Image *p;
1091
4.50k
    long scene=0;
1092
1093
    /*
1094
      Rewind list, removing any empty images while rewinding.
1095
    */
1096
4.50k
    p=image;
1097
4.50k
    image=NULL;
1098
9.01k
    while(p != (Image *) NULL)
1099
4.50k
      {
1100
4.50k
        Image *tmp=p;
1101
4.50k
        if ((p->rows == 0) || (p->columns == 0)) {
1102
0
          p=p->previous;
1103
0
          DeleteImageFromList(&tmp);
1104
4.50k
        } else {
1105
4.50k
          image=p;
1106
4.50k
          p=p->previous;
1107
4.50k
        }
1108
4.50k
      }
1109
1110
    /*
1111
      Fix scene numbers
1112
    */
1113
9.01k
    for(p=image; p != (Image *)NULL; p=p->next)
1114
4.50k
      p->scene=scene++;
1115
4.50k
  }
1116
1117
4.50k
  if(logging) (void)LogMagickEvent(CoderEvent,GetMagickModule(),"return");
1118
4.50k
  return (image);
1119
0
}
1120

1121
/*
1122
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1123
%                                                                             %
1124
%                                                                             %
1125
%                                                                             %
1126
%   R e g i s t e r T X T I m a g e                                           %
1127
%                                                                             %
1128
%                                                                             %
1129
%                                                                             %
1130
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1131
%
1132
%  Method RegisterTXTImage adds attributes for the TXT image format to
1133
%  the list of supported formats.  The attributes include the image format
1134
%  tag, a method to read and/or write the format, whether the format
1135
%  supports the saving of more than one frame to the same file or blob,
1136
%  whether the format supports native in-memory I/O, and a brief
1137
%  description of the format.
1138
%
1139
%  The format of the RegisterTXTImage method is:
1140
%
1141
%      RegisterTXTImage(void)
1142
%
1143
*/
1144
ModuleExport void RegisterTXTImage(void)
1145
2
{
1146
2
  MagickInfo
1147
2
    *entry;
1148
1149
2
  entry=SetMagickInfo("TEXT");
1150
2
  entry->decoder=(DecoderHandler) ReadTXTImage;
1151
2
  entry->encoder=(EncoderHandler) WriteTXTImage;
1152
2
  entry->raw=MagickTrue;
1153
2
  entry->description="ASCII Text";
1154
2
  entry->module="TXT";
1155
2
  (void) RegisterMagickInfo(entry);
1156
1157
2
  entry=SetMagickInfo("TXT");
1158
2
  entry->decoder=(DecoderHandler) ReadTXTImage;
1159
2
  entry->encoder=(EncoderHandler) WriteTXTImage;
1160
2
  entry->seekable_stream=MagickTrue;
1161
2
  entry->description="ASCII Text";
1162
2
  entry->module="TXT";
1163
2
  (void) RegisterMagickInfo(entry);
1164
2
}
1165

1166
/*
1167
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1168
%                                                                             %
1169
%                                                                             %
1170
%                                                                             %
1171
%   U n r e g i s t e r T X T I m a g e                                       %
1172
%                                                                             %
1173
%                                                                             %
1174
%                                                                             %
1175
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176
%
1177
%  Method UnregisterTXTImage removes format registrations made by the
1178
%  TXT module from the list of supported formats.
1179
%
1180
%  The format of the UnregisterTXTImage method is:
1181
%
1182
%      UnregisterTXTImage(void)
1183
%
1184
*/
1185
ModuleExport void UnregisterTXTImage(void)
1186
0
{
1187
0
  (void) UnregisterMagickInfo("TEXT");
1188
0
  (void) UnregisterMagickInfo("TXT");
1189
0
}
1190

1191
/*
1192
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1193
%                                                                             %
1194
%                                                                             %
1195
%                                                                             %
1196
%   W r i t e T X T I m a g e                                                 %
1197
%                                                                             %
1198
%                                                                             %
1199
%                                                                             %
1200
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201
%
1202
%  Method WriteTXTImage writes the pixel values as text numbers.
1203
%
1204
%  The format of the WriteTXTImage method is:
1205
%
1206
%      unsigned int WriteTXTImage(const ImageInfo *image_info,Image *image)
1207
%
1208
%  A description of each parameter follows.
1209
%
1210
%    o status: Method WriteTXTImage return MagickTrue if the image is written.
1211
%      False is returned is there is a memory shortage or if the image file
1212
%      fails to write.
1213
%
1214
%    o image_info: Specifies a pointer to a ImageInfo structure.
1215
%
1216
%    o image:  A pointer to an Image structure.
1217
%
1218
%
1219
*/
1220
static unsigned int WriteTXTImage(const ImageInfo *image_info,Image *image)
1221
4.17k
{
1222
4.17k
  char
1223
4.17k
    buffer[MaxTextExtent],
1224
4.17k
    tuple[MaxTextExtent];
1225
1226
4.17k
  long
1227
4.17k
    y;
1228
1229
4.17k
  register const PixelPacket
1230
4.17k
    *p;
1231
1232
4.17k
  register long
1233
4.17k
    x;
1234
1235
4.17k
  size_t
1236
4.17k
    image_list_length;
1237
1238
4.17k
  unsigned int
1239
4.17k
    status;
1240
1241
4.17k
  unsigned long
1242
4.17k
    scene;
1243
1244
  /*
1245
    Open output image file.
1246
  */
1247
4.17k
  assert(image_info != (const ImageInfo *) NULL);
1248
4.17k
  assert(image_info->signature == MagickSignature);
1249
4.17k
  assert(image != (Image *) NULL);
1250
4.17k
  assert(image->signature == MagickSignature);
1251
4.17k
  status=OpenBlob(image_info,image,WriteBlobMode,&image->exception);
1252
4.17k
  if (status == False)
1253
4.17k
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
1254
4.17k
  scene=0;
1255
4.17k
  image_list_length=GetImageListLength(image);
1256
4.17k
  do
1257
4.17k
    {
1258
4.17k
      unsigned int
1259
4.17k
        depth;
1260
1261
4.17k
      if (TransformColorspace(image,RGBColorspace) == MagickFail)
1262
4.17k
        ThrowWriterException(CoderError,UnableToTransformColorspace,image);
1263
4.17k
      if (image->depth <= 8)
1264
2.49k
        depth=8;
1265
1.67k
      else if (image->depth <= 16)
1266
1.67k
        depth=16;
1267
0
      else
1268
0
        depth=32;
1269
1270
4.17k
      if ((AccessDefinition(image_info,"txt","with-im-header")))
1271
0
        {
1272
          /* Write ImageMagick txt header */
1273
1274
0
          unsigned char a = image->matte ? 'a' : ' ';
1275
1276
0
          MagickFormatString(buffer,sizeof(buffer),
1277
0
                             "# ImageMagick pixel enumeration: %.20g,%.20g,%.20g,rgb%c\n",
1278
0
                             (double) image->columns, (double) image->rows, (double) depth, a);
1279
1280
0
          (void) WriteBlobString(image,buffer);
1281
0
        }
1282
1283
      /*
1284
        Convert MIFF to TXT raster pixels.
1285
      */
1286
53.3k
      for (y=0; y < (long) image->rows; y++)
1287
49.1k
        {
1288
49.1k
          p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
1289
49.1k
          if (p == (const PixelPacket *) NULL)
1290
0
            break;
1291
1.33M
          for (x=0; x < (long) image->columns; x++)
1292
1.28M
            {
1293
1.28M
              MagickFormatString(buffer,sizeof(buffer),"%ld,%ld: ",x,y);
1294
1.28M
              (void) WriteBlobString(image,buffer);
1295
1.28M
              GetColorTuple(p,depth,image->matte,MagickFalse,tuple);
1296
1.28M
              (void) strlcat(tuple," ",sizeof(tuple));
1297
1.28M
              (void) WriteBlobString(image,tuple);
1298
              /* (void) QueryColorname(image,p,SVGCompliance,tuple,&image->exception); */
1299
1.28M
              GetColorTuple(p,depth,image->matte,MagickTrue,tuple);
1300
1.28M
              (void) WriteBlobString(image,tuple);
1301
1.28M
              (void) WriteBlobString(image,"\n");
1302
1.28M
              p++;
1303
1.28M
            }
1304
49.1k
        }
1305
4.17k
      if (image->next == (Image *) NULL)
1306
4.17k
        break;
1307
0
      image=SyncNextImageInList(image);
1308
0
      status=MagickMonitorFormatted(scene,image_list_length,
1309
0
                                    &image->exception,SaveImagesText,
1310
0
                                    image->filename);
1311
0
      if (status == False)
1312
0
        break;
1313
0
      scene++;
1314
0
    } while (image_info->adjoin);
1315
4.17k
  if (image_info->adjoin)
1316
4.17k
    while (image->previous != (Image *) NULL)
1317
0
      image=image->previous;
1318
4.17k
  status &= CloseBlob(image);
1319
4.17k
  return (status);
1320
4.17k
}