File ImageMagick-CVE-2017-14989.patch of Package ImageMagick.8212

From 28bad01242898d7f863deedbfa8502c348293093 Mon Sep 17 00:00:00 2001
From: Cristy <urban-warrior@imagemagick.org>
Date: Tue, 26 Sep 2017 07:16:49 -0400
Subject: [PATCH] https://github.com/ImageMagick/ImageMagick/issues/781

---
 magick/annotate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: ImageMagick-6.8.8-1/magick/annotate.c
===================================================================
@@ -45,6 +45,7 @@
 #include "magick/studio.h"
 #include "magick/annotate.h"
 #include "magick/attribute.h"
+#include "magick/cache-private.h"
 #include "magick/cache-view.h"
 #include "magick/channel.h"
 #include "magick/client.h"
@@ -70,13 +71,14 @@
 #include "magick/semaphore.h"
 #include "magick/statistic.h"
 #include "magick/string_.h"
+#include "magick/token.h"
 #include "magick/token-private.h"
 #include "magick/transform.h"
 #include "magick/type.h"
 #include "magick/utility.h"
 #include "magick/xwindow-private.h"
 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
-#if defined(__MINGW32__) || defined(__MINGW64__)
+#if defined(__MINGW32__)
 #  undef interface
 #endif
 #include <ft2build.h>
@@ -101,6 +103,20 @@
 #  include <freetype/ftbbox.h>
 #endif /* defined(FT_BBOX_H) */
 #endif
+#if defined(MAGICKCORE_RAQM_DELEGATE)
+#include <raqm.h>
+#endif
+typedef struct _GraphemeInfo
+{
+  ssize_t
+    index,
+    x_offset,
+    x_advance,
+    y_offset;
+
+  size_t
+    cluster;
+} GraphemeInfo;
 
 /*
   Annotate semaphores.
@@ -138,7 +154,8 @@ static MagickBooleanType
 */
 MagickExport MagickBooleanType AnnotateComponentGenesis(void)
 {
-  AcquireSemaphoreInfo(&annotate_semaphore);
+  if (annotate_semaphore == (SemaphoreInfo *) NULL)
+    annotate_semaphore=AllocateSemaphoreInfo();
   return(MagickTrue);
 }
 
@@ -318,24 +361,22 @@ MagickExport MagickBooleanType AnnotateI
       {
         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
           geometry.width/2.0+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
-          annotate_info->affine.ry*(metrics.ascent+metrics.descent);
+          annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
+          (metrics.ascent+metrics.descent);
         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
           annotate_info->affine.sy*height+annotate_info->affine.sy*
-          metrics.ascent-annotate_info->affine.rx*(metrics.width-
-          metrics.bounds.x1)/2.0;
+          metrics.ascent-annotate_info->affine.rx*metrics.width/2.0;
         break;
       }
       case NorthEastGravity:
       {
         offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
           geometry.width+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
-          annotate_info->affine.ry*(metrics.ascent+metrics.descent)-1.0;
+          annotate_info->affine.sx*metrics.width+annotate_info->affine.ry*
+          (metrics.ascent+metrics.descent)-1.0;
         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
           annotate_info->affine.sy*height+annotate_info->affine.sy*
-          metrics.ascent-annotate_info->affine.rx*(metrics.width-
-          metrics.bounds.x1);
+          metrics.ascent-annotate_info->affine.rx*metrics.width;
         break;
       }
       case WestGravity:
@@ -354,28 +395,24 @@ MagickExport MagickBooleanType AnnotateI
       {
         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
           geometry.width/2.0+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
-          annotate_info->affine.ry*(metrics.ascent+metrics.descent-
-          (number_lines-1)*height)/2.0;
+          annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
+          (metrics.ascent+metrics.descent-(number_lines-1)*height)/2.0;
         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
           geometry.height/2.0+i*annotate_info->affine.sy*height-
-          annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0+
-          annotate_info->affine.sy*(metrics.ascent+metrics.descent-
-          (number_lines-1.0)*height)/2.0;
+          annotate_info->affine.rx*metrics.width/2.0+annotate_info->affine.sy*
+          (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
         break;
       }
       case EastGravity:
       {
         offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
           geometry.width+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
-          annotate_info->affine.ry*(metrics.ascent+metrics.descent-
-          (number_lines-1.0)*height)/2.0-1.0;
+          annotate_info->affine.sx*metrics.width+annotate_info->affine.ry*
+          (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0-1.0;
         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
           geometry.height/2.0+i*annotate_info->affine.sy*height-
-          annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)+
-          annotate_info->affine.sy*(metrics.ascent+metrics.descent-
-          (number_lines-1.0)*height)/2.0;
+          annotate_info->affine.rx*metrics.width+annotate_info->affine.sy*
+          (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
         break;
       }
       case SouthWestGravity:
@@ -392,24 +429,24 @@ MagickExport MagickBooleanType AnnotateI
       {
         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
           geometry.width/2.0+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0-
-          annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
+          annotate_info->affine.sx*metrics.width/2.0-annotate_info->affine.ry*
+          (number_lines-1.0)*height/2.0;
         offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
           geometry.height+i*annotate_info->affine.sy*height-
-          annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0-
-          annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
+          annotate_info->affine.rx*metrics.width/2.0-annotate_info->affine.sy*
+          (number_lines-1.0)*height+metrics.descent;
         break;
       }
       case SouthEastGravity:
       {
         offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
           geometry.width+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)-
-          annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
+          annotate_info->affine.sx*metrics.width-annotate_info->affine.ry*
+          (number_lines-1.0)*height-1.0;
         offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
           geometry.height+i*annotate_info->affine.sy*height-
-          annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)-
-          annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
+          annotate_info->affine.rx*metrics.width-annotate_info->affine.sy*
+          (number_lines-1.0)*height+metrics.descent;
         break;
       }
     }
@@ -424,17 +461,17 @@ MagickExport MagickBooleanType AnnotateI
       case CenterAlign:
       {
         offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0;
+          annotate_info->affine.sx*metrics.width/2.0;
         offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
-          annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0;
+          annotate_info->affine.rx*metrics.width/2.0;
         break;
       }
       case RightAlign:
       {
         offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
-          annotate_info->affine.sx*(metrics.width+metrics.bounds.x1);
+          annotate_info->affine.sx*metrics.width;
         offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
-          annotate_info->affine.rx*(metrics.width+metrics.bounds.x1);
+          annotate_info->affine.rx*metrics.width;
         break;
       }
       default:
@@ -454,7 +491,7 @@ MagickExport MagickBooleanType AnnotateI
         undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
         undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
         (void) FormatLocaleString(primitive,MaxTextExtent,
-          "rectangle -0.5,-0.5 %g,%.20g",metrics.origin.x,(double) height);
+          "rectangle 0.0,0.0 %g,%g",metrics.origin.x,(double) height);
         (void) CloneString(&undercolor_info->primitive,primitive);
         (void) DrawImage(image,undercolor_info);
         (void) DestroyDrawInfo(undercolor_info);
@@ -538,10 +575,8 @@ MagickExport MagickBooleanType AnnotateI
 MagickExport ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
   const MagickBooleanType split,TypeMetric *metrics,char **caption)
 {
-  char
-    *text;
-
   MagickBooleanType
+    digit,
     status;
 
   register char
@@ -558,13 +593,16 @@ MagickExport ssize_t FormatMagickCaption
   ssize_t
     n;
 
-  text=AcquireString(draw_info->text);
+  digit=MagickFalse;
+  width=0;
   q=draw_info->text;
   s=(char *) NULL;
   for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
   {
-    if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
+    if ((digit == MagickFalse) && (IsUTFSpace(GetUTFCode(p)) != MagickFalse))
       s=p;
+    digit=((GetUTFCode(p) >= 0x0030) && (GetUTFCode(p) <= 0x0039)) ?
+      MagickTrue : MagickFalse;
     if (GetUTFCode(p) == '\n')
       q=draw_info->text;
     for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
@@ -574,16 +612,15 @@ MagickExport ssize_t FormatMagickCaption
     if (status == MagickFalse)
       break;
     width=(size_t) floor(metrics->width+draw_info->stroke_width+0.5);
-    if ((width <= image->columns) || (strcmp(text,draw_info->text) == 0))
+    if ((width <= image->columns) || (s == (char *) NULL))
       continue;
-    (void) strcpy(text,draw_info->text);
     if ((s != (char *) NULL) && (GetUTFOctets(s) == 1))
       {
         *s='\n';
         p=s;
       }
     else
-      if ((s != (char *) NULL) || (split != MagickFalse))
+      if (split != MagickFalse)
         {
           char
             *target;
@@ -603,7 +640,61 @@ MagickExport ssize_t FormatMagickCaption
     q=draw_info->text;
     s=(char *) NULL;
   }
-  text=DestroyString(text);
+  if (width > image->columns)
+    {
+      char
+        *text;
+
+      /*
+        No convenient break point, force one.
+      */
+      text=AcquireString(draw_info->text);
+      q=draw_info->text;
+      s=(char *) NULL;
+      for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
+      {
+        if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
+          s=p;
+        if (GetUTFCode(p) == '\n')
+          q=draw_info->text;
+        for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
+          *q++=(*(p+i));
+        *q='\0';
+        status=GetTypeMetrics(image,draw_info,metrics);
+        if (status == MagickFalse)
+          break;
+        width=(size_t) floor(metrics->width+draw_info->stroke_width+0.5);
+        if ((width <= image->columns) || (strcmp(text,draw_info->text) == 0))
+          continue;
+        (void) strcpy(text,draw_info->text);
+        if ((s != (char *) NULL) && (GetUTFOctets(s) == 1))
+          {
+            *s='\n';
+            p=s;
+          }
+        else
+          if ((s != (char *) NULL) || (split != MagickFalse))
+            {
+              char
+                *target;
+
+              /*
+                No convenient line breaks-- insert newline.
+              */
+              target=AcquireString(*caption);
+              n=p-(*caption);
+              CopyMagickString(target,*caption,n+1);
+              ConcatenateMagickString(target,"\n",strlen(*caption)+1);
+              ConcatenateMagickString(target,p,strlen(*caption)+2);
+              (void) DestroyString(*caption);
+              *caption=target;
+              p=(*caption)+n;
+            }
+        q=draw_info->text;
+        s=(char *) NULL;
+      }
+      text=DestroyString(text);
+    }
   n=0;
   for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
     if (GetUTFCode(p) == '\n')
@@ -950,6 +1041,140 @@ static MagickBooleanType RenderType(Imag
 
 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
 
+static size_t ComplexTextLayout(const Image *image,const DrawInfo *draw_info,
+  const char *text,const size_t length,const FT_Face face,const FT_Int32 flags,
+  GraphemeInfo **grapheme)
+{
+#if defined(MAGICKCORE_RAQM_DELEGATE)
+  const char
+    *features;
+
+  raqm_t
+    *rq;
+
+  raqm_glyph_t
+    *glyphs;
+
+  register ssize_t
+    i;
+
+  size_t
+    extent;
+
+  extent=0;
+  rq=raqm_create();
+  if (rq == (raqm_t *) NULL)
+    goto cleanup;
+  if (raqm_set_text_utf8(rq,text,length) == 0)
+    goto cleanup;
+  if (raqm_set_par_direction(rq,(raqm_direction_t) draw_info->direction) == 0)
+    goto cleanup;
+  if (raqm_set_freetype_face(rq,face) == 0)
+    goto cleanup;
+  features=GetImageProperty(image,"type:features");
+  if (features != (const char *) NULL)
+    {
+      char
+        breaker,
+        quote,
+        *token;
+
+      int
+        next,
+        status_token;
+
+      TokenInfo
+        *token_info;
+
+      next=0;
+      token_info=AcquireTokenInfo();
+      token=AcquireString("");
+      status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
+        &breaker,&next,&quote);
+      while (status_token == 0)
+      {
+        raqm_add_font_feature(rq,token,strlen(token));
+        status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
+          &breaker,&next,&quote);
+      }
+      token_info=DestroyTokenInfo(token_info);
+      token=DestroyString(token);
+    }
+  if (raqm_layout(rq) == 0)
+    goto cleanup;
+  glyphs=raqm_get_glyphs(rq,&extent);
+  if (glyphs == (raqm_glyph_t *) NULL)
+    {
+      extent=0;
+      goto cleanup;
+    }
+  *grapheme=(GraphemeInfo *) AcquireQuantumMemory(extent,sizeof(**grapheme));
+  if (*grapheme == (GraphemeInfo *) NULL)
+    {
+      extent=0;
+      goto cleanup;
+    }
+  for (i=0; i < (ssize_t) extent; i++)
+  {
+    (*grapheme)[i].index=glyphs[i].index;
+    (*grapheme)[i].x_offset=glyphs[i].x_offset;
+    (*grapheme)[i].x_advance=glyphs[i].x_advance;
+    (*grapheme)[i].y_offset=glyphs[i].y_offset;
+    (*grapheme)[i].cluster=glyphs[i].cluster;
+  }
+
+cleanup:
+  raqm_destroy(rq);
+  return(extent);
+#else
+  const char
+    *p;
+
+  FT_Error
+    ft_status;
+
+  register ssize_t
+    i;
+
+  ssize_t
+    last_glyph;
+
+  /*
+    Simple layout for bi-directional text (right-to-left or left-to-right).
+  */
+  *grapheme=(GraphemeInfo *) AcquireQuantumMemory(length+1,sizeof(**grapheme));
+  if (*grapheme == (GraphemeInfo *) NULL)
+    return(0);
+  last_glyph=0;
+  p=text;
+  for (i=0; GetUTFCode(p) != 0; p+=GetUTFOctets(p), i++)
+  {
+    (*grapheme)[i].index=FT_Get_Char_Index(face,GetUTFCode(p));
+    (*grapheme)[i].x_offset=0;
+    (*grapheme)[i].y_offset=0;
+    if (((*grapheme)[i].index != 0) && (last_glyph != 0))
+      {
+        if (FT_HAS_KERNING(face))
+          {
+            FT_Vector
+              kerning;
+
+            ft_status=FT_Get_Kerning(face,(FT_UInt) last_glyph,(FT_UInt)
+              (*grapheme)[i].index,ft_kerning_default,&kerning);
+            if (ft_status == 0)
+              (*grapheme)[i-1].x_advance+=(FT_Pos) ((draw_info->direction ==
+                RightToLeftDirection ? -1.0 : 1.0)*kerning.x);
+          }
+      }
+    ft_status=FT_Load_Glyph(face,(*grapheme)[i].index,flags);
+    (*grapheme)[i].x_advance=face->glyph->advance.x;
+    (*grapheme)[i].cluster=p-text;
+    last_glyph=(*grapheme)[i].index;
+  }
+  return((size_t) i);
+#endif
+}
+
 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
   DrawInfo *draw_info)
 {
@@ -960,10 +1185,9 @@ static int TraceCubicBezier(FT_Vector *p
     path[MaxTextExtent];
 
   affine=draw_info->affine;
-  (void) FormatLocaleString(path,MaxTextExtent,
-    "C%g,%g %g,%g %g,%g",affine.tx+p->x/64.0,affine.ty-
-    p->y/64.0,affine.tx+q->x/64.0,affine.ty-q->y/64.0,affine.tx+to->x/64.0,
-    affine.ty-to->y/64.0);
+  (void) FormatLocaleString(path,MaxTextExtent,"C%g,%g %g,%g %g,%g",affine.tx+
+    p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,affine.ty-q->y/64.0,
+    affine.tx+to->x/64.0,affine.ty-to->y/64.0);
   (void) ConcatenateString(&draw_info->primitive,path);
   return(0);
 }
@@ -977,8 +1201,8 @@ static int TraceLineTo(FT_Vector *to,Dra
     path[MaxTextExtent];
 
   affine=draw_info->affine;
-  (void) FormatLocaleString(path,MaxTextExtent,"L%g,%g",affine.tx+
-    to->x/64.0,affine.ty-to->y/64.0);
+  (void) FormatLocaleString(path,MaxTextExtent,"L%g,%g",affine.tx+to->x/64.0,
+    affine.ty-to->y/64.0);
   (void) ConcatenateString(&draw_info->primitive,path);
   return(0);
 }
@@ -992,8 +1216,8 @@ static int TraceMoveTo(FT_Vector *to,Dra
     path[MaxTextExtent];
 
   affine=draw_info->affine;
-  (void) FormatLocaleString(path,MaxTextExtent,"M%g,%g",affine.tx+
-    to->x/64.0,affine.ty-to->y/64.0);
+  (void) FormatLocaleString(path,MaxTextExtent,"M%g,%g",affine.tx+to->x/64.0,
+    affine.ty-to->y/64.0);
   (void) ConcatenateString(&draw_info->primitive,path);
   return(0);
 }
@@ -1008,9 +1232,9 @@ static int TraceQuadraticBezier(FT_Vecto
     path[MaxTextExtent];
 
   affine=draw_info->affine;
-  (void) FormatLocaleString(path,MaxTextExtent,"Q%g,%g %g,%g",
-    affine.tx+control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,
-    affine.ty-to->y/64.0);
+  (void) FormatLocaleString(path,MaxTextExtent,"Q%g,%g %g,%g",affine.tx+
+    control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,affine.ty-
+    to->y/64.0);
   (void) ConcatenateString(&draw_info->primitive,path);
   return(0);
 }
@@ -1037,12 +1261,12 @@ static MagickBooleanType RenderFreetype(
   const char
     *value;
 
-  double
-    direction;
-
   DrawInfo
     *annotate_info;
 
+  ExceptionInfo
+    *exception;
+
   FT_BBox
     bounds;
 
@@ -1077,6 +1301,9 @@ static MagickBooleanType RenderFreetype(
     glyph,
     last_glyph;
 
+  GraphemeInfo
+    *grapheme;
+
   MagickBooleanType
     status;
 
@@ -1087,6 +1314,12 @@ static MagickBooleanType RenderFreetype(
   register char
     *p;
 
+  register ssize_t
+    i;
+
+  size_t
+    length;
+
   ssize_t
     code,
     y;
@@ -1121,14 +1354,16 @@ static MagickBooleanType RenderFreetype(
       args.pathname=ConstantString(draw_info->font+1);
   face=(FT_Face) NULL;
   ft_status=FT_Open_Face(library,&args,(long) draw_info->face,&face);
-  args.pathname=DestroyString(args.pathname);
+  exception=(&image->exception);
   if (ft_status != 0)
     {
       (void) FT_Done_FreeType(library);
-      (void) ThrowMagickException(&image->exception,GetMagickModule(),TypeError,
-        "UnableToReadFont","`%s'",draw_info->font);
+      (void) ThrowMagickException(exception,GetMagickModule(),TypeError,
+        "UnableToReadFont","`%s'",args.pathname);
+      args.pathname=DestroyString(args.pathname);
       return(MagickFalse);
     }
+  args.pathname=DestroyString(args.pathname);
   if ((draw_info->metrics != (char *) NULL) &&
       (IsPathAccessible(draw_info->metrics) != MagickFalse))
     (void) FT_Attach_File(face,draw_info->metrics);
@@ -1170,7 +1405,11 @@ static MagickBooleanType RenderFreetype(
         encoding_type=ft_encoding_wansung;
       ft_status=FT_Select_Charmap(face,encoding_type);
       if (ft_status != 0)
-        ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
+        {
+          (void) FT_Done_Face(face);
+          (void) FT_Done_FreeType(library);
+          ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
+        }
     }
   /*
     Set text size.
@@ -1194,6 +1433,12 @@ static MagickBooleanType RenderFreetype(
   ft_status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
     (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
     (FT_UInt) resolution.y);
+  if (ft_status != 0)
+    {
+      (void) FT_Done_Face(face);
+      (void) FT_Done_FreeType(library);
+      ThrowBinaryException(TypeError,"UnableToReadFont",draw_info->font);
+    }
   metrics->pixels_per_em.x=face->size->metrics.x_ppem;
   metrics->pixels_per_em.y=face->size->metrics.y_ppem;
   metrics->ascent=(double) face->size->metrics.ascender/64.0;
@@ -1227,7 +1472,9 @@ static MagickBooleanType RenderFreetype(
       encoding != (char *) NULL ? encoding : "none",
       draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
       draw_info->pointsize);
-  flags=FT_LOAD_NO_BITMAP;
+  flags=FT_LOAD_DEFAULT;
+  if (draw_info->render == MagickFalse)
+    flags=FT_LOAD_NO_BITMAP;
   if (draw_info->text_antialias == MagickFalse)
     flags|=FT_LOAD_TARGET_MONO;
   else
@@ -1259,7 +1506,10 @@ static MagickBooleanType RenderFreetype(
       affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
     }
   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+  if (annotate_info->dash_pattern != (double *) NULL)
+    annotate_info->dash_pattern[0]=0.0;
   (void) CloneString(&annotate_info->primitive,"path '");
+  status=MagickTrue;
   if (draw_info->render != MagickFalse)
     {
       if (image->storage_class != DirectClass)
@@ -1267,9 +1517,6 @@ static MagickBooleanType RenderFreetype(
       if (image->matte == MagickFalse)
         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
     }
-  direction=1.0;
-  if (draw_info->direction == RightToLeftDirection)
-    direction=(-1.0);
   point.x=0.0;
   point.y=0.0;
   for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
@@ -1284,32 +1531,22 @@ static MagickBooleanType RenderFreetype(
       if (utf8 != (unsigned char *) NULL)
         p=(char *) utf8;
     }
-  status=MagickTrue;
-  for (code=0; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
+  grapheme=(GraphemeInfo *) NULL;
+  length=ComplexTextLayout(image,draw_info,p,strlen(p),face,flags,&grapheme);
+  code=0;
+  for (i=0; i < (ssize_t) length; i++)
   {
     /*
       Render UTF-8 sequence.
     */
-    glyph.id=FT_Get_Char_Index(face,GetUTFCode(p));
+    glyph.id=grapheme[i].index;
     if (glyph.id == 0)
       glyph.id=FT_Get_Char_Index(face,'?');
     if ((glyph.id != 0) && (last_glyph.id != 0))
-      {
-        if (fabs(draw_info->kerning) >= MagickEpsilon)
-          origin.x+=(FT_Pos) (64.0*direction*draw_info->kerning);
-        else
-          if (FT_HAS_KERNING(face))
-            {
-              FT_Vector
-                kerning;
-
-              ft_status=FT_Get_Kerning(face,last_glyph.id,glyph.id,
-                ft_kerning_default,&kerning);
-              if (ft_status == 0)
-                origin.x+=(FT_Pos) (direction*kerning.x);
-            }
-        }
+      origin.x+=(FT_Pos) (64.0*draw_info->kerning);
     glyph.origin=origin;
+    glyph.origin.x+=grapheme[i].x_offset;
+    glyph.origin.y+=grapheme[i].y_offset;
     ft_status=FT_Load_Glyph(face,glyph.id,flags);
     if (ft_status != 0)
       continue;
@@ -1321,27 +1558,29 @@ static MagickBooleanType RenderFreetype(
     if (ft_status != 0)
       continue;
     if ((p == draw_info->text) || (bounds.xMin < metrics->bounds.x1))
-      metrics->bounds.x1=(double) bounds.xMin;
+      if (bounds.xMin != 0)
+        metrics->bounds.x1=(double) bounds.xMin;
     if ((p == draw_info->text) || (bounds.yMin < metrics->bounds.y1))
-      metrics->bounds.y1=(double) bounds.yMin;
+      if (bounds.yMin != 0)
+        metrics->bounds.y1=(double) bounds.yMin;
     if ((p == draw_info->text) || (bounds.xMax > metrics->bounds.x2))
-      metrics->bounds.x2=(double) bounds.xMax;
+      if (bounds.xMax != 0)
+        metrics->bounds.x2=(double) bounds.xMax;
     if ((p == draw_info->text) || (bounds.yMax > metrics->bounds.y2))
-      metrics->bounds.y2=(double) bounds.yMax;
-    if ((draw_info->stroke.opacity != TransparentOpacity) ||
-        (draw_info->stroke_pattern != (Image *) NULL))
+      if (bounds.yMax != 0)
+        metrics->bounds.y2=(double) bounds.yMax;
+    if (((draw_info->stroke.opacity != TransparentOpacity) ||
+         (draw_info->stroke_pattern != (Image *) NULL)) &&
+        ((status != MagickFalse) && (draw_info->render != MagickFalse)))
       {
-        if ((status != MagickFalse) && (draw_info->render != MagickFalse))
-          {
-            /*
-              Trace the glyph.
-            */
-            annotate_info->affine.tx=glyph.origin.x/64.0;
-            annotate_info->affine.ty=glyph.origin.y/64.0;
-            (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->
-              outline,&OutlineMethods,annotate_info);
-          }
-        }
+        /*
+          Trace the glyph.
+        */
+        annotate_info->affine.tx=glyph.origin.x/64.0;
+        annotate_info->affine.ty=(-glyph.origin.y/64.0);
+        (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->outline,
+          &OutlineMethods,annotate_info);
+      }
     FT_Vector_Transform(&glyph.origin,&affine);
     (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
     ft_status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
@@ -1358,16 +1597,20 @@ static MagickBooleanType RenderFreetype(
         CacheView
           *image_view;
 
-        ExceptionInfo
-          *exception;
-
         register unsigned char
           *p;
 
+        MagickBooleanType
+          transparent_fill;
+
         /*
           Rasterize the glyph.
         */
-        exception=(&image->exception);
+        transparent_fill=((draw_info->fill.opacity == TransparentOpacity) &&
+          (draw_info->fill_pattern == (Image *) NULL) &&
+          (draw_info->stroke.opacity == TransparentOpacity) &&
+          (draw_info->stroke_pattern == (Image *) NULL)) ? MagickTrue :
+          MagickFalse;
         p=bitmap->bitmap.buffer;
         image_view=AcquireAuthenticCacheView(image,exception);
         for (y=0; y < (ssize_t) bitmap->bitmap.rows; y++)
@@ -1415,7 +1658,8 @@ static MagickBooleanType RenderFreetype(
             x_offset++;
             if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
               {
-                q++;
+                if (q != (PixelPacket *) NULL)
+                  q++;
                 continue;
               }
             if (bitmap->bitmap.pixel_mode != ft_pixel_mode_mono)
@@ -1429,14 +1673,25 @@ static MagickBooleanType RenderFreetype(
               q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
                 exception);
             if (q == (PixelPacket *) NULL)
+              continue;
+            if (transparent_fill == MagickFalse)
               {
-                q++;
-                continue;
+                (void) GetFillColor(draw_info,x_offset,y_offset,&fill_color);
+                fill_opacity=QuantumRange-fill_opacity*(QuantumRange-
+                  fill_color.opacity);
+                MagickCompositeOver(&fill_color,fill_opacity,q,q->opacity,q);
+              }
+            else
+              {
+                double
+                  Sa,
+                  Da;
+                
+                Da=1.0-(QuantumScale*(QuantumRange-q->opacity));
+                Sa=fill_opacity;
+                fill_opacity=(1.0-RoundToUnity(Sa+Da-Sa*Da))*QuantumRange;
+                SetPixelAlpha(q,fill_opacity);
               }
-            (void) GetFillColor(draw_info,x_offset,y_offset,&fill_color);
-            fill_opacity=QuantumRange-fill_opacity*(QuantumRange-
-              fill_color.opacity);
-            MagickCompositeOver(&fill_color,fill_opacity,q,q->opacity,q);
             if (active == MagickFalse)
               {
                 sync=SyncCacheViewAuthenticPixels(image_view,exception);
@@ -1450,75 +1705,52 @@ static MagickBooleanType RenderFreetype(
             status=MagickFalse;
         }
         image_view=DestroyCacheView(image_view);
+        if (((draw_info->stroke.opacity != TransparentOpacity) ||
+             (draw_info->stroke_pattern != (Image *) NULL)) &&
+            (status != MagickFalse))
+          {
+            /*
+              Draw text stroke.
+            */
+            annotate_info->linejoin=RoundJoin;
+            annotate_info->affine.tx=offset->x;
+            annotate_info->affine.ty=offset->y;
+            (void) ConcatenateString(&annotate_info->primitive,"'");
+            (void) DrawImage(image,annotate_info);
+            (void) CloneString(&annotate_info->primitive,"path '");
+          }
       }
-    if ((bitmap->left+bitmap->bitmap.width) > metrics->width)
-      metrics->width=bitmap->left+bitmap->bitmap.width;
     if ((fabs(draw_info->interword_spacing) >= MagickEpsilon) &&
-        (IsUTFSpace(GetUTFCode(p)) != MagickFalse) &&
+        (IsUTFSpace(GetUTFCode(p+grapheme[i].cluster)) != MagickFalse) &&
         (IsUTFSpace(code) == MagickFalse))
-      origin.x+=(FT_Pos) (64.0*direction*draw_info->interword_spacing);
+      origin.x+=(FT_Pos) (64.0*draw_info->interword_spacing);
     else
-      origin.x+=(FT_Pos) (direction*face->glyph->advance.x);
+      origin.x+=(FT_Pos) grapheme[i].x_advance;
     metrics->origin.x=(double) origin.x;
     metrics->origin.y=(double) origin.y;
+    if (metrics->origin.x > metrics->width)
+      metrics->width=metrics->origin.x;
     if (last_glyph.id != 0)
       FT_Done_Glyph(last_glyph.image);
     last_glyph=glyph;
-    code=GetUTFCode(p);
+    code=GetUTFCode(p+grapheme[i].cluster);
   }
+  if (grapheme != (GraphemeInfo *) NULL)
+    grapheme=(GraphemeInfo *) RelinquishMagickMemory(grapheme);
   if (utf8 != (unsigned char *) NULL)
     utf8=(unsigned char *) RelinquishMagickMemory(utf8);
   if (last_glyph.id != 0)
     FT_Done_Glyph(last_glyph.image);
-  if ((draw_info->stroke.opacity != TransparentOpacity) ||
-      (draw_info->stroke_pattern != (Image *) NULL))
-    {
-      if ((status != MagickFalse) && (draw_info->render != MagickFalse))
-        {
-          /*
-            Draw text stroke.
-          */
-          annotate_info->linejoin=RoundJoin;
-          annotate_info->affine.tx=offset->x;
-          annotate_info->affine.ty=offset->y;
-          (void) ConcatenateString(&annotate_info->primitive,"'");
-          (void) DrawImage(image,annotate_info);
-        }
-      }
   /*
     Determine font metrics.
   */
-  glyph.id=FT_Get_Char_Index(face,'_');
-  glyph.origin=origin;
-  ft_status=FT_Load_Glyph(face,glyph.id,flags);
-  if (ft_status == 0)
-    {
-      ft_status=FT_Get_Glyph(face->glyph,&glyph.image);
-      if (ft_status == 0)
-        {
-          ft_status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->
-            outline,&bounds);
-          if (ft_status == 0)
-            {
-              FT_Vector_Transform(&glyph.origin,&affine);
-              (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
-              ft_status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
-                (FT_Vector *) NULL,MagickTrue);
-              bitmap=(FT_BitmapGlyph) glyph.image;
-              if (bitmap->left > metrics->width)
-                metrics->width=bitmap->left;
-            }
-        }
-      FT_Done_Glyph(glyph.image);
-    }
-  metrics->width-=metrics->bounds.x1/64.0;
-  metrics->width+=annotate_info->stroke_width;
   metrics->bounds.x1/=64.0;
   metrics->bounds.y1/=64.0;
   metrics->bounds.x2/=64.0;
   metrics->bounds.y2/=64.0;
   metrics->origin.x/=64.0;
   metrics->origin.y/=64.0;
+  metrics->width/=64.0;
   /*
     Relinquish resources.
   */
@@ -1570,41 +1802,48 @@ static MagickBooleanType RenderFreetype(
 %
 */
 
-static inline size_t MagickMin(const size_t x,const size_t y)
-{
-  if (x < y)
-    return(x);
-  return(y);
-}
-
-static char *EscapeParenthesis(const char *text)
+static char *EscapeParenthesis(const char *source)
 {
   char
-    *buffer;
+    *destination;
 
   register char
-    *p;
+    *q;
 
-  register ssize_t
-    i;
+  register const char
+    *p;
 
   size_t
-    escapes;
+    length;
 
-  escapes=0;
-  buffer=AcquireString(text);
-  p=buffer;
-  for (i=0; i < (ssize_t) MagickMin(strlen(text),MaxTextExtent-escapes-1); i++)
+  assert(source != (const char *) NULL);
+  length=0;
+  for (p=source; *p != '\0'; p++)
   {
-    if ((text[i] == '(') || (text[i] == ')'))
+    if ((*p == '\\') || (*p == '(') || (*p == ')'))
       {
-        *p++='\\';
-        escapes++;
+        if (~length < 1)
+          ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
+        length++;
       }
-    *p++=text[i];
+    length++;
+  }
+  destination=(char *) NULL;
+  if (~length >= (MaxTextExtent-1))
+    destination=(char *) AcquireQuantumMemory(length+MaxTextExtent,
+      sizeof(*destination));
+  if (destination == (char *) NULL)
+    ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
+  *destination='\0';
+  q=destination;
+  for (p=source; *p != '\0'; p++)
+  {
+    if ((*p == '\\') || (*p == '(') || (*p == ')'))
+      *q++='\\';
+    *q++=(*p);
   }
-  *p='\0';
-  return(buffer);
+  *q='\0';
+  return(destination);
 }
 
 static MagickBooleanType RenderPostscript(Image *image,