Commit 550974a06 for imagemagick.org

commit 550974a06effafb1b9d75f9f185dfe56cbc7879f
Author: Dirk Lemstra <dirk@lemstra.org>
Date:   Sat Nov 7 21:12:21 2026 +0100

    Use a buffer to avoid writing small number bytes in the FTXT encoder.

diff --git a/coders/ftxt.c b/coders/ftxt.c
index 222795d50..80f46a8ba 100644
--- a/coders/ftxt.c
+++ b/coders/ftxt.c
@@ -803,6 +803,31 @@ ModuleExport void UnregisterFTXTImage(void)
 {
   (void) UnregisterMagickInfo("FTXT");
 }
+
+static MagickBooleanType WriteFTXTString(Image *image,const char *text,
+  unsigned char *pixels,size_t *extent)
+{
+  size_t
+    count,
+    length;
+
+  count=strlen(text);
+  while (count != 0)
+  {
+    length=MagickMin(count,MaxTextExtent-*extent);
+    (void) memcpy(pixels+*extent,text,length);
+    *extent+=length;
+    text+=length;
+    count-=length;
+    if (*extent == MaxTextExtent)
+      {
+        if (WriteBlob(image,*extent,pixels) != (ssize_t) *extent)
+          return(MagickFalse);
+        *extent=0;
+      }
+  }
+  return(MagickTrue);
+}

 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -858,6 +883,12 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
   PixelInfo
     pixel;

+  size_t
+    extent;
+
+  unsigned char
+    *pixels;
+
   assert(image_info != (const ImageInfo *) NULL);
   assert(image_info->signature == MagickCoreSignature);
   assert(image != (Image *) NULL);
@@ -881,6 +912,10 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
     chSep=sChSep[0];
   sSuff[0]='\0';
   sSuff[1]='\0';
+  pixels=(unsigned char *) AcquireQuantumMemory(MaxTextExtent,sizeof(*pixels));
+  if (pixels == (unsigned char *) NULL)
+    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+  extent=0;

   do
   {
@@ -889,11 +924,15 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
       y;

     GetPixelInfo(image,&pixel);
-    for (y=0; y < (long) image->rows; y++)
+    for (y=0; (y < (long) image->rows) && (status != MagickFalse); y++)
     {
       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
-      if (!p) break;
-      for (x=0; x < (long) image->columns; x++)
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      for (x=0; (x < (long) image->columns) && (status != MagickFalse); x++)
       {
         const char
           *pFmt;
@@ -909,7 +948,7 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
         hexFmt=MagickFalse;
         fltHexFmt=MagickFalse;
         pFmt=sFmt;
-        while (*pFmt)
+        while ((*pFmt != '\0') && (status != MagickFalse))
         {
           if (*pFmt == chEsc)
           {
@@ -919,24 +958,24 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
               case 'x':
               {
                 FormatLocaleString(buffer,MaxTextExtent,"%li",x);
-                WriteBlobString(image,buffer);
+                status=WriteFTXTString(image,buffer,pixels,&extent);
                 break;
               }
               case 'y':
               {
                 FormatLocaleString (buffer,MaxTextExtent,"%li",y);
-                WriteBlobString(image,buffer);
+                status=WriteFTXTString(image,buffer,pixels,&extent);
                 break;
               }
               case 'n':
               {
-                WriteBlobString(image,"\n");
+                status=WriteFTXTString(image,"\n",pixels,&extent);
                 break;
               }
               case chEsc:
               {
                 FormatLocaleString(buffer,MaxTextExtent,"%c%c",chEsc,chEsc);
-                WriteBlobString(image,buffer);
+                status=WriteFTXTString(image,buffer,pixels,&extent);
                 break;
               }
               case 'c':
@@ -976,7 +1015,8 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
                   }
                 /* Output all "-channel" channels. */
                 sSep[0]=sSep[1]='\0';
-                for (i=0; i < (ssize_t) GetPixelChannels (image); i++)
+                for (i=0; (i < (ssize_t) GetPixelChannels (image)) &&
+                     (status != MagickFalse); i++)
                 {
                   PixelChannel
                     channel;
@@ -997,7 +1037,7 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
                   else
                     FormatLocaleString(buffer,MaxTextExtent,"%s%.*g%s",sSep,
                       precision,(double) (p[i]*valMult),sSuff);
-                  WriteBlobString(image,buffer);
+                  status=WriteFTXTString(image,buffer,pixels,&extent);
                   sSep[0]=chSep;
                 }
                 break;
@@ -1011,7 +1051,7 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
               {
                 GetPixelInfoPixel(image,p,&pixel);
                 GetColorTuple(&pixel,MagickFalse,buffer);
-                WriteBlobString(image,buffer);
+                status=WriteFTXTString(image,buffer,pixels,&extent);
                 break;
               }
               case 'H':
@@ -1024,7 +1064,7 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
                 if (pixel.depth > 32)
                   pixel.depth=32;
                 GetColorTuple(&pixel,MagickTrue,buffer);
-                WriteBlobString(image,buffer);
+                status=WriteFTXTString(image,buffer,pixels,&extent);
                 break;
               }
               default:
@@ -1036,14 +1076,20 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
               /* Not an escape char. */
               buffer[0]=*pFmt;
               buffer[1]='\0';
-              (void) WriteBlobString(image,buffer);
+              status=WriteFTXTString(image,buffer,pixels,&extent);
             }
           if (*pFmt)
             pFmt++;
         }
         p+=(ptrdiff_t) GetPixelChannels(image);
       }
-      if ((image->previous == (Image *) NULL) &&
+      if ((status != MagickFalse) && (extent != 0))
+        {
+          if (WriteBlob(image,extent,pixels) != (ssize_t) extent)
+            status=MagickFalse;
+          extent=0;
+        }
+      if ((status != MagickFalse) && (image->previous == (Image *) NULL) &&
           (image->progress_monitor != (MagickProgressMonitor) NULL) &&
           (QuantumTick(y,image->rows) != MagickFalse))
         {
@@ -1053,7 +1099,9 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
             break;
         }
     }
-    if (GetNextImageInList(image) == (Image *) NULL)
+    if (status == MagickFalse)
+      break;
+    if (GetNextImageInList(image) == (Image*)NULL)
       break;
     image=SyncNextImageInList(image);
     status=SetImageProgress(image,SaveImagesTag,scene,
@@ -1062,6 +1110,7 @@ static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info,
       break;
     scene++;
   } while (image_info->adjoin != MagickFalse);
+  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
   if (CloseBlob(image) == MagickFalse)
     status=MagickFalse;
   return(status);