Coverage Report

Created: 2026-05-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/coders/ftxt.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                        FFFFF  TTTTT  X   X  TTTTT                           %
7
%                        F        T     X X     T                             %
8
%                        FFF      T      X      T                             %
9
%                        F        T     X X     T                             %
10
%                        F        T    X   X    T                             %
11
%                                                                             %
12
%                 Read and Write Pixels as Formatted Text                     %
13
%                                                                             %
14
%                               Software Design                               %
15
%                             snibgo (Alan Gibson)                            %
16
%                                 October 2021                                %
17
%                                                                             %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/license/                                         %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
%
38
*/
39

40
#include "MagickCore/studio.h"
41
#include "MagickCore/annotate.h"
42
#include "MagickCore/artifact.h"
43
#include "MagickCore/attribute.h"
44
#include "MagickCore/blob.h"
45
#include "MagickCore/blob-private.h"
46
#include "MagickCore/cache.h"
47
#include "MagickCore/channel.h"
48
#include "MagickCore/color.h"
49
#include "MagickCore/color-private.h"
50
#include "MagickCore/colorspace.h"
51
#include "MagickCore/constitute.h"
52
#include "MagickCore/draw.h"
53
#include "MagickCore/exception.h"
54
#include "MagickCore/exception-private.h"
55
#include "MagickCore/geometry.h"
56
#include "MagickCore/image.h"
57
#include "MagickCore/image-private.h"
58
#include "MagickCore/list.h"
59
#include "MagickCore/magick.h"
60
#include "MagickCore/memory_.h"
61
#include "MagickCore/module.h"
62
#include "MagickCore/monitor.h"
63
#include "MagickCore/monitor-private.h"
64
#include "MagickCore/option.h"
65
#include "MagickCore/pixel-accessor.h"
66
#include "MagickCore/quantum-private.h"
67
#include "MagickCore/static.h"
68
#include "MagickCore/statistic.h"
69
#include "MagickCore/string_.h"
70
#include "MagickCore/token.h"
71
#include "coders/ftxt.h"
72

73
/*
74
  Define declarations.
75
*/
76
14.7k
#define chEsc '\\'
77
186
#define dfltChSep ","
78
186
#define dfltFmt "\\x,\\y:\\c\\n"
79
80
/*
81
  Enumerated declarations.
82
*/
83
typedef enum
84
  {
85
    vtAny,
86
    vtQuant,
87
    vtPercent,
88
    vtProp,
89
    vtIntHex,
90
    vtFltHex
91
  } ValueTypeT;
92
93
/*
94
  Forward declaration.
95
*/
96
static MagickBooleanType
97
  WriteFTXTImage(const ImageInfo *,Image *,ExceptionInfo *);
98

99
/*
100
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101
%                                                                             %
102
%                                                                             %
103
%                                                                             %
104
%   I s F T X T                                                               %
105
%                                                                             %
106
%                                                                             %
107
%                                                                             %
108
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109
%
110
%  IsFTXT() returns MagickTrue if the image format type, identified by the
111
%  magick string, is formatted text.
112
%
113
%  The format of the IsFTXT method is:
114
%
115
%      MagickBooleanType IsFTXT(const unsigned char *magick,const size_t length)
116
%
117
%  A description of each parameter follows:
118
%
119
%    o magick: compare image format pattern against these bytes.
120
%
121
%    o length: Specifies the length of the magick string.
122
%
123
*/
124
static MagickBooleanType IsFTXT(const unsigned char *magick,const size_t length)
125
0
{
126
0
  if (length < 7)
127
0
    return(MagickFalse);
128
0
  if (LocaleNCompare((char *) magick,"id=ftxt",7) == 0)
129
0
    return(MagickTrue);
130
0
  return(MagickFalse);
131
0
}
132

133
/*
134
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135
%                                                                             %
136
%                                                                             %
137
%                                                                             %
138
%   R e a d F T X T I m a g e                                                 %
139
%                                                                             %
140
%                                                                             %
141
%                                                                             %
142
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143
%
144
%  ReadFTXTImage() reads an formatted text image file and returns it.  It
145
%  allocates the memory necessary for the new Image structure and returns a
146
%  pointer to the new image.
147
%
148
%  The format of the ReadFTXTImage method is:
149
%
150
%      Image *ReadFTXTImage(image_info,ExceptionInfo *exception)
151
%
152
%  A description of each parameter follows:
153
%
154
%    o image_info: the image info.
155
%
156
%    o exception: return any errors or warnings in this structure.
157
%
158
*/
159
160
static int ReadChar(Image *image, int *chPushed)
161
36.1k
{
162
36.1k
  int
163
36.1k
    ch;
164
165
36.1k
  if (*chPushed)
166
6.41k
    {
167
6.41k
      ch=*chPushed;
168
6.41k
      *chPushed=0;
169
6.41k
    }
170
29.7k
  else
171
29.7k
    ch=ReadBlobByte(image);
172
36.1k
  return(ch);
173
36.1k
}
174
175
static int ReadInt(Image * image,MagickBooleanType *eofInp,int *chPushed,
176
  MagickBooleanType *err)
177
4.39k
{
178
4.39k
  char
179
4.39k
    buffer[MaxTextExtent];
180
181
4.39k
  char
182
4.39k
    *p,
183
4.39k
    *tail;
184
185
4.39k
  int
186
4.39k
    chIn,
187
4.39k
    val;
188
189
4.39k
  p=buffer;
190
4.39k
  chIn=ReadChar(image,chPushed);
191
4.39k
  if (chIn == EOF)
192
78
    *eofInp = MagickTrue;
193
4.39k
  while (isdigit(chIn))
194
6.50k
  {
195
6.50k
    *p=(char) chIn;
196
6.50k
    p++;
197
6.50k
    if (p-buffer >= MaxTextExtent)
198
0
      {
199
0
        *eofInp=MagickTrue;
200
0
        break;
201
0
      }
202
6.50k
    chIn=ReadChar(image,chPushed);
203
6.50k
  }
204
4.39k
  if (p == buffer)
205
85
    {
206
85
      *eofInp=MagickTrue;
207
85
      return(0);
208
85
    }
209
4.31k
  if (*eofInp)
210
0
    {
211
0
      *chPushed='\0';
212
0
      return(0);
213
0
    }
214
4.31k
  *p='\0';
215
4.31k
  *chPushed=chIn;
216
4.31k
  errno=0;
217
4.31k
  val=strtol(buffer,&tail,10);
218
4.31k
  if (errno || *tail)
219
0
    {
220
0
      *eofInp=MagickTrue;
221
0
      *err=MagickTrue;
222
0
    }
223
4.31k
  if (val < 0)
224
0
    *err=MagickTrue;
225
4.31k
  return (val);
226
4.31k
}
227
228
static long double BufToFlt(char * buffer,char ** tail,ValueTypeT expectType,
229
  MagickBooleanType *err)
230
3.38k
{
231
3.38k
  long double
232
3.38k
    val;
233
234
3.38k
  *err=MagickFalse;
235
3.38k
  val=0;
236
3.38k
  if (*buffer == '#')
237
729
    {
238
729
      char
239
729
        *p;
240
241
      /* read hex integer */
242
729
      p=buffer+1;
243
3.56k
      while (*p)
244
3.49k
      {
245
3.49k
        short
246
3.49k
          v;
247
248
3.49k
        if (*p >= '0' && *p <= '9')
249
1.20k
          v=*p-'0';
250
2.29k
        else if (*p >= 'a' && *p <= 'f')
251
529
          v=*p-'a'+10;
252
1.76k
        else if (*p >= 'A' && *p <= 'F')
253
1.10k
          v=*p-'A'+10;
254
657
        else
255
657
          break;
256
2.83k
        val=val*16+v;
257
2.83k
        p++;
258
2.83k
      }
259
729
      *tail=p;
260
729
      if ((expectType != vtAny) && (expectType != vtIntHex))
261
0
        *err=MagickTrue;
262
729
    }
263
2.65k
  else if ((*buffer == '0') && (*(buffer + 1) == 'x'))
264
0
    {
265
      /* read hex floating-point */
266
0
      val=strtold(buffer,tail);
267
0
      if ((expectType != vtAny) && (expectType != vtFltHex))
268
0
        *err=MagickTrue;
269
0
    }
270
2.65k
  else
271
2.65k
    {
272
      /* Read decimal floating-point (possibly a percent). */
273
2.65k
      errno=0;
274
2.65k
      val=strtold(buffer,tail);
275
2.65k
      if (errno)
276
0
        *err=MagickTrue;
277
2.65k
      if (**tail=='%')
278
620
        {
279
620
          (*tail)++;
280
620
          val*=(double) QuantumRange/100.0;
281
620
          if ((expectType != vtAny) && (expectType != vtPercent))
282
0
            *err=MagickTrue;
283
620
        }
284
2.03k
      else if (expectType == vtPercent)
285
0
        *err=MagickTrue;
286
2.65k
    }
287
3.38k
  return(val);
288
3.38k
}
289
290
static void SkipUntil(Image * image,int UntilChar,MagickBooleanType *eofInp,
291
  int *chPushed)
292
0
{
293
0
  int
294
0
    chIn;
295
296
0
  chIn=ReadChar(image,chPushed);
297
0
  if (chIn == EOF)
298
0
    {
299
0
      *eofInp=MagickTrue;
300
0
      *chPushed='\0';
301
0
      return;
302
0
    }
303
0
  while ((chIn != UntilChar) && (chIn != EOF))
304
0
    chIn=ReadChar(image,chPushed);
305
0
  if (chIn == EOF)
306
0
    {
307
0
      *eofInp=MagickTrue;
308
0
      *chPushed='\0';
309
0
      return;
310
0
    }
311
0
  *chPushed=chIn;
312
0
}
313
314
static void ReadUntil(Image * image,int UntilChar,MagickBooleanType *eofInp,
315
  int *chPushed,char *buf,int bufSize)
316
2.11k
{
317
2.11k
  int
318
2.11k
    chIn,
319
2.11k
    i;
320
321
2.11k
  i=0;
322
2.11k
  for (;;)
323
18.8k
    {
324
18.8k
      chIn=ReadChar(image,chPushed);
325
18.8k
      if (chIn == EOF)
326
43
        {
327
43
          if (i==0)
328
2
            *eofInp=MagickTrue;
329
43
          break;
330
43
        }
331
18.7k
      if (chIn == UntilChar)
332
2.06k
        break;
333
16.7k
      if (i >= bufSize)
334
0
        {
335
0
          *eofInp=MagickTrue;
336
0
          break;
337
0
        }
338
16.7k
      buf[i++]=(char) chIn;
339
16.7k
    }
340
2.11k
  if (*eofInp)
341
2
    *chPushed='\0';
342
2.10k
  else
343
2.10k
    *chPushed=chIn;
344
2.11k
  buf[i]='\0';
345
2.11k
  if ((UntilChar == '\n') && (i > 0) && (buf[i-1] == '\r'))
346
1
    buf[i-1]='\0';
347
2.11k
}
348
349
static Image *ReadFTXTImage(const ImageInfo *image_info,
350
  ExceptionInfo *exception)
351
186
{
352
186
  char
353
186
    buffer[MaxTextExtent],
354
186
    chSep,
355
186
    *ppf,
356
186
    procFmt[MaxTextExtent];
357
358
186
  const char
359
186
    *pf,
360
186
    *sChSep,
361
186
    *sFmt,
362
186
    *sNumMeta;
363
364
186
  Image
365
186
    *image;
366
367
186
  int
368
186
    chIn,
369
186
    chPushed,
370
186
    i,
371
186
    nExpCh,
372
186
    numMeta;
373
374
186
  long double
375
186
    chVals[MaxPixelChannels];
376
377
186
  MagickBooleanType
378
186
    eofInp,
379
186
    firstX,
380
186
    firstY,
381
186
    hasAlpha,
382
186
    intErr,
383
186
    nChErr,
384
186
    status,
385
186
    typeErr;
386
387
186
  PixelInfo
388
186
    mppBlack;
389
390
186
  Quantum
391
186
    *q;
392
393
186
  ssize_t
394
186
    maxX,
395
186
    maxY,
396
186
    nPix,
397
186
    x,
398
186
    y;
399
400
186
  assert(image_info != (const ImageInfo *) NULL);
401
186
  assert(image_info->signature == MagickCoreSignature);
402
186
  assert(exception != (ExceptionInfo *) NULL);
403
186
  assert(exception->signature == MagickCoreSignature);
404
186
  if (IsEventLogging() != MagickFalse)
405
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
406
0
      image_info->filename);
407
186
  image=AcquireImage(image_info,exception);
408
186
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
409
186
  if (status == MagickFalse)
410
0
    {
411
0
      image=DestroyImageList(image);
412
0
      return((Image *) NULL);
413
0
    }
414
186
  SetImageColorspace(image,RGBColorspace,exception);
415
186
  SetImageColorspace(image,image_info->colorspace,exception);
416
186
  sFmt=GetImageArtifact(image,"ftxt:format");
417
186
  if (sFmt == (const char *) NULL)
418
186
    sFmt=dfltFmt;
419
186
  sChSep=GetImageArtifact(image,"ftxt:chsep");
420
186
  if (sChSep == (const char *) NULL)
421
186
    sChSep=dfltChSep;
422
186
  if ((sChSep[0] == chEsc) && ((sChSep[1] == 'n' || sChSep[1] == 'N')))
423
0
    chSep='\n';
424
186
  else
425
186
    chSep=sChSep[0];
426
186
  hasAlpha=IsStringTrue(GetImageArtifact(image,"ftxt:hasalpha"));
427
186
  numMeta=0;
428
186
  sNumMeta=GetImageArtifact(image,"ftxt:nummeta");
429
186
  if (sNumMeta != (const char *) NULL)
430
0
    numMeta=atoi(sNumMeta);
431
186
  if (hasAlpha)
432
0
    {
433
0
      if (SetImageAlphaChannel(image,OpaqueAlphaChannel,exception) == MagickFalse)
434
0
        ThrowReaderException(OptionError,"SetImageAlphaChannelFailure");
435
0
    }
436
186
  if (numMeta)
437
0
    {
438
0
      if (SetPixelMetaChannels (image, (size_t) numMeta, exception) == MagickFalse)
439
0
        ThrowReaderException(OptionError,"SetPixelMetaChannelsFailure");
440
0
    }
441
  /* make image zero (if RGB channels, transparent black). */
442
186
  GetPixelInfo(image,&mppBlack);
443
186
  if (hasAlpha)
444
0
    mppBlack.alpha=TransparentAlpha;
445
186
  SetImageColor(image,&mppBlack,exception);
446
186
  pf=sFmt;
447
186
  ppf=procFmt;
448
186
  i=0;
449
1.30k
  while (*pf)
450
1.11k
  {
451
1.11k
    if (*pf == chEsc)
452
744
      {
453
744
        pf++;
454
744
        switch (*pf) {
455
0
          case chEsc:
456
0
            if (++i >= MaxTextExtent)
457
0
              ThrowReaderException(DelegateFatalError,"ppf bust");
458
0
            *ppf=chEsc;
459
0
            ppf++;
460
0
            break;
461
186
          case 'n':
462
186
            if (++i >= MaxTextExtent)
463
186
              ThrowReaderException(DelegateFatalError,"ppf bust");
464
186
            *ppf='\n';
465
186
            ppf++;
466
186
            break;
467
0
          case 'j':
468
0
            if (*(pf+1)=='\0')
469
0
              ThrowReaderException(DelegateFatalError,"EscapeJproblem");
470
0
            magick_fallthrough;
471
558
          default:
472
558
            if ((i+=2) >= MaxTextExtent)
473
558
              ThrowReaderException(DelegateFatalError,"ppf bust");
474
558
            *ppf=chEsc;
475
558
            ppf++;
476
558
            *ppf=*pf;
477
558
            ppf++;
478
558
            break;
479
744
        }
480
744
      }
481
372
    else
482
372
      {
483
        /* Not escape */
484
372
        if (++i >= MaxTextExtent)
485
372
          ThrowReaderException (DelegateFatalError,"ppf bust");
486
372
        *ppf=*pf;
487
372
        ppf++;
488
372
      }
489
1.11k
    pf++;
490
1.11k
  }
491
186
  *ppf='\0';
492
186
  if ((image->columns == 0) || (image->rows == 0))
493
152
    ThrowReaderException(OptionError,"MustSpecifyImageSize");
494
  /* How many channel values can we expect? */
495
152
  nExpCh=0;
496
323
  for (i=0; i < (ssize_t) GetPixelChannels (image); i++)
497
171
  {
498
171
    PixelChannel
499
171
      channel;
500
501
171
    PixelTrait
502
171
      traits;
503
504
171
    channel=GetPixelChannelChannel(image,i);
505
171
    traits=GetPixelChannelTraits(image,channel);
506
171
    if ((traits & UpdatePixelTrait) != UpdatePixelTrait)
507
0
      continue;
508
171
    nExpCh++;
509
171
  }
510
9.88k
  for (i=0; i < MaxPixelChannels; i++)
511
9.72k
    chVals[i] = 0;
512
152
  eofInp=MagickFalse;
513
152
  chPushed=0;
514
152
  x=0;
515
152
  y=0,
516
152
  maxX=-1;
517
152
  maxY=-1;
518
152
  nPix=0;
519
152
  firstX=MagickTrue,
520
152
  firstY=MagickTrue,
521
152
  intErr=MagickFalse,
522
152
  typeErr=MagickFalse,
523
152
  nChErr=MagickFalse;
524
2.30k
  while (!eofInp)
525
2.21k
  {
526
2.21k
    ValueTypeT
527
2.21k
      expectType;
528
529
2.21k
    expectType=vtAny;
530
2.21k
    ppf=procFmt;
531
15.0k
    while (*ppf && eofInp == MagickFalse)
532
12.9k
    {
533
12.9k
      if (*ppf == chEsc)
534
6.50k
        {
535
6.50k
          ppf++;
536
6.50k
          switch (*ppf)
537
6.50k
          {
538
2.21k
            case 'x':
539
2.21k
            {
540
2.21k
              x=ReadInt(image,&eofInp,&chPushed,&intErr);
541
2.21k
              if ((intErr != MagickFalse) || (eofInp != MagickFalse))
542
27
                continue;
543
2.19k
              if (firstX != MagickFalse)
544
151
                {
545
151
                  firstX=MagickFalse;
546
151
                  maxX=x;
547
151
                }
548
2.04k
              else if (maxX < x)
549
19
                maxX=x;
550
2.19k
              break;
551
2.21k
            }
552
2.17k
            case 'y':
553
2.17k
            {
554
2.17k
              y=ReadInt(image,&eofInp,&chPushed,&intErr);
555
2.17k
              if ((intErr != MagickFalse) || (eofInp != MagickFalse))
556
58
                continue;
557
2.11k
              if (firstY)
558
90
                {
559
90
                  firstY=MagickFalse;
560
90
                  maxY=y;
561
90
                }
562
2.02k
              else if (maxY < y)
563
6
                maxY=y;
564
2.11k
              break;
565
2.17k
            }
566
2.11k
            case 'c':
567
2.11k
            case 'v':
568
2.11k
            case 'p':
569
2.11k
            case 'o':
570
2.11k
            case 'h':
571
2.11k
            case 'f':
572
2.11k
            {
573
2.11k
              char
574
2.11k
                *pt,
575
2.11k
                *tail;
576
577
2.11k
              int
578
2.11k
                untilChar;
579
580
2.11k
              long double
581
2.11k
                val;
582
583
2.11k
              if (*ppf == 'c')
584
2.11k
                expectType=vtAny;
585
0
              else if (*ppf == 'v')
586
0
                expectType=vtQuant;
587
0
              else if (*ppf=='p')
588
0
                expectType=vtPercent;
589
0
              else if (*ppf=='o')
590
0
                expectType=vtProp;
591
0
              else if (*ppf=='h')
592
0
                expectType=vtIntHex;
593
0
              else if (*ppf=='f')
594
0
                expectType=vtFltHex;
595
              /*
596
                Read chars until next char in format,
597
                then parse that string into chVals[],
598
                then write that into image.
599
              */
600
2.11k
              untilChar=*(ppf+1);
601
2.11k
              ReadUntil(image,untilChar,&eofInp,&chPushed,buffer,
602
2.11k
                MaxTextExtent-1);
603
2.11k
              if (eofInp != MagickFalse)
604
2
                break;
605
2.10k
              pt=buffer;
606
2.10k
              i=0;
607
2.10k
              for (;;)
608
3.38k
              {
609
                /* Loop through input channels. */
610
3.38k
                val=BufToFlt(pt,&tail,expectType,&typeErr);
611
3.38k
                if (expectType == vtProp)
612
0
                  val *= QuantumRange;
613
3.38k
                if (typeErr)
614
0
                  break;
615
3.38k
                if (i < MaxPixelChannels)
616
3.38k
                  chVals[i]=val;
617
3.38k
                if ((*tail == '\r') && (chSep == '\n') && (*(tail + 1) == '\n'))
618
0
                  tail++;
619
3.38k
                if (*tail == chSep)
620
1.27k
                  pt=tail+1;
621
2.10k
                else
622
2.10k
                  break;
623
1.27k
                i++;
624
1.27k
              }
625
2.10k
              if (i+1 != nExpCh)
626
2.03k
                nChErr=MagickTrue;
627
2.10k
              if (x < (ssize_t) image->columns && y < (ssize_t) image->rows)
628
1.66k
                {
629
1.66k
                  q=QueueAuthenticPixels(image,x,y,1,1,exception);
630
1.66k
                  if (q == (Quantum *) NULL)
631
565
                    break;
632
4.41k
                  for (i=0; i< nExpCh; i++)
633
3.31k
                    q[i]=(char) chVals[i];
634
1.10k
                  if (SyncAuthenticPixels(image,exception) == MagickFalse)
635
0
                    break;
636
1.10k
                }
637
1.54k
              break;
638
2.10k
            }
639
1.54k
            case 'j':
640
0
            {
641
              /* Skip chars until we find char after *ppf. */
642
0
              SkipUntil(image,*(ppf+1),&eofInp,&chPushed);
643
0
              break;
644
2.10k
            }
645
0
            case 'H':
646
0
            case 's':
647
0
            {
648
0
              int
649
0
                untilChar;
650
651
0
              PixelInfo
652
0
                pixelinf;
653
654
0
              untilChar=*(ppf+1);
655
0
              ReadUntil(image,untilChar,&eofInp,&chPushed,buffer,
656
0
                MaxTextExtent-1);
657
0
              if (eofInp != MagickFalse)
658
0
                break;
659
0
              if (*buffer == 0)
660
0
                ThrowReaderException(CorruptImageError,
661
0
                  "No input for escape 'H' or 's'.");
662
0
              if (QueryColorCompliance(buffer,AllCompliance,&pixelinf,
663
0
                    exception) == MagickFalse)
664
0
                break;
665
0
              if (x < (ssize_t) image->columns && y < (ssize_t) image->rows)
666
0
                {
667
0
                  q=QueueAuthenticPixels(image,x,y,1,1,exception);
668
0
                  if (q == (Quantum *) NULL)
669
0
                    break;
670
0
                  SetPixelViaPixelInfo(image,&pixelinf,q);
671
0
                  if (SyncAuthenticPixels(image,exception) == MagickFalse)
672
0
                    break;
673
0
                }
674
0
              break;
675
0
            }
676
0
            default:
677
0
              break;
678
6.50k
          }
679
6.50k
        }
680
6.41k
      else
681
6.41k
        {
682
          /* Not escape */
683
6.41k
          chIn=ReadChar(image,&chPushed);
684
6.41k
          if (chIn == EOF)
685
42
            {
686
42
              if (ppf != procFmt)
687
42
                ThrowReaderException(CorruptImageError,"EOFduringFormat");
688
0
              eofInp=MagickTrue;
689
0
            }
690
6.37k
          else
691
6.37k
            {
692
6.37k
              if ((chIn == '\r') && (*ppf == '\n'))
693
0
                {
694
0
                  chIn=ReadChar(image,&chPushed);
695
0
                  if (chIn != '\n')
696
0
                    ThrowReaderException(CorruptImageError,"BackslashRbad");
697
0
                }
698
6.37k
              if (chIn != *ppf)
699
6.35k
                ThrowReaderException(CorruptImageError,"UnexpectedInputChar");
700
6.35k
            }
701
6.41k
        }
702
12.7k
      ppf++;
703
12.7k
    }
704
2.15k
    if (eofInp == MagickFalse)
705
2.06k
      {
706
2.06k
        nPix++;
707
2.06k
        if (maxX < x)
708
0
          maxX=x;
709
2.06k
        if (maxY < y)
710
0
          maxY=y;
711
2.06k
        if ((firstX != MagickFalse) && (firstY != MagickFalse))
712
0
          {
713
0
            x++;
714
0
            if (x >= (ssize_t) image->columns)
715
0
              {
716
0
                x=0;
717
0
                y++;
718
0
              }
719
0
          }
720
2.06k
      }
721
2.15k
  }
722
87
  if (intErr != MagickFalse)
723
87
    ThrowReaderException(CorruptImageError,"ParseIntegerError");
724
87
  if (typeErr != MagickFalse)
725
87
    ThrowReaderException(CorruptImageError,"TypeError");
726
87
  if (chPushed != 0)
727
87
    ThrowReaderException(CorruptImageError,"UnusedPushedChar");
728
87
  if ((maxX < 0) && (maxY < 0))
729
86
    ThrowReaderException(CorruptImageError,"UnexpectedEof");
730
86
  if (nChErr != MagickFalse)
731
56
    ThrowReaderException(CorruptImageError,"NumChannelsError");
732
56
  if (nPix > (ssize_t) (image->columns * image->rows))
733
24
    ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
734
24
      "TooManyPixels","`%s'",image_info->filename);
735
32
  else if ((maxX >= (ssize_t) image->columns) ||
736
27
           (maxY >= (ssize_t) image->rows))
737
11
    ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
738
11
      "ImageBoundsExceeded","`%s'",image_info->filename);
739
56
  if (CloseBlob(image) == MagickFalse)
740
0
    status=MagickFalse;
741
56
  if (status == MagickFalse)
742
0
    return(DestroyImageList(image));
743
56
  return(GetFirstImageInList(image));
744
56
}
745

746
/*
747
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748
%                                                                             %
749
%                                                                             %
750
%                                                                             %
751
%   R e g i s t e r F T X T I m a g e                                         %
752
%                                                                             %
753
%                                                                             %
754
%                                                                             %
755
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756
%
757
%  RegisterFTXTImage() adds properties for the FTXT image format to
758
%  the list of supported formats. The properties include the image format
759
%  tag, a method to read and/or write the format, whether the format
760
%  supports the saving of more than one frame to the same file or blob,
761
%  whether the format supports native in-memory I/O, and a brief
762
%  description of the format.
763
%
764
%  The format of the RegisterFTXTImage method is:
765
%
766
%      size_t RegisterFTXTImage(void)
767
%
768
*/
769
ModuleExport size_t RegisterFTXTImage(void)
770
9
{
771
9
  MagickInfo
772
9
    *entry;
773
774
9
  entry=AcquireMagickInfo("FTXT","FTXT","Formatted text image");
775
9
  entry->decoder=(DecodeImageHandler *) ReadFTXTImage;
776
9
  entry->encoder=(EncodeImageHandler *) WriteFTXTImage;
777
9
  entry->magick=(IsImageFormatHandler *) IsFTXT;
778
9
  entry->flags^=CoderAdjoinFlag;
779
9
  (void) RegisterMagickInfo(entry);
780
9
  return(MagickImageCoderSignature);
781
9
}
782

783
/*
784
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785
%                                                                             %
786
%                                                                             %
787
%                                                                             %
788
%   U n r e g i s t e r F T X T I m a g e                                     %
789
%                                                                             %
790
%                                                                             %
791
%                                                                             %
792
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793
%
794
%  UnregisterFTXTImage() removes format registrations made by the
795
%  FTXT module from the list of supported formats.
796
%
797
%  The format of the UnregisterFTXTImage method is:
798
%
799
%      UnregisterFTXTImage(void)
800
%
801
*/
802
ModuleExport void UnregisterFTXTImage(void)
803
0
{
804
0
  (void) UnregisterMagickInfo("FTXT");
805
0
}
806

807
/*
808
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809
%                                                                             %
810
%                                                                             %
811
%                                                                             %
812
%   W r i t e F T X T I m a g e                                               %
813
%                                                                             %
814
%                                                                             %
815
%                                                                             %
816
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817
%
818
%  WriteFTXTImage() writes an image in the formatted text image format.
819
%
820
%  The format of the WriteFTXTImage method is:
821
%
822
%      MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
823
%        Image *image,ExceptionInfo *exception)
824
%
825
%  A description of each parameter follows.
826
%
827
%    o image_info: the image info.
828
%
829
%    o image:  The image.
830
%
831
%    o exception: return any errors or warnings in this structure.
832
%
833
*/
834
static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
835
  Image *image,ExceptionInfo *exception)
836
0
{
837
0
  char
838
0
    buffer[MaxTextExtent],
839
0
    chSep,
840
0
    sSuff[2];
841
842
0
  const char
843
0
    *sChSep,
844
0
    *sFmt;
845
846
0
  const Quantum
847
0
    *p;
848
849
0
  int
850
0
    precision;
851
852
0
  MagickBooleanType
853
0
    status;
854
855
0
  MagickOffsetType
856
0
    scene;
857
858
0
  PixelInfo
859
0
    pixel;
860
861
0
  assert(image_info != (const ImageInfo *) NULL);
862
0
  assert(image_info->signature == MagickCoreSignature);
863
0
  assert(image != (Image *) NULL);
864
0
  assert(image->signature == MagickCoreSignature);
865
0
  if (IsEventLogging() != MagickFalse)
866
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
867
0
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
868
0
  if (status == MagickFalse)
869
0
    return(status);
870
0
  scene=0;
871
0
  precision=GetMagickPrecision();
872
0
  sFmt=GetImageArtifact(image,"ftxt:format");
873
0
  if (sFmt == (const char *) NULL)
874
0
    sFmt=dfltFmt;
875
0
  sChSep=GetImageArtifact(image,"ftxt:chsep");
876
0
  if (sChSep == (const char *) NULL)
877
0
    sChSep=dfltChSep;
878
0
  if ((sChSep[0]==chEsc) && ((sChSep[1] == 'n') || (sChSep[1] == 'N')))
879
0
    chSep='\n';
880
0
  else
881
0
    chSep=sChSep[0];
882
0
  sSuff[0]='\0';
883
0
  sSuff[1]='\0';
884
885
0
  do
886
0
  {
887
0
    long
888
0
      x,
889
0
      y;
890
891
0
    GetPixelInfo(image,&pixel);
892
0
    for (y=0; y < (long) image->rows; y++)
893
0
    {
894
0
      p=GetVirtualPixels(image,0,y,image->columns,1,exception);
895
0
      if (!p) break;
896
0
      for (x=0; x < (long) image->columns; x++)
897
0
      {
898
0
        const char
899
0
          *pFmt;
900
901
0
        long double
902
0
          valMult;
903
904
0
        MagickBooleanType
905
0
          fltHexFmt,
906
0
          hexFmt;
907
908
0
        valMult=1.0;
909
0
        hexFmt=MagickFalse;
910
0
        fltHexFmt=MagickFalse;
911
0
        pFmt=sFmt;
912
0
        while (*pFmt)
913
0
        {
914
0
          if (*pFmt == chEsc)
915
0
          {
916
0
            pFmt++;
917
0
            switch (*pFmt)
918
0
            {
919
0
              case 'x':
920
0
              {
921
0
                FormatLocaleString(buffer,MaxTextExtent,"%li",x);
922
0
                WriteBlobString(image,buffer);
923
0
                break;
924
0
              }
925
0
              case 'y':
926
0
              {
927
0
                FormatLocaleString (buffer,MaxTextExtent,"%li",y);
928
0
                WriteBlobString(image,buffer);
929
0
                break;
930
0
              }
931
0
              case 'n':
932
0
              {
933
0
                WriteBlobString(image,"\n");
934
0
                break;
935
0
              }
936
0
              case chEsc:
937
0
              {
938
0
                FormatLocaleString(buffer,MaxTextExtent,"%c%c",chEsc,chEsc);
939
0
                WriteBlobString(image,buffer);
940
0
                break;
941
0
              }
942
0
              case 'c':
943
0
              case 'v':
944
0
              case 'p':
945
0
              case 'o':
946
0
              case 'h':
947
0
              case 'f':
948
0
              {
949
0
                char
950
0
                  sSep[2];
951
952
0
                ssize_t
953
0
                  i;
954
955
0
                hexFmt=MagickFalse;
956
0
                if (*pFmt == 'c')
957
0
                  valMult=1.0;
958
0
                else if (*pFmt == 'v')
959
0
                  valMult=1.0;
960
0
                else if (*pFmt == 'p')
961
0
                  {
962
0
                    valMult=100*QuantumScale;
963
0
                    sSuff[0]='%';
964
0
                  }
965
0
                else if (*pFmt == 'o')
966
0
                  valMult=QuantumScale;
967
0
                else if (*pFmt == 'h')
968
0
                  {
969
0
                    valMult=1.0;
970
0
                    hexFmt=MagickTrue;
971
0
                  }
972
0
                else if (*pFmt == 'f')
973
0
                  {
974
0
                    valMult=1.0;
975
0
                    fltHexFmt=MagickTrue;
976
0
                  }
977
                /* Output all "-channel" channels. */
978
0
                sSep[0]=sSep[1]='\0';
979
0
                for (i=0; i < (ssize_t) GetPixelChannels (image); i++)
980
0
                {
981
0
                  PixelChannel
982
0
                    channel;
983
984
0
                  PixelTrait
985
0
                    traits;
986
987
0
                  channel=GetPixelChannelChannel(image,i);
988
0
                  traits=GetPixelChannelTraits(image,channel);
989
0
                  if ((traits & UpdatePixelTrait) != UpdatePixelTrait)
990
0
                    continue;
991
0
                  if (hexFmt)
992
0
                    FormatLocaleString(buffer,MaxTextExtent,"%s#%llx",sSep,
993
0
                      (signed long long)(((long double) p[i])+0.5));
994
0
                  else if (fltHexFmt)
995
0
                    FormatLocaleString(buffer,MaxTextExtent,"%s%a",sSep,
996
0
                      (double) p[i]);
997
0
                  else
998
0
                    FormatLocaleString(buffer,MaxTextExtent,"%s%.*g%s",sSep,
999
0
                      precision,(double) (p[i]*valMult),sSuff);
1000
0
                  WriteBlobString(image,buffer);
1001
0
                  sSep[0]=chSep;
1002
0
                }
1003
0
                break;
1004
0
              }
1005
0
              case 'j':
1006
0
              {
1007
                /* Output nothing. */
1008
0
                break;
1009
0
              }
1010
0
              case 's':
1011
0
              {
1012
0
                GetPixelInfoPixel(image,p,&pixel);
1013
0
                GetColorTuple(&pixel,MagickFalse,buffer);
1014
0
                WriteBlobString(image,buffer);
1015
0
                break;
1016
0
              }
1017
0
              case 'H':
1018
0
              {
1019
0
                GetPixelInfoPixel(image,p,&pixel);
1020
                /*
1021
                  For reading, QueryColorCompliance misreads 64 bit/channel
1022
                  hex colours, so when writing we ensure it is at most 32 bits.
1023
                */
1024
0
                if (pixel.depth > 32)
1025
0
                  pixel.depth=32;
1026
0
                GetColorTuple(&pixel,MagickTrue,buffer);
1027
0
                WriteBlobString(image,buffer);
1028
0
                break;
1029
0
              }
1030
0
              default:
1031
0
                break;
1032
0
            }
1033
0
          }
1034
0
          else
1035
0
            {
1036
              /* Not an escape char. */
1037
0
              buffer[0]=*pFmt;
1038
0
              buffer[1]='\0';
1039
0
              (void) WriteBlobString(image,buffer);
1040
0
            }
1041
0
          if (*pFmt)
1042
0
            pFmt++;
1043
0
        }
1044
0
        p+=(ptrdiff_t) GetPixelChannels(image);
1045
0
      }
1046
0
      if ((image->previous == (Image *) NULL) &&
1047
0
          (image->progress_monitor != (MagickProgressMonitor) NULL) &&
1048
0
          (QuantumTick(y,image->rows) != MagickFalse))
1049
0
        {
1050
0
          status=image->progress_monitor(SaveImageTag,y,image->rows,
1051
0
            image->client_data);
1052
0
          if (status == MagickFalse)
1053
0
            break;
1054
0
        }
1055
0
    }
1056
0
    if (GetNextImageInList(image) == (Image *) NULL)
1057
0
      break;
1058
0
    image=SyncNextImageInList(image);
1059
0
    status=SetImageProgress(image,SaveImagesTag,scene,
1060
0
      GetImageListLength(image));
1061
0
    if (status == MagickFalse)
1062
0
      break;
1063
0
    scene++;
1064
0
  } while (image_info->adjoin != MagickFalse);
1065
0
  if (CloseBlob(image) == MagickFalse)
1066
0
    status=MagickFalse;
1067
0
  return(status);
1068
0
}