Coverage Report

Created: 2026-07-25 07:06

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
105k
{
88
105k
  int
89
105k
    ch=0;
90
91
105k
  if (pch)
92
105k
    ch=*pch;
93
0
  else
94
0
    ch=' ';
95
96
39.0M
  while (ch != 10 /* LF */ && ch != 13 /* CR */ && ch != EOF)
97
38.9M
    {
98
38.9M
      ch = ReadBlobByte(image);
99
38.9M
    }
100
105k
  if (pch)
101
105k
    *pch=ch;
102
105k
}
103
104
static long ReadInt(Image *image, int *pch)
105
535k
{
106
535k
  int
107
535k
    ch;
108
109
535k
  long
110
535k
    n;
111
112
535k
  unsigned int
113
535k
    digits;
114
115
535k
  n=0;
116
535k
  if (pch)
117
535k
    ch=*pch;
118
0
  else
119
0
    ch=' ';
120
121
2.55M
  while (isspace(ch) || ch == 0)
122
2.02M
    {
123
2.02M
      ch = ReadBlobByte(image);
124
2.02M
      if (ch == EOF)
125
11.7k
        return (0);
126
2.02M
    }
127
128
523k
  digits=0;
129
1.00M
  while ((digits < 10) && isdigit(ch))
130
483k
    {
131
483k
      n=10*n+(ch-'0');
132
483k
      ch = ReadBlobByte(image);
133
483k
      if (ch == EOF)
134
1.88k
        return (n);
135
481k
      ch &= 0xff;
136
481k
      digits++;
137
481k
    }
138
139
522k
  if (pch)
140
522k
    *pch=ch;
141
  /*  else
142
      ungetc(ch,F); */
143
144
522k
  return (n);
145
523k
}
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.76k
{
180
6.76k
  if (length < 20)
181
55
    return (NO_TXT);
182
183
6.70k
  {
184
6.70k
    unsigned long
185
6.70k
      column,
186
6.70k
      row;
187
188
6.70k
    unsigned int
189
6.70k
      red,
190
6.70k
      green,
191
6.70k
      blue,
192
6.70k
      opacity,
193
6.70k
      hex_red,
194
6.70k
      hex_green,
195
6.70k
      hex_blue,
196
6.70k
      hex_opacity;
197
198
6.70k
    int
199
6.70k
      count;
200
201
6.70k
    char
202
6.70k
      buffer[MaxTextExtent];
203
204
6.70k
    (void) memset((void *)buffer,0,MaxTextExtent);
205
6.70k
    (void) memcpy((void *)buffer,(const void *)magick,Min(MaxTextExtent-1,length));
206
207
6.70k
    if (!strncmp(buffer,"# ImageMagick pixel enumeration:",32))
208
375
      return IMAGEMAGICK_TXT;
209
210
6.33k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u) #%02X%02X%02X",
211
6.33k
                 &column, &row, &red, &green, &blue, &hex_red, &hex_green,
212
6.33k
                 &hex_blue);
213
6.33k
    if ((count == 8) && (column == 0) && (row == 0) && (red == hex_red) &&
214
407
        (green == hex_green) && (blue == hex_blue))
215
4
      return (TXT_GM8B_HEX);
216
217
6.32k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u) #%04X%04X%04X",
218
6.32k
                 &column, &row, &red, &green, &blue, &hex_red, &hex_green,
219
6.32k
                 &hex_blue);
220
6.32k
    if ((count == 8) && (column == 0) && (row == 0) && (red == hex_red) &&
221
320
        (green == hex_green) && (blue == hex_blue))
222
3
      return (TXT_GM16B_HEX);
223
224
6.32k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u) #%08X%08X%08X",
225
6.32k
                 &column, &row, &red, &green, &blue, &hex_red, &hex_green,
226
6.32k
                 &hex_blue);
227
6.32k
    if ((count == 8) && (column == 0) && (row == 0) && (red == hex_red) &&
228
310
        (green == hex_green) && (blue == hex_blue))
229
2
      return (TXT_GM32B_HEX);
230
231
6.32k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u) #%02X%02X%02X%02X",
232
6.32k
                 &column, &row, &red, &green, &blue, &opacity, &hex_red,
233
6.32k
                 &hex_green, &hex_blue, &hex_opacity);
234
6.32k
    if ((count == 10) && (column == 0) && (row == 0) && (red == hex_red) &&
235
716
        (green == hex_green) && (blue == hex_blue) && (opacity == hex_opacity))
236
3
      return (TXT_GM8B_HEX_Q);
237
238
6.32k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u) #%04X%04X%04X%04X",
239
6.32k
                 &column, &row, &red, &green, &blue, &opacity, &hex_red,
240
6.32k
                 &hex_green, &hex_blue, &hex_opacity);
241
6.32k
    if ((count == 10) && (column == 0) && (row == 0) && (red == hex_red) &&
242
442
        (green == hex_green) && (blue == hex_blue) && (opacity == hex_opacity))
243
2
      return (TXT_GM16B_HEX_Q);
244
245
6.31k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u) #%08X%08X%08X%08X",
246
6.31k
                 &column, &row, &red, &green, &blue, &opacity, &hex_red,
247
6.31k
                 &hex_green, &hex_blue, &hex_opacity);
248
6.31k
    if ((count == 10) && (column == 0) && (row == 0) && (red == hex_red) &&
249
399
        (green == hex_green) && (blue == hex_blue) && (opacity == hex_opacity))
250
2
      return (TXT_GM32B_HEX_Q);
251
252
6.31k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u, %u)",
253
6.31k
                 &column, &row, &red, &green, &blue, &opacity);
254
6.31k
    if (count==6)
255
1.86k
      return TXT_GM8B_PLAIN_Q;
256
257
4.45k
    count=sscanf(buffer,"%lu,%lu: %u, %u, %u, %u",
258
4.45k
                 &column, &row, &red, &green, &blue, &opacity);
259
4.45k
    if (count==6)
260
1.49k
      return TXT_GM8B_PLAIN2_Q;
261
262
2.95k
    count=sscanf(buffer,"%lu,%lu: (%u, %u, %u)",
263
2.95k
                 &column, &row, &red, &green, &blue);
264
2.95k
    if (count==5)
265
2.17k
      return TXT_GM8B_PLAIN;
266
267
781
    count=sscanf(buffer,"%lu,%lu: %u, %u, %u",
268
781
                 &column, &row, &red, &green, &blue);
269
781
    if (count==5)
270
499
      return TXT_GM8B_PLAIN2;
271
272
781
  }
273
282
  return (NO_TXT);
274
781
}
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.77k
{
309
6.77k
  char
310
6.77k
    *p,
311
6.77k
    text[MaxTextExtent];
312
313
6.77k
  Image
314
6.77k
    *image;
315
316
6.77k
  TXT_TYPE
317
6.77k
    txt_subformat;
318
319
6.77k
  MagickPassFail
320
6.77k
    status;
321
322
6.77k
  MagickBool
323
6.77k
    logging;
324
325
  /*
326
    Open image file.
327
  */
328
6.77k
  assert(image_info != (const ImageInfo *) NULL);
329
6.77k
  assert(image_info->signature == MagickSignature);
330
6.77k
  assert(exception != (ExceptionInfo *) NULL);
331
6.77k
  assert(exception->signature == MagickSignature);
332
333
6.77k
  logging = IsEventLogged(CoderEvent);
334
6.77k
  image=AllocateImage(image_info);
335
6.77k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
336
6.77k
  if (status == False)
337
6.77k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
338
339
6.77k
  p = ReadBlobString(image,text);
340
6.77k
  if (p == NULL)
341
6.76k
    ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
342
6.76k
  txt_subformat = IsTXT((unsigned char *)p,strlen(p));
343
344
6.76k
  if (txt_subformat != NO_TXT)
345
6.42k
    {
346
6.42k
#define ThrowNOTXTReaderException(code_,reason_,image_)                 \
347
6.42k
      do {                                                              \
348
1.35k
        MagickFreeResourceLimitedMemory(unsigned char *,BImgBuff);      \
349
1.35k
        ThrowReaderException(code_,reason_,image_);                     \
350
0
      } while (0);
351
352
6.42k
      unsigned
353
6.42k
        x,
354
6.42k
        y;
355
356
6.42k
      unsigned
357
6.42k
        x_min,
358
6.42k
        x_max,
359
6.42k
        y_curr;
360
361
6.42k
      int
362
6.42k
        ch;
363
364
6.42k
      unsigned long
365
6.42k
        max,
366
6.42k
        i;
367
368
6.42k
      char
369
6.42k
        NumOfPlanes;
370
371
6.42k
      unsigned char
372
6.42k
        *BImgBuff=0;
373
374
6.42k
      magick_uint16_t
375
6.42k
        *WImgBuff;
376
377
6.42k
      magick_uint32_t
378
6.42k
        *DImgBuff;
379
380
6.42k
      magick_uint32_t
381
6.42k
        R,
382
6.42k
        G,
383
6.42k
        B,
384
6.42k
        A;
385
386
6.42k
      PixelPacket
387
6.42k
        *q;
388
389
6.42k
      ExtendedSignedIntegralType NextImagePos = 0;
390
391
6.42k
      ImportPixelAreaOptions
392
6.42k
        import_options;
393
394
6.42k
      if (logging)
395
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
396
0
                              "File RAW TXT type: %d", (int) txt_subformat);
397
398
6.56k
      do {
399
6.56k
        (void) SeekBlob(image,NextImagePos,SEEK_SET);
400
401
6.56k
        if(NextImagePos!=0)
402
138
          {
403
138
            if (image_info->subrange != 0)
404
138
              if (image->scene >= (image_info->subimage+image_info->subrange-1))
405
138
                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.42k
        A=0;
416
6.42k
        x=0;
417
6.42k
        y=0;
418
6.42k
        max=0;
419
420
6.42k
        switch(txt_subformat)
421
6.42k
          {
422
4
          case TXT_GM8B_HEX:
423
7
          case TXT_GM8B_HEX_Q:
424
7
            max=255;
425
7
            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.40k
          default:
435
6.40k
            break;
436
6.42k
          }
437
438
        /*
439
          Set opacity flag.
440
        */
441
6.42k
        image->matte=MagickFalse;
442
6.42k
        if (txt_subformat >= IMAGEMAGICK_TXT_Q)
443
3.36k
          image->matte=MagickTrue;
444
445
6.42k
        if (!strncmp(p,"# ImageMagick pixel enumeration:",32))
446
375
          {
447
375
            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
298
              {
449
298
                if ((x_max == 0) || (x_max < x_min))
450
291
                  ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
451
291
                if (strstr(p+32,",rgb")!=NULL)
452
130
                  {
453
130
                    x = x_min-1;
454
130
                    y = y_curr-1;
455
130
                    max = x_max;
456
130
                  }
457
291
                if (strstr(p+32,",rgba")!=NULL)
458
11
                  {
459
11
                    txt_subformat = IMAGEMAGICK_TXT_Q;
460
11
                  }
461
291
              }
462
375
          }
463
464
6.41k
        ch=0;
465
6.41k
        if (x == 0 && y == 0)
466
42.3k
          while (!EOFBlob(image))       /* auto detect sizes and num of planes */
467
37.0k
            {
468
89.4k
              while (!(ch >= '0' && ch <= '9'))
469
52.7k
                {
470
                  /* go to the begin of number */
471
52.7k
                  ch = ReadBlobByte(image);
472
52.7k
                  if (ch == EOF)
473
288
                    goto EndReading;
474
52.4k
                  if (ch == '#')
475
4.98k
                    {
476
4.98k
                      readln(image,&ch);
477
4.98k
                      continue;
478
4.98k
                    }
479
47.4k
                  if (ch == 0 || ch > 128 ||
480
47.4k
                      (ch >= 'a' && ch <= 'z') ||
481
47.4k
                      (ch >= 'A' && ch <= 'Z'))
482
62
                    {
483
122
                    TXT_FAIL:                   /* not a text data */
484
122
                      ThrowNOTXTReaderException(CoderError,ImageTypeNotSupported,image);
485
0
                    }
486
47.4k
                }
487
              /* x,y: (R,G,B) */
488
36.6k
              x_min = ReadInt(image,&ch);               /* x */
489
36.6k
              if (x_min > x)
490
2.93k
                x = x_min;
491
492
4.54M
              while (ch != ',')
493
4.50M
                {
494
4.50M
                  ch = ReadBlobByte(image);
495
4.50M
                  if (ch==EOF)
496
859
                    break;
497
4.50M
                  if (ch == 10 || ch == 13)
498
9
                    goto TXT_FAIL;
499
4.50M
                }
500
36.6k
              ch=0;
501
36.6k
              i=ReadInt(image,&ch);             /* y */
502
503
              /* Check for next image start. */
504
36.6k
              if(x_min==0 && i==0 && x>0 && y>0)
505
543
                goto EndReading;
506
507
36.1k
              if (i > y)
508
3.08k
                y=i;
509
510
3.71M
              while (ch != ':')
511
3.68M
                {
512
3.68M
                  ch = ReadBlobByte(image);
513
3.68M
                  if (ch == 10 || ch == 13)
514
6
                    goto TXT_FAIL;
515
3.68M
                  if (ch == EOF)
516
1.12k
                    break;
517
3.68M
                }
518
36.1k
              if (txt_subformat != TXT_GM8B_PLAIN2_Q)
519
716k
                while (ch != '(')
520
696k
                  {
521
696k
                    ch = ReadBlobByte(image);
522
696k
                    if (ch == 10 || ch == 13)
523
17
                      goto TXT_FAIL;
524
696k
                    if (ch == EOF)
525
586
                      break;
526
696k
                  }
527
36.1k
              ch=0;
528
36.1k
              R = ReadInt(image,&ch);           /* R */
529
36.1k
              if (R > max)
530
4.70k
                max=R;
531
532
1.47M
              while (ch != ',')
533
1.43M
                {
534
1.43M
                  ch = ReadBlobByte(image);
535
1.43M
                  if (ch == 10 || ch == 13)
536
7
                    goto TXT_FAIL;
537
1.43M
                  if (ch == EOF)
538
1.36k
                    break;
539
1.43M
                }
540
36.0k
              ch=0;
541
36.0k
              G = ReadInt(image,&ch);           /* G */
542
36.0k
              if (G > max)
543
3.17k
                max=G;
544
545
528k
              while (ch != ',')
546
493k
                {
547
493k
                  ch = ReadBlobByte(image);
548
493k
                  if (ch == 10 || ch == 13)
549
9
                    goto TXT_FAIL;
550
493k
                  if (ch == EOF)
551
1.48k
                    break;
552
493k
                }
553
36.0k
              ch=0;
554
36.0k
              B = ReadInt(image,&ch);           /* B */
555
36.0k
              if (B > max)
556
2.16k
                max=B;
557
558
36.0k
              if (txt_subformat >= IMAGEMAGICK_TXT_Q)
559
18.9k
                {
560
1.13M
                  while (ch != ',')
561
1.11M
                    {
562
1.11M
                      ch = ReadBlobByte(image);
563
1.11M
                      if (ch == 10 || ch == 13)
564
6
                        goto TXT_FAIL;
565
1.11M
                      if (ch == EOF)
566
869
                        break;
567
1.11M
                    }
568
18.9k
                  ch=0;
569
18.9k
                  A = ReadInt(image,&ch);               /* A */
570
18.9k
                  if (A > max)
571
1.05k
                    max=A;
572
18.9k
                }
573
574
36.0k
              if (txt_subformat != TXT_GM8B_PLAIN2_Q)
575
513k
                while (ch != ')')
576
494k
                  {
577
494k
                    ch = ReadBlobByte(image);
578
494k
                    if (ch == 10 || ch == 13)
579
6
                      goto TXT_FAIL;
580
494k
                    if (ch == EOF)
581
1.12k
                      break;
582
494k
                  }
583
584
36.0k
              readln(image,&ch);
585
36.0k
            }
586
587
6.29k
      EndReading:
588
6.29k
        x_min = 1;
589
6.29k
        x_max = 0;
590
6.29k
        y_curr = 0;
591
592
6.29k
        NumOfPlanes=8;
593
        /*   if (max>=    2) NumOfPlanes=2; */
594
        /*   if (max>=    4) NumOfPlanes=4; */
595
        /*   if (max>=   16) NumOfPlanes=8; */
596
6.29k
        if (max >=  256)
597
2.01k
          NumOfPlanes=16;
598
6.29k
        if (max >=65536)
599
1.22k
          NumOfPlanes=32;
600
601
6.29k
        if (logging)
602
0
          (void)LogMagickEvent(CoderEvent,GetMagickModule(),
603
0
                               "Image detected[%lu] [%u * %u]: %d", image->scene, x, y, NumOfPlanes);
604
605
6.29k
        image->depth = Min(QuantumDepth,NumOfPlanes);
606
6.29k
        ImportPixelAreaOptionsInit(&import_options);
607
6.29k
        import_options.endian = NativeEndian;
608
609
6.29k
        image->columns = x+1;
610
6.29k
        image->rows = y+1;
611
6.29k
        if (logging)
612
0
          (void)LogMagickEvent(CoderEvent,GetMagickModule(),
613
0
                               "Image Geometry: %lux%lu", image->columns, image->rows);
614
615
6.29k
        if (CheckImagePixelLimits(image, exception) != MagickPass)
616
5.10k
          ThrowNOTXTReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
617
618
        /*
619
          Validate that file size is sufficient for claimed image properties
620
        */
621
5.10k
        if (BlobIsSeekable(image))
622
5.10k
          {
623
5.10k
            magick_off_t
624
5.10k
              file_size;
625
626
5.10k
            if ((file_size=GetBlobSize(image)) != 0)
627
5.10k
              {
628
5.10k
                double
629
5.10k
                  ratio;
630
631
5.10k
                double
632
5.10k
                  packet_size=40.0;  /* Max characters in a row */
633
634
5.10k
                double
635
5.10k
                  number_pixels=(double) image->columns*image->rows;
636
637
5.10k
                ratio = (((double) number_pixels*packet_size)/file_size);
638
639
5.10k
                if (ratio > 2.0)
640
407
                  {
641
407
                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642
407
                                          "Unreasonable file size "
643
407
                                          "(ratio of pixels to file size %g)",
644
407
                                          ratio);
645
407
                    ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,
646
407
                                         image);
647
0
                  }
648
5.10k
              }
649
5.10k
          }
650
651
        /* Assure that all image pixels are initialized to black */
652
4.69k
        SetImage(image,OpaqueOpacity);
653
654
4.69k
        BImgBuff = MagickAllocateResourceLimitedClearedArray(unsigned char *,
655
4.69k
                                                             ((size_t)x+1),
656
4.69k
                                                             ((size_t)((image->matte) ? 4 : 3)
657
4.69k
                                                              * NumOfPlanes/8));
658
4.69k
        WImgBuff = (magick_uint16_t *)BImgBuff;
659
4.69k
        DImgBuff = (magick_uint32_t *)BImgBuff;
660
4.69k
        if (BImgBuff == NULL)
661
4.69k
          ThrowNOTXTReaderException(ResourceLimitError,MemoryAllocationFailed,image);
662
663
664
4.69k
        (void) SeekBlob(image,NextImagePos,SEEK_SET);
665
4.69k
        NextImagePos = 0;
666
667
        /*
668
          Load picture data
669
        */
670
64.5k
        while (!EOFBlob(image))
671
60.5k
          {
672
60.5k
            x=0;
673
140k
            while (!(ch >= '0' && ch <= '9'))
674
80.1k
              {
675
                /* move to the beginning of number */
676
80.1k
                if (EOFBlob(image))
677
276
                  goto FINISH;
678
79.8k
                ch = ReadBlobByte(image);
679
79.8k
                if (ch == '#')
680
4.19k
                  {
681
4.19k
                    readln(image,&ch);
682
4.19k
                    continue;
683
4.19k
                  }
684
75.6k
                if (ch == 0 || ch > 128 || /* Duplicate image check for data with fixed geometry previous check is skipped. */
685
75.6k
                    (ch >= 'a' && ch <= 'z') ||
686
75.6k
                    (ch >= 'A' && ch <= 'Z'))
687
33
                  {                                 /* not a text data */
688
33
                    ThrowNOTXTReaderException(CoderError,ImageTypeNotSupported,image);
689
0
                  }
690
75.6k
              }
691
692
60.1k
            x = ReadInt(image,&ch);             /* x */
693
694
3.99M
            while (ch != ',')
695
3.93M
              {
696
3.93M
                ch = ReadBlobByte(image);
697
3.93M
                if (ch == EOF)
698
655
                  break;
699
3.93M
              }
700
60.1k
            ch = 0;
701
60.1k
            y = ReadInt(image,&ch);             /* y */
702
703
            /* New image detected. */
704
60.1k
            if(x==0 && y==0 && y_curr>0 && x_max>0)
705
151
              {
706
151
                NextImagePos = TellBlob(image) - 4;
707
151
                break;
708
151
              }
709
710
3.16M
            while (ch != ':')
711
3.10M
              {
712
3.10M
                ch = ReadBlobByte(image);
713
3.10M
                if (ch == EOF)
714
1.03k
                  break;
715
3.10M
              }
716
3.98M
            while (ch != '(')
717
3.93M
              {
718
3.93M
                ch = ReadBlobByte(image);
719
3.93M
                if (ch == EOF)
720
1.26k
                  break;
721
3.93M
              }
722
60.0k
            ch=0;
723
60.0k
            R = ReadInt(image,&ch);             /* R */
724
725
714k
            while (ch != ',')
726
655k
              {
727
655k
                ch = ReadBlobByte(image);
728
655k
                if (ch == EOF)
729
1.34k
                  break;
730
655k
              }
731
60.0k
            ch=0;
732
60.0k
            G = ReadInt(image,&ch);             /* G */
733
734
1.14M
            while (ch != ',')
735
1.08M
              {
736
1.08M
                ch = ReadBlobByte(image);
737
1.08M
                if (ch == EOF)
738
1.44k
                  break;
739
1.08M
              }
740
60.0k
            ch=0;
741
60.0k
            B = ReadInt(image,&ch);             /* B */
742
743
60.0k
            if (image->matte)
744
34.5k
              {
745
2.46M
                while (ch != ',')
746
2.42M
                  {
747
2.42M
                    ch = ReadBlobByte(image);
748
2.42M
                    if (ch == EOF)
749
842
                      break;
750
2.42M
                  }
751
34.5k
                ch=0;
752
34.5k
                A = ReadInt(image,&ch);         /* A */
753
34.5k
                if (A > max)
754
249
                  max=A;
755
34.5k
              }
756
757
6.50M
            while (ch != ')')
758
6.44M
              {
759
6.44M
                ch = ReadBlobByte(image);
760
6.44M
                if (ch == EOF)
761
1.92k
                  break;
762
6.44M
              }
763
764
765
            /* A new line has been detected */
766
60.0k
            if ((y != y_curr) && (x_max >= x_min))
767
23.2k
              {
768
23.2k
                q = SetImagePixels(image,x_min,y_curr,x_max-x_min+1,1);
769
23.2k
                if (q == (PixelPacket *)NULL)
770
219
                  break;
771
772
23.0k
                if (image->matte)
773
14.6k
                  (void)ImportImagePixelArea(image,RGBAQuantum,NumOfPlanes,
774
14.6k
                                             BImgBuff + (size_t)4*x_min*(NumOfPlanes/8),
775
14.6k
                                             &import_options,0);
776
8.41k
                else
777
8.41k
                  (void)ImportImagePixelArea(image,RGBQuantum,NumOfPlanes,
778
8.41k
                                             BImgBuff + (size_t)3*x_min*(NumOfPlanes/8),
779
8.41k
                                             &import_options,0);
780
23.0k
                if (!SyncImagePixels(image))
781
0
                  break;
782
783
23.0k
                x_min = 1;
784
23.0k
                x_max = 0;
785
23.0k
                y_curr=y;
786
23.0k
              }
787
788
59.8k
            if (x < image->columns)
789
56.2k
              {
790
56.2k
                if (image->matte)
791
32.6k
                  {
792
32.6k
                    switch(NumOfPlanes)
793
32.6k
                      {
794
10.6k
                      case 8:
795
10.6k
                        BImgBuff[0+4*x] = R;
796
10.6k
                        BImgBuff[1+4*x] = G;
797
10.6k
                        BImgBuff[2+4*x] = B;
798
10.6k
                        BImgBuff[3+4*x] = 255U-A;
799
10.6k
                        break;
800
8.40k
                      case 16:
801
8.40k
                        WImgBuff[0+4*x] = R;
802
8.40k
                        WImgBuff[1+4*x] = G;
803
8.40k
                        WImgBuff[2+4*x] = B;
804
8.40k
                        WImgBuff[3+4*x] = 65535U-A;
805
8.40k
                        break;
806
13.5k
                      case 32:
807
13.5k
                        DImgBuff[0+4*x] = R;
808
13.5k
                        DImgBuff[1+4*x] = G;
809
13.5k
                        DImgBuff[2+4*x] = B;
810
13.5k
                        DImgBuff[3+4*x] = 4294967295U-A;
811
13.5k
                        break;
812
32.6k
                      }
813
32.6k
                  }
814
23.6k
                else
815
23.6k
                  {
816
23.6k
                    switch(NumOfPlanes)
817
23.6k
                      {
818
9.58k
                      case 8:
819
9.58k
                        BImgBuff[0+3*x] = R;
820
9.58k
                        BImgBuff[1+3*x] = G;
821
9.58k
                        BImgBuff[2+3*x] = B;
822
9.58k
                        break;
823
4.30k
                      case 16:
824
4.30k
                        WImgBuff[0+3*x] = R;
825
4.30k
                        WImgBuff[1+3*x] = G;
826
4.30k
                        WImgBuff[2+3*x] = B;
827
4.30k
                        break;
828
9.75k
                      case 32:
829
9.75k
                        DImgBuff[0+3*x] = R;
830
9.75k
                        DImgBuff[1+3*x] = G;
831
9.75k
                        DImgBuff[2+3*x] = B;
832
9.75k
                        break;
833
23.6k
                      }
834
23.6k
                  }
835
56.2k
                if (x_min > x_max)
836
27.5k
                  {
837
27.5k
                    x_max=x_min=x;
838
27.5k
                  }
839
28.7k
                else
840
28.7k
                  {
841
28.7k
                    if (x < x_min)
842
1.87k
                      x_min=x;
843
28.7k
                    if (x > x_max)
844
2.01k
                      x_max=x;
845
28.7k
                  }
846
56.2k
              }
847
848
59.8k
            readln(image,&ch);
849
59.8k
          }
850
851
4.66k
      FINISH:
852
4.66k
        if (x_min <= x_max)
853
4.47k
          {
854
4.47k
            q = SetImagePixels(image,x_min,y_curr,x_max-x_min+1,1);
855
4.47k
            if (q != (PixelPacket *)NULL)
856
3.93k
              {
857
3.93k
                if (image->matte)
858
2.08k
                  (void)ImportImagePixelArea(image, RGBAQuantum, NumOfPlanes,
859
2.08k
                                             BImgBuff + (size_t)4*x_min*(NumOfPlanes/8),
860
2.08k
                                             &import_options, 0);
861
1.84k
                else
862
1.84k
                  (void)ImportImagePixelArea(image, RGBQuantum, NumOfPlanes,
863
1.84k
                                             BImgBuff + (size_t)3*x_min*(NumOfPlanes/8),
864
1.84k
                                             &import_options, 0);
865
3.93k
                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.93k
              }
873
4.47k
          }
874
        /* Note that DImgBuff and WImgBuff point to BImgBuff */
875
4.66k
        MagickFreeResourceLimitedMemory(unsigned char *,BImgBuff);
876
4.66k
      } while(!EOFBlob(image) && NextImagePos>0);
877
878
4.66k
      goto FINISH_TXT;
879
6.42k
    }
880
881
337
  {
882
    /*
883
      Render arbitrary ASCII text as image.
884
    */
885
337
    char
886
337
      filename[MaxTextExtent],
887
337
      geometry[MaxTextExtent];
888
889
337
    double
890
337
      dx_resolution,
891
337
      dy_resolution;
892
893
337
    Image
894
337
      *texture = (Image *) NULL;
895
896
337
    long
897
337
      count,
898
337
      line_num,
899
337
      lines_per_page,
900
337
      margins,
901
337
      page_num,
902
337
      pixels_per_line;
903
904
337
    DrawInfo
905
337
      *draw_info = (DrawInfo *) NULL;
906
907
337
    RectangleInfo
908
337
      page;
909
910
337
    TypeMetric
911
337
      metrics;
912
913
    /*
914
      Set the page geometry.
915
    */
916
337
    dx_resolution=72.0;
917
337
    dy_resolution=72.0;
918
337
    if ((image->x_resolution == 0.0) || (image->y_resolution == 0.0))
919
337
      {
920
337
        char
921
337
          density[MaxTextExtent];
922
923
337
        (void) strlcpy(density,PSDensityGeometry,sizeof(density));
924
337
        count=GetMagickDimension(density,&image->x_resolution,
925
337
                                 &image->y_resolution,NULL,NULL);
926
337
        if (count != 2)
927
0
          image->y_resolution=image->x_resolution;
928
337
      }
929
337
    SetGeometry(image,&page);
930
337
    page.width=612;
931
337
    page.height=792;
932
337
    (void) GetGeometry("612x792+43+43",&page.x,&page.y,&page.width,&page.height);
933
337
    if (image_info->page != (char *) NULL)
934
0
      (void) GetGeometry(image_info->page,&page.x,&page.y,&page.width,
935
0
                         &page.height);
936
337
    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
337
    image->columns=(unsigned long)
944
337
      ceil(((page.width*image->x_resolution)/dx_resolution)-0.5);
945
337
    image->rows=(unsigned long)
946
337
      ceil(((page.height*image->y_resolution)/dy_resolution)-0.5);
947
948
337
    if (CheckImagePixelLimits(image, exception) != MagickPass)
949
337
      ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
950
951
337
    texture=(Image *) NULL;
952
337
    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=(void *) 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
337
    (void) SetImageEx(image,OpaqueOpacity,exception);
968
337
    draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
969
337
    draw_info->fill=image_info->pen;
970
337
    (void) CloneString(&draw_info->text,image_info->filename);
971
337
    MagickFormatString(geometry,sizeof(geometry),"0x0%+ld%+ld",page.x,page.y);
972
337
    (void) CloneString(&draw_info->geometry,geometry);
973
337
    status=GetTypeMetrics(image,draw_info,&metrics);
974
337
    if (status == False)
975
337
      {
976
337
        if (texture != (Image *) NULL)
977
0
          DestroyImage(texture);
978
337
        DestroyDrawInfo(draw_info);
979
337
        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.66k
 FINISH_TXT:
1088
4.66k
  CloseBlob(image);
1089
4.66k
  {
1090
4.66k
    Image *p;
1091
4.66k
    long scene=0;
1092
1093
    /*
1094
      Rewind list, removing any empty images while rewinding.
1095
    */
1096
4.66k
    p=image;
1097
4.66k
    image=NULL;
1098
9.32k
    while(p != (Image *) NULL)
1099
4.66k
      {
1100
4.66k
        Image *tmp=p;
1101
4.66k
        if ((p->rows == 0) || (p->columns == 0)) {
1102
0
          p=p->previous;
1103
0
          DeleteImageFromList(&tmp);
1104
4.66k
        } else {
1105
4.66k
          image=p;
1106
4.66k
          p=p->previous;
1107
4.66k
        }
1108
4.66k
      }
1109
1110
    /*
1111
      Fix scene numbers
1112
    */
1113
9.32k
    for(p=image; p != (Image *)NULL; p=p->next)
1114
4.66k
      p->scene=scene++;
1115
4.66k
  }
1116
1117
4.66k
  if(logging) (void)LogMagickEvent(CoderEvent,GetMagickModule(),"return");
1118
4.66k
  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.32k
{
1222
4.32k
  char
1223
4.32k
    buffer[MaxTextExtent],
1224
4.32k
    tuple[MaxTextExtent];
1225
1226
4.32k
  long
1227
4.32k
    y;
1228
1229
4.32k
  register const PixelPacket
1230
4.32k
    *p;
1231
1232
4.32k
  register long
1233
4.32k
    x;
1234
1235
4.32k
  size_t
1236
4.32k
    image_list_length;
1237
1238
4.32k
  unsigned int
1239
4.32k
    status;
1240
1241
4.32k
  unsigned long
1242
4.32k
    scene;
1243
1244
  /*
1245
    Open output image file.
1246
  */
1247
4.32k
  assert(image_info != (const ImageInfo *) NULL);
1248
4.32k
  assert(image_info->signature == MagickSignature);
1249
4.32k
  assert(image != (Image *) NULL);
1250
4.32k
  assert(image->signature == MagickSignature);
1251
4.32k
  status=OpenBlob(image_info,image,WriteBlobMode,&image->exception);
1252
4.32k
  if (status == False)
1253
4.32k
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
1254
4.32k
  scene=0;
1255
4.32k
  image_list_length=GetImageListLength(image);
1256
4.32k
  do
1257
4.32k
    {
1258
4.32k
      unsigned int
1259
4.32k
        depth;
1260
1261
4.32k
      if (TransformColorspace(image,RGBColorspace) == MagickFail)
1262
4.32k
        ThrowWriterException(CoderError,UnableToTransformColorspace,image);
1263
4.32k
      if (image->depth <= 8)
1264
2.57k
        depth=8;
1265
1.74k
      else if (image->depth <= 16)
1266
1.74k
        depth=16;
1267
0
      else
1268
0
        depth=32;
1269
1270
4.32k
      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
61.0k
      for (y=0; y < (long) image->rows; y++)
1287
56.7k
        {
1288
56.7k
          p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
1289
56.7k
          if (p == (const PixelPacket *) NULL)
1290
0
            break;
1291
1.37M
          for (x=0; x < (long) image->columns; x++)
1292
1.31M
            {
1293
1.31M
              MagickFormatString(buffer,sizeof(buffer),"%ld,%ld: ",x,y);
1294
1.31M
              (void) WriteBlobString(image,buffer);
1295
1.31M
              GetColorTuple(p,depth,image->matte,MagickFalse,tuple);
1296
1.31M
              (void) strlcat(tuple," ",sizeof(tuple));
1297
1.31M
              (void) WriteBlobString(image,tuple);
1298
              /* (void) QueryColorname(image,p,SVGCompliance,tuple,&image->exception); */
1299
1.31M
              GetColorTuple(p,depth,image->matte,MagickTrue,tuple);
1300
1.31M
              (void) WriteBlobString(image,tuple);
1301
1.31M
              (void) WriteBlobString(image,"\n");
1302
1.31M
              p++;
1303
1.31M
            }
1304
56.7k
        }
1305
4.32k
      if (image->next == (Image *) NULL)
1306
4.32k
        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.32k
  if (image_info->adjoin)
1316
4.32k
    while (image->previous != (Image *) NULL)
1317
0
      image=image->previous;
1318
4.32k
  status &= CloseBlob(image);
1319
4.32k
  return (status);
1320
4.32k
}