File ImageMagick-CVE-2017-14989.patch of Package ImageMagick.9293
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,"e);
+ while (status_token == 0)
+ {
+ raqm_add_font_feature(rq,token,strlen(token));
+ status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
+ &breaker,&next,"e);
+ }
+ 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,