Files
ImageMagick-mirror/coders/ftxt.c
T

1069 lines
31 KiB
C

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% FFFFF TTTTT X X TTTTT %
% F T X X T %
% FFF T X T %
% F T X X T %
% F T X X T %
% %
% Read and Write Pixels as Formatted Text %
% %
% Software Design %
% snibgo (Alan Gibson) %
% October 2021 %
% %
% %
% %
% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% obtain a copy of the License at %
% %
% https://imagemagick.org/license/ %
% %
% Unless required by applicable law or agreed to in writing, software %
% distributed under the License is distributed on an "AS IS" BASIS, %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
% See the License for the specific language governing permissions and %
% limitations under the License. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
%
*/
#include "MagickCore/studio.h"
#include "MagickCore/annotate.h"
#include "MagickCore/artifact.h"
#include "MagickCore/attribute.h"
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/channel.h"
#include "MagickCore/color.h"
#include "MagickCore/color-private.h"
#include "MagickCore/colorspace.h"
#include "MagickCore/constitute.h"
#include "MagickCore/draw.h"
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/geometry.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
#include "MagickCore/list.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
#include "MagickCore/module.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/option.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/static.h"
#include "MagickCore/statistic.h"
#include "MagickCore/string_.h"
#include "MagickCore/token.h"
#include "coders/ftxt.h"
/*
Define declarations.
*/
#define chEsc '\\'
#define dfltChSep ","
#define dfltFmt "\\x,\\y:\\c\\n"
/*
Enumerated declarations.
*/
typedef enum
{
vtAny,
vtQuant,
vtPercent,
vtProp,
vtIntHex,
vtFltHex
} ValueTypeT;
/*
Forward declaration.
*/
static MagickBooleanType
WriteFTXTImage(const ImageInfo *,Image *,ExceptionInfo *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s F T X T %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsFTXT() returns MagickTrue if the image format type, identified by the
% magick string, is formatted text.
%
% The format of the IsFTXT method is:
%
% MagickBooleanType IsFTXT(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o magick: compare image format pattern against these bytes.
%
% o length: Specifies the length of the magick string.
%
*/
static MagickBooleanType IsFTXT(const unsigned char *magick,const size_t length)
{
if (length < 7)
return(MagickFalse);
if (LocaleNCompare((char *) magick,"id=ftxt",7) == 0)
return(MagickTrue);
return(MagickFalse);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d F T X T I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ReadFTXTImage() reads an formatted text image file and returns it. It
% allocates the memory necessary for the new Image structure and returns a
% pointer to the new image.
%
% The format of the ReadFTXTImage method is:
%
% Image *ReadFTXTImage(image_info,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image_info: the image info.
%
% o exception: return any errors or warnings in this structure.
%
*/
static int ReadChar(Image *image, int *chPushed)
{
int
ch;
if (*chPushed)
{
ch=*chPushed;
*chPushed=0;
}
else
ch=ReadBlobByte(image);
return(ch);
}
static int ReadInt(Image * image,MagickBooleanType *eofInp,int *chPushed,
MagickBooleanType *err)
{
char
buffer[MaxTextExtent];
char
*p,
*tail;
int
chIn,
val;
p=buffer;
chIn=ReadChar(image,chPushed);
if (chIn == EOF)
*eofInp = MagickTrue;
while (isdigit(chIn))
{
*p=(char) chIn;
p++;
if (p-buffer >= MaxTextExtent)
{
*eofInp=MagickTrue;
break;
}
chIn=ReadChar(image,chPushed);
}
if (p == buffer)
{
*eofInp=MagickTrue;
return(0);
}
if (*eofInp)
{
*chPushed='\0';
return(0);
}
*p='\0';
*chPushed=chIn;
errno=0;
val=strtol(buffer,&tail,10);
if (errno || *tail)
{
*eofInp=MagickTrue;
*err=MagickTrue;
}
if (val < 0)
*err=MagickTrue;
return (val);
}
static long double BufToFlt(char * buffer,char ** tail,ValueTypeT expectType,
MagickBooleanType *err)
{
long double
val;
*err=MagickFalse;
val=0;
if (*buffer == '#')
{
char
*p;
/* read hex integer */
p=buffer+1;
while (*p)
{
short
v;
if (*p >= '0' && *p <= '9')
v=*p-'0';
else if (*p >= 'a' && *p <= 'f')
v=*p-'a'+10;
else if (*p >= 'A' && *p <= 'F')
v=*p-'A'+10;
else
break;
val=val*16+v;
p++;
}
*tail=p;
if ((expectType != vtAny) && (expectType != vtIntHex))
*err=MagickTrue;
}
else if ((*buffer == '0') && (*(buffer + 1) == 'x'))
{
/* read hex floating-point */
val=strtold(buffer,tail);
if ((expectType != vtAny) && (expectType != vtFltHex))
*err=MagickTrue;
}
else
{
/* Read decimal floating-point (possibly a percent). */
errno=0;
val=strtold(buffer,tail);
if (errno)
*err=MagickTrue;
if (**tail=='%')
{
(*tail)++;
val*=(double) QuantumRange/100.0;
if ((expectType != vtAny) && (expectType != vtPercent))
*err=MagickTrue;
}
else if (expectType == vtPercent)
*err=MagickTrue;
}
return(val);
}
static void SkipUntil(Image * image,int UntilChar,MagickBooleanType *eofInp,
int *chPushed)
{
int
chIn;
chIn=ReadChar(image,chPushed);
if (chIn == EOF)
{
*eofInp=MagickTrue;
*chPushed='\0';
return;
}
while ((chIn != UntilChar) && (chIn != EOF))
chIn=ReadChar(image,chPushed);
if (chIn == EOF)
{
*eofInp=MagickTrue;
*chPushed='\0';
return;
}
*chPushed=chIn;
}
static void ReadUntil(Image * image,int UntilChar,MagickBooleanType *eofInp,
int *chPushed,char *buf,int bufSize)
{
int
chIn,
i;
i=0;
for (;;)
{
chIn=ReadChar(image,chPushed);
if (chIn == EOF)
{
if (i==0)
*eofInp=MagickTrue;
break;
}
if (chIn == UntilChar)
break;
if (i >= bufSize)
{
*eofInp=MagickTrue;
break;
}
buf[i++]=(char) chIn;
}
if (*eofInp)
*chPushed='\0';
else
*chPushed=chIn;
buf[i]='\0';
if ((UntilChar == '\n') && (i > 0) && (buf[i-1] == '\r'))
buf[i-1]='\0';
}
static Image *ReadFTXTImage(const ImageInfo *image_info,
ExceptionInfo *exception)
{
char
buffer[MaxTextExtent],
chSep,
*ppf,
procFmt[MaxTextExtent];
const char
*pf,
*sChSep,
*sFmt,
*sNumMeta;
Image
*image;
int
chIn,
chPushed,
i,
nExpCh,
numMeta;
long double
chVals[MaxPixelChannels];
MagickBooleanType
eofInp,
firstX,
firstY,
hasAlpha,
intErr,
nChErr,
status,
typeErr;
PixelInfo
mppBlack;
Quantum
*q;
ssize_t
maxX,
maxY,
nPix,
x,
y;
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
if (IsEventLogging() != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
image_info->filename);
image=AcquireImage(image_info,exception);
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == MagickFalse)
{
image=DestroyImageList(image);
return((Image *) NULL);
}
SetImageColorspace(image,RGBColorspace,exception);
SetImageColorspace(image,image_info->colorspace,exception);
sFmt=GetImageArtifact(image,"ftxt:format");
if (sFmt == (const char *) NULL)
sFmt=dfltFmt;
sChSep=GetImageArtifact(image,"ftxt:chsep");
if (sChSep == (const char *) NULL)
sChSep=dfltChSep;
if ((sChSep[0] == chEsc) && ((sChSep[1] == 'n' || sChSep[1] == 'N')))
chSep='\n';
else
chSep=sChSep[0];
hasAlpha=IsStringTrue(GetImageArtifact(image,"ftxt:hasalpha"));
numMeta=0;
sNumMeta=GetImageArtifact(image,"ftxt:nummeta");
if (sNumMeta != (const char *) NULL)
numMeta=atoi(sNumMeta);
if (hasAlpha)
{
if (SetImageAlphaChannel(image,OpaqueAlphaChannel,exception) == MagickFalse)
ThrowReaderException(OptionError,"SetImageAlphaChannelFailure");
}
if (numMeta)
{
if (SetPixelMetaChannels (image, (size_t) numMeta, exception) == MagickFalse)
ThrowReaderException(OptionError,"SetPixelMetaChannelsFailure");
}
/* make image zero (if RGB channels, transparent black). */
GetPixelInfo(image,&mppBlack);
if (hasAlpha)
mppBlack.alpha=TransparentAlpha;
SetImageColor(image,&mppBlack,exception);
pf=sFmt;
ppf=procFmt;
i=0;
while (*pf)
{
if (*pf == chEsc)
{
pf++;
switch (*pf) {
case chEsc:
if (++i >= MaxTextExtent)
ThrowReaderException(DelegateFatalError,"ppf bust");
*ppf=chEsc;
ppf++;
break;
case 'n':
if (++i >= MaxTextExtent)
ThrowReaderException(DelegateFatalError,"ppf bust");
*ppf='\n';
ppf++;
break;
case 'j':
if (*(pf+1)=='\0')
ThrowReaderException(DelegateFatalError,"EscapeJproblem");
magick_fallthrough;
default:
if ((i+=2) >= MaxTextExtent)
ThrowReaderException(DelegateFatalError,"ppf bust");
*ppf=chEsc;
ppf++;
*ppf=*pf;
ppf++;
break;
}
}
else
{
/* Not escape */
if (++i >= MaxTextExtent)
ThrowReaderException (DelegateFatalError,"ppf bust");
*ppf=*pf;
ppf++;
}
pf++;
}
*ppf='\0';
if ((image->columns == 0) || (image->rows == 0))
ThrowReaderException(OptionError,"MustSpecifyImageSize");
/* How many channel values can we expect? */
nExpCh=0;
for (i=0; i < (ssize_t) GetPixelChannels (image); i++)
{
PixelChannel
channel;
PixelTrait
traits;
channel=GetPixelChannelChannel(image,i);
traits=GetPixelChannelTraits(image,channel);
if ((traits & UpdatePixelTrait) != UpdatePixelTrait)
continue;
nExpCh++;
}
for (i=0; i < MaxPixelChannels; i++)
chVals[i] = 0;
eofInp=MagickFalse;
chPushed=0;
x=0;
y=0,
maxX=-1;
maxY=-1;
nPix=0;
firstX=MagickTrue,
firstY=MagickTrue,
intErr=MagickFalse,
typeErr=MagickFalse,
nChErr=MagickFalse;
while (!eofInp)
{
ValueTypeT
expectType;
expectType=vtAny;
ppf=procFmt;
while (*ppf && eofInp == MagickFalse)
{
if (*ppf == chEsc)
{
ppf++;
switch (*ppf)
{
case 'x':
{
x=ReadInt(image,&eofInp,&chPushed,&intErr);
if ((intErr != MagickFalse) || (eofInp != MagickFalse))
continue;
if (firstX != MagickFalse)
{
firstX=MagickFalse;
maxX=x;
}
else if (maxX < x)
maxX=x;
break;
}
case 'y':
{
y=ReadInt(image,&eofInp,&chPushed,&intErr);
if ((intErr != MagickFalse) || (eofInp != MagickFalse))
continue;
if (firstY)
{
firstY=MagickFalse;
maxY=y;
}
else if (maxY < y)
maxY=y;
break;
}
case 'c':
case 'v':
case 'p':
case 'o':
case 'h':
case 'f':
{
char
*pt,
*tail;
int
untilChar;
long double
val;
if (*ppf == 'c')
expectType=vtAny;
else if (*ppf == 'v')
expectType=vtQuant;
else if (*ppf=='p')
expectType=vtPercent;
else if (*ppf=='o')
expectType=vtProp;
else if (*ppf=='h')
expectType=vtIntHex;
else if (*ppf=='f')
expectType=vtFltHex;
/*
Read chars until next char in format,
then parse that string into chVals[],
then write that into image.
*/
untilChar=*(ppf+1);
ReadUntil(image,untilChar,&eofInp,&chPushed,buffer,
MaxTextExtent-1);
if (eofInp != MagickFalse)
break;
pt=buffer;
i=0;
for (;;)
{
/* Loop through input channels. */
val=BufToFlt(pt,&tail,expectType,&typeErr);
if (expectType == vtProp)
val *= QuantumRange;
if (typeErr)
break;
if (i < MaxPixelChannels)
chVals[i]=val;
if ((*tail == '\r') && (chSep == '\n') && (*(tail + 1) == '\n'))
tail++;
if (*tail == chSep)
pt=tail+1;
else
break;
i++;
}
if (i+1 != nExpCh)
nChErr=MagickTrue;
if (x < (ssize_t) image->columns && y < (ssize_t) image->rows)
{
q=QueueAuthenticPixels(image,x,y,1,1,exception);
if (q == (Quantum *) NULL)
break;
for (i=0; i< nExpCh; i++)
q[i]=(char) chVals[i];
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
}
break;
}
case 'j':
{
/* Skip chars until we find char after *ppf. */
SkipUntil(image,*(ppf+1),&eofInp,&chPushed);
break;
}
case 'H':
case 's':
{
int
untilChar;
PixelInfo
pixelinf;
untilChar=*(ppf+1);
ReadUntil(image,untilChar,&eofInp,&chPushed,buffer,
MaxTextExtent-1);
if (eofInp != MagickFalse)
break;
if (*buffer == 0)
ThrowReaderException(CorruptImageError,
"No input for escape 'H' or 's'.");
if (QueryColorCompliance(buffer,AllCompliance,&pixelinf,
exception) == MagickFalse)
break;
if (x < (ssize_t) image->columns && y < (ssize_t) image->rows)
{
q=QueueAuthenticPixels(image,x,y,1,1,exception);
if (q == (Quantum *) NULL)
break;
SetPixelViaPixelInfo(image,&pixelinf,q);
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
}
break;
}
default:
break;
}
}
else
{
/* Not escape */
chIn=ReadChar(image,&chPushed);
if (chIn == EOF)
{
if (ppf != procFmt)
ThrowReaderException(CorruptImageError,"EOFduringFormat");
eofInp=MagickTrue;
}
else
{
if ((chIn == '\r') && (*ppf == '\n'))
{
chIn=ReadChar(image,&chPushed);
if (chIn != '\n')
ThrowReaderException(CorruptImageError,"BackslashRbad");
}
if (chIn != *ppf)
ThrowReaderException(CorruptImageError,"UnexpectedInputChar");
}
}
ppf++;
}
if (eofInp == MagickFalse)
{
nPix++;
if (maxX < x)
maxX=x;
if (maxY < y)
maxY=y;
if ((firstX != MagickFalse) && (firstY != MagickFalse))
{
x++;
if (x >= (ssize_t) image->columns)
{
x=0;
y++;
}
}
}
}
if (intErr != MagickFalse)
ThrowReaderException(CorruptImageError,"ParseIntegerError");
if (typeErr != MagickFalse)
ThrowReaderException(CorruptImageError,"TypeError");
if (chPushed != 0)
ThrowReaderException(CorruptImageError,"UnusedPushedChar");
if ((maxX < 0) && (maxY < 0))
ThrowReaderException(CorruptImageError,"UnexpectedEof");
if (nChErr != MagickFalse)
ThrowReaderException(CorruptImageError,"NumChannelsError");
if (nPix > (ssize_t) (image->columns * image->rows))
ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
"TooManyPixels","`%s'",image_info->filename);
else if ((maxX >= (ssize_t) image->columns) ||
(maxY >= (ssize_t) image->rows))
ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
"ImageBoundsExceeded","`%s'",image_info->filename);
if (CloseBlob(image) == MagickFalse)
status=MagickFalse;
if (status == MagickFalse)
return(DestroyImageList(image));
return(GetFirstImageInList(image));
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r F T X T I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% RegisterFTXTImage() adds properties for the FTXT image format to
% the list of supported formats. The properties include the image format
% tag, a method to read and/or write the format, whether the format
% supports the saving of more than one frame to the same file or blob,
% whether the format supports native in-memory I/O, and a brief
% description of the format.
%
% The format of the RegisterFTXTImage method is:
%
% size_t RegisterFTXTImage(void)
%
*/
ModuleExport size_t RegisterFTXTImage(void)
{
MagickInfo
*entry;
entry=AcquireMagickInfo("FTXT","FTXT","Formatted text image");
entry->decoder=(DecodeImageHandler *) ReadFTXTImage;
entry->encoder=(EncodeImageHandler *) WriteFTXTImage;
entry->magick=(IsImageFormatHandler *) IsFTXT;
entry->flags^=CoderAdjoinFlag;
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r F T X T I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% UnregisterFTXTImage() removes format registrations made by the
% FTXT module from the list of supported formats.
%
% The format of the UnregisterFTXTImage method is:
%
% UnregisterFTXTImage(void)
%
*/
ModuleExport void UnregisterFTXTImage(void)
{
(void) UnregisterMagickInfo("FTXT");
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W r i t e F T X T I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% WriteFTXTImage() writes an image in the formatted text image format.
%
% The format of the WriteFTXTImage method is:
%
% MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
% Image *image,ExceptionInfo *exception)
%
% A description of each parameter follows.
%
% o image_info: the image info.
%
% o image: The image.
%
% o exception: return any errors or warnings in this structure.
%
*/
static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
Image *image,ExceptionInfo *exception)
{
char
buffer[MaxTextExtent],
chSep,
sSuff[2];
const char
*sChSep,
*sFmt;
const Quantum
*p;
int
precision;
MagickBooleanType
status;
MagickOffsetType
scene;
PixelInfo
pixel;
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (IsEventLogging() != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
if (status == MagickFalse)
return(status);
scene=0;
precision=GetMagickPrecision();
sFmt=GetImageArtifact(image,"ftxt:format");
if (sFmt == (const char *) NULL)
sFmt=dfltFmt;
sChSep=GetImageArtifact(image,"ftxt:chsep");
if (sChSep == (const char *) NULL)
sChSep=dfltChSep;
if ((sChSep[0]==chEsc) && ((sChSep[1] == 'n') || (sChSep[1] == 'N')))
chSep='\n';
else
chSep=sChSep[0];
sSuff[0]='\0';
sSuff[1]='\0';
do
{
long
x,
y;
GetPixelInfo(image,&pixel);
for (y=0; y < (long) image->rows; y++)
{
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
if (!p) break;
for (x=0; x < (long) image->columns; x++)
{
const char
*pFmt;
long double
valMult;
MagickBooleanType
fltHexFmt,
hexFmt;
valMult=1.0;
hexFmt=MagickFalse;
fltHexFmt=MagickFalse;
pFmt=sFmt;
while (*pFmt)
{
if (*pFmt == chEsc)
{
pFmt++;
switch (*pFmt)
{
case 'x':
{
FormatLocaleString(buffer,MaxTextExtent,"%li",x);
WriteBlobString(image,buffer);
break;
}
case 'y':
{
FormatLocaleString (buffer,MaxTextExtent,"%li",y);
WriteBlobString(image,buffer);
break;
}
case 'n':
{
WriteBlobString(image,"\n");
break;
}
case chEsc:
{
FormatLocaleString(buffer,MaxTextExtent,"%c%c",chEsc,chEsc);
WriteBlobString(image,buffer);
break;
}
case 'c':
case 'v':
case 'p':
case 'o':
case 'h':
case 'f':
{
char
sSep[2];
ssize_t
i;
hexFmt=MagickFalse;
if (*pFmt == 'c')
valMult=1.0;
else if (*pFmt == 'v')
valMult=1.0;
else if (*pFmt == 'p')
{
valMult=100*QuantumScale;
sSuff[0]='%';
}
else if (*pFmt == 'o')
valMult=QuantumScale;
else if (*pFmt == 'h')
{
valMult=1.0;
hexFmt=MagickTrue;
}
else if (*pFmt == 'f')
{
valMult=1.0;
fltHexFmt=MagickTrue;
}
/* Output all "-channel" channels. */
sSep[0]=sSep[1]='\0';
for (i=0; i < (ssize_t) GetPixelChannels (image); i++)
{
PixelChannel
channel;
PixelTrait
traits;
channel=GetPixelChannelChannel(image,i);
traits=GetPixelChannelTraits(image,channel);
if ((traits & UpdatePixelTrait) != UpdatePixelTrait)
continue;
if (hexFmt)
FormatLocaleString(buffer,MaxTextExtent,"%s#%llx",sSep,
(signed long long)(((long double) p[i])+0.5));
else if (fltHexFmt)
FormatLocaleString(buffer,MaxTextExtent,"%s%a",sSep,
(double) p[i]);
else
FormatLocaleString(buffer,MaxTextExtent,"%s%.*g%s",sSep,
precision,(double) (p[i]*valMult),sSuff);
WriteBlobString(image,buffer);
sSep[0]=chSep;
}
break;
}
case 'j':
{
/* Output nothing. */
break;
}
case 's':
{
GetPixelInfoPixel(image,p,&pixel);
GetColorTuple(&pixel,MagickFalse,buffer);
WriteBlobString(image,buffer);
break;
}
case 'H':
{
GetPixelInfoPixel(image,p,&pixel);
/*
For reading, QueryColorCompliance misreads 64 bit/channel
hex colours, so when writing we ensure it is at most 32 bits.
*/
if (pixel.depth > 32)
pixel.depth=32;
GetColorTuple(&pixel,MagickTrue,buffer);
WriteBlobString(image,buffer);
break;
}
default:
break;
}
}
else
{
/* Not an escape char. */
buffer[0]=*pFmt;
buffer[1]='\0';
(void) WriteBlobString(image,buffer);
}
if (*pFmt)
pFmt++;
}
p+=(ptrdiff_t) GetPixelChannels(image);
}
if ((image->previous == (Image *) NULL) &&
(image->progress_monitor != (MagickProgressMonitor) NULL) &&
(QuantumTick(y,image->rows) != MagickFalse))
{
status=image->progress_monitor(SaveImageTag,y,image->rows,
image->client_data);
if (status == MagickFalse)
break;
}
}
if (GetNextImageInList(image) == (Image *) NULL)
break;
image=SyncNextImageInList(image);
status=SetImageProgress(image,SaveImagesTag,scene,
GetImageListLength(image));
if (status == MagickFalse)
break;
scene++;
} while (image_info->adjoin != MagickFalse);
if (CloseBlob(image) == MagickFalse)
status=MagickFalse;
return(status);
}