]> granicus.if.org Git - graphviz/commitdiff
- fix incorrect bitmap format rendered (missing initializer causing off-by-one error)
authorglenlow <devnull@localhost>
Thu, 22 Apr 2010 13:24:31 +0000 (13:24 +0000)
committerglenlow <devnull@localhost>
Thu, 22 Apr 2010 13:24:31 +0000 (13:24 +0000)
- rendering bitmaps on iPhoneOS now uses memory-mapped scratch files
- text layout better abstracted between Mac OS X and iPhoneOS
- more robust environment detection at build time

plugin/quartz/GVTextLayout.h
plugin/quartz/GVTextLayout.m
plugin/quartz/gvdevice_quartz.c
plugin/quartz/gvplugin_quartz.c
plugin/quartz/gvplugin_quartz.h
plugin/quartz/gvrender_quartz.c
plugin/quartz/gvtextlayout_quartz.c

index 1926846e701f8bf2adc5cb29eb5b40e956357137..fcf41f97843affd8687493934048f1afdf494d79 100644 (file)
        NSString* _text;
 }
 
-@property (readonly, nonatomic) UIFont* font;
-@property (readonly, nonatomic) NSString* text;
-@property (readonly, nonatomic) CGSize size;
-
 - (id)initWithFontName:(char*)fontName fontSize:(CGFloat)fontSize text:(char*)text;
 
-- (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context;
+- (void)sizeUpWidth:(double*)width height:(double*)height yoffset:(double*)yoffset;
+- (void)drawInContext:(CGContextRef)context atPosition:(CGPoint)position;
 
 - (void)dealloc;
 
index 6e2232e3df1f38d1df4f7b5fffe14249001485ff..17293ea0166792fec4165c2c51e84cf7008845ca 100644 (file)
 
 #include "gvplugin_quartz.h"
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 20000
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
 
 #import "GVTextLayout.h"
 
-boolean quartz_textlayout(textpara_t *para, char **fontpath)
+void *quartz_new_layout(char* fontname, double fontsize, char* text)
 {
-       GVTextLayout* layout = [[GVTextLayout alloc] initWithFontName:para->fontname fontSize:para->fontsize text:para->str];
-       CGSize size = layout.size;
-       
-       para->layout = layout;
-       para->free_layout = &quartz_free_layout;
-       para->width = size.width;
-       para->height = size.height;
-       para->yoffset_layout = layout.font.ascender;
-       para->yoffset_centerline = 0;
-       
-       return TRUE;
+       return [[GVTextLayout alloc] initWithFontName:fontname fontSize:fontsize text:text];
 }
 
-void quartz_free_layout(void *layout)
+void quartz_size_layout(void *layout, double* width, double* height, double* yoffset_layout)
 {
-       [(GVTextLayout*)layout release];
+       [(GVTextLayout*)layout sizeUpWidth:width height:height yoffset:yoffset_layout];
 }
 
-void quartzgen_textpara(GVJ_t *job, pointf p, textpara_t *para)
+void quartz_draw_layout(void *layout, CGContextRef context, CGPoint position)
 {
-       CGContextRef context = (CGContextRef)job->context;
-
-       /* adjust text position */
-       switch (para->just) {
-               case 'r':
-                       p.x -= para->width;
-                       break;
-               case 'l':
-                       p.x -= 0.0;
-                       break;
-               case 'n':
-               default:
-                       p.x -= para->width / 2.0;
-                       break;
-               }
-       p.y += para->yoffset_centerline;
-       
-       GVTextLayout* layout;
-       if (para->free_layout == &quartz_free_layout)
-               layout = (GVTextLayout*)para->layout;
-       else
-               layout = [[GVTextLayout alloc] initWithFontName:para->fontname fontSize:para->fontsize text:para->str];
-               
-       CGContextSaveGState(context);
-       CGContextScaleCTM(context, 1.0, -1.0);
-       CGContextSetRGBFillColor(context, job->obj->pencolor.u.RGBA [0], job->obj->pencolor.u.RGBA [1], job->obj->pencolor.u.RGBA [2], job->obj->pencolor.u.RGBA [3]);
-       [layout drawAtPoint:CGPointMake(p.x, -p.y - para->yoffset_layout) inContext:context];
-       CGContextRestoreGState(context);
-       
-       if (para->free_layout != &quartz_free_layout)
-               [layout release];
+       [(GVTextLayout*)layout drawInContext:context atPosition:position];      
+}
+
+void quartz_free_layout(void *layout)
+{
+       [(GVTextLayout*)layout release];
 }
 
 static NSString* _defaultFontName = @"TimesNewRomanPSMT";
 
 @implementation GVTextLayout
 
-@synthesize font = _font;
-@synthesize text = _text;
-
 - (id)initWithFontName:(char*)fontName fontSize:(CGFloat)fontSize text:(char*)text
 {
        if (self = [super init])
@@ -107,6 +69,16 @@ static NSString* _defaultFontName = @"TimesNewRomanPSMT";
        return self;
 }
 
+- (void)sizeUpWidth:(double*)width height:(double*)height yoffset:(double*)yoffset
+{
+       CGSize size = [_text sizeWithFont:_font];
+       CGFloat ascender = _font.ascender;
+       
+       *width = size.width;
+       *height = size.height;
+       *yoffset = ascender;
+}
+
 - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context
 {
        UIGraphicsPushContext(context);
@@ -114,9 +86,11 @@ static NSString* _defaultFontName = @"TimesNewRomanPSMT";
        UIGraphicsPopContext();
 }
 
-- (CGSize)size
+- (void)drawInContext:(CGContextRef)context atPosition:(CGPoint)position
 {
-       return [_text sizeWithFont:_font];
+       UIGraphicsPushContext(context);
+       [_text drawAtPoint:position withFont:_font];
+       UIGraphicsPopContext(); 
 }
 
 - (void)dealloc
index 28174eabd9887f98e9a03cfce71cc3be26102fb7..d3459465c552ea66b5b647d872974aa8486cf266 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "gvplugin_quartz.h"
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040 && defined(HAVE_PANGOCAIRO)
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040 && defined(HAVE_PANGOCAIRO)
 
 const void *memory_data_consumer_get_byte_pointer(void *info)
 {
index f9c8ff21473764f3400f92407553453e190849a5..059d803d8123de814596837988343c92ffa39c7e 100644 (file)
@@ -23,11 +23,12 @@ extern gvplugin_installed_t gvtextlayout_quartz_types;
 extern gvplugin_installed_t gvloadimage_quartz_types;
 extern gvplugin_installed_t gvdevice_quartz_types;
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040
 extern gvplugin_installed_t gvdevice_quartz_types_for_cairo;
 
 /* Uniform Type Identifiers corresponding to each format_type */
 CFStringRef format_uti [] = {
+       NULL,
        NULL,
        CFSTR("com.microsoft.bmp"),
        CFSTR("com.ilm.openexr-image"),
@@ -61,7 +62,7 @@ static gvplugin_api_t apis[] = {
        {API_textlayout, &gvtextlayout_quartz_types},
        {API_loadimage, &gvloadimage_quartz_types},
        {API_device, &gvdevice_quartz_types},
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040 && defined(HAVE_PANGOCAIRO)
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040 && defined(HAVE_PANGOCAIRO)
     {API_device, &gvdevice_quartz_types_for_cairo},
 #endif
     {(api_t)0, 0},
index 0b5ddbac47feb819308f50b14861e1522a20327c..dba821c3a1da1144ad27338bc2bc89884203030e 100644 (file)
 
 #include <Availability.h>
 
-#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
-#include <ApplicationServices/ApplicationServices.h>
-#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
 #include <CoreGraphics/CoreGraphics.h>
+#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#include <ApplicationServices/ApplicationServices.h>
 #endif
 
 #include "types.h"
@@ -52,28 +52,19 @@ static const int BYTE_ALIGN = 15;                   /* align to 16 bytes */
 static const int BITS_PER_COMPONENT = 8;       /* bits per color component */
 static const int BYTES_PER_PIXEL = 4;          /* bytes per pixel */
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040
 extern CFStringRef format_uti [];
 #endif
 
 extern CGDataConsumerCallbacks device_data_consumer_callbacks;
 
-typedef struct {
-       CGFontRef font;
-       CGGlyph* glyphs;
-       size_t glyph_count;
-} quartz_layout;
-
-/* gvtextlayout_quartz.c in Mac OS X */
-/* GVTextLayout.m in iPhoneOS */
+/* gvtextlayout_quartz.c in Mac OS X: layout is a CoreText CTLineRef */
+/* GVTextLayout.m in iPhoneOS: layout is a custom Objective-C GVTextLayout */
 
+void *quartz_new_layout(char* fontname, double fontsize, char* text);
+void quartz_size_layout(void *layout, double* width, double* height, double* yoffset_layout);
+void quartz_draw_layout(void *layout, CGContextRef context, CGPoint position);
 void quartz_free_layout(void *layout);
-boolean quartz_textlayout(textpara_t *para, char **fontpath);
-
-/* gvrender_quartz.c in Mac OS X */
-/* GVTextLayout.m in iPhoneOS */
-
-void quartzgen_textpara(GVJ_t *job, pointf p, textpara_t *para);
 
 #ifdef __cplusplus
 }
index 33246e55e377a042ba42d6a586cac98588ae01c5..e15257514e2a103285d5a00c1adcf7f18540be15 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
+#include <mach/mach_host.h>
+#include <sys/mman.h>
+#endif
+
 #include "gvplugin_device.h"
 #include "gvplugin_render.h"
 #include "graph.h"
@@ -57,7 +62,7 @@ static void quartzgen_end_job(GVJ_t *job)
                case FORMAT_CGIMAGE:
                        break;
                        
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040
                default:        /* bitmap formats */
                        {
                                /* create an image destination */
@@ -84,7 +89,14 @@ static void quartzgen_end_job(GVJ_t *job)
        {
                /* create an image and save it where the window field is, which was set to the passed-in context at begin job */
                *((CGImageRef*)job->window) = CGBitmapContextCreateImage(context);
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
+               void* context_data = CGBitmapContextGetData(context);
+               size_t context_datalen = CGBitmapContextGetBytesPerRow(context) * CGBitmapContextGetHeight(context);
+#endif
                CGContextRelease(context);
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
+               munmap(context_data, context_datalen);
+#endif
        }
 }
 
@@ -133,16 +145,60 @@ static void quartzgen_begin_page(GVJ_t *job)
                
                default: /* bitmap formats */
                        {       
+                               size_t bytes_per_row = (job->width * BYTES_PER_PIXEL + BYTE_ALIGN) & ~BYTE_ALIGN;
+                               
+                               void* buffer = NULL;
+                               
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
+                               
+                               /* iPhoneOS has no swap files for memory, so if we're short of memory we need to make our own temp scratch file to back it */
+                               
+                               size_t buffer_size = job->height * bytes_per_row;
+                               mach_msg_type_number_t vm_info_size = HOST_VM_INFO_COUNT;
+                               vm_statistics_data_t vm_info;
+                               
+                               if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_info, &vm_info_size) != KERN_SUCCESS
+                                       || buffer_size * 2 > vm_info.free_count * vm_page_size)
+                               {
+                                       FILE* temp_file = tmpfile();
+                                       if (temp_file)
+                                       {
+                                               int temp_file_descriptor = fileno(temp_file);
+                                               if (temp_file_descriptor >= 0 && ftruncate(temp_file_descriptor, buffer_size) == 0)
+                                               {
+                                                       buffer = mmap(
+                                                               NULL,
+                                                               buffer_size,
+                                                               PROT_READ | PROT_WRITE,
+                                                               MAP_FILE | MAP_SHARED,
+                                                               temp_file_descriptor,
+                                                               0);
+                                                       if (buffer == (void*)-1)
+                                                               buffer = NULL;
+                                               }
+                                               fclose(temp_file);
+                                       }
+                               }
+                               if (!buffer)
+                                       buffer = mmap(
+                                               NULL,
+                                               buffer_size,
+                                               PROT_READ | PROT_WRITE,
+                                               MAP_ANON| MAP_SHARED,
+                                               -1,
+                                               0);                             
+#endif                         
+                               
                                /* create a true color bitmap for drawing into */
                                CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
                                job->context = CGBitmapContextCreate(
-                                       NULL,                                                                                                           /* data: let Quartz take care of memory management */
-                                       job->width,                                                                                                     /* width in pixels */
-                                       job->height,                                                                                            /* height in pixels */
-                                       BITS_PER_COMPONENT,                                                                                     /* bits per component */
-                                       (job->width * BYTES_PER_PIXEL + BYTE_ALIGN) & ~BYTE_ALIGN,      /* bytes per row: align to 16 byte boundary */
-                                       color_space,                                                                                            /* color space: device RGB */
-                                       kCGImageAlphaPremultipliedFirst                                                         /* bitmap info: premul ARGB has best support in OS X */
+                                       buffer,                                                 /* data: MacOSX lets system allocate, iPhoneOS use manual memory mapping */
+                                       job->width,                                             /* width in pixels */
+                                       job->height,                                    /* height in pixels */
+                                       BITS_PER_COMPONENT,                             /* bits per component */
+                                       bytes_per_row,                                  /* bytes per row: align to 16 byte boundary */
+                                       color_space,                                    /* color space: device RGB */
+                                       kCGImageAlphaPremultipliedFirst /* bitmap info: premul ARGB has best support in OS X */
                                );
                                job->imagedata = CGBitmapContextGetData((CGContextRef)job->context);
                                
@@ -228,6 +284,47 @@ static void quartzgen_path(GVJ_t *job, int filled)
        CGContextDrawPath(context, filled ? kCGPathFillStroke : kCGPathStroke);
 }
 
+void quartzgen_textpara(GVJ_t *job, pointf p, textpara_t *para)
+{
+       CGContextRef context = (CGContextRef)job->context;
+       
+       /* adjust text position */
+       switch (para->just) {
+               case 'r':
+                       p.x -= para->width;
+                       break;
+               case 'l':
+                       p.x -= 0.0;
+                       break;
+               case 'n':
+               default:
+                       p.x -= para->width / 2.0;
+                       break;
+       }
+       p.y += para->yoffset_centerline;
+       
+       void* layout;
+       if (para->free_layout == &quartz_free_layout)
+               layout = para->layout;
+       else
+               layout = quartz_new_layout(para->fontname, para->fontsize, para->str);
+       
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
+       CGContextSaveGState(context);
+       CGContextScaleCTM(context, 1.0, -1.0);
+       p.y = -p.y - para->yoffset_layout;
+#endif
+       CGContextSetRGBFillColor(context, job->obj->pencolor.u.RGBA[0], job->obj->pencolor.u.RGBA[1], job->obj->pencolor.u.RGBA[2], job->obj->pencolor.u.RGBA[3]);
+       quartz_draw_layout(layout, context, CGPointMake(p.x, p.y));
+       
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
+       CGContextRestoreGState(context);
+#endif
+
+       if (para->free_layout != &quartz_free_layout)
+               quartz_free_layout(layout);
+}
+
 static void quartzgen_ellipse(GVJ_t *job, pointf *A, int filled)
 {
        /* convert ellipse into the current path */
@@ -325,7 +422,7 @@ static gvrender_features_t render_features_quartz = {
     RGBA_DOUBLE                                /* color_type */
 };
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 20000
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040 || __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
 static gvdevice_features_t device_features_quartz = {
     GVDEVICE_BINARY_FORMAT
       | GVDEVICE_DOES_TRUECOLOR,/* flags */
@@ -352,10 +449,10 @@ gvplugin_installed_t gvrender_quartz_types[] = {
 
 gvplugin_installed_t gvdevice_quartz_types[] = {
        {FORMAT_PDF, "pdf:quartz", 8, NULL, &device_features_quartz_paged},
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 20000
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040 || __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
        {FORMAT_CGIMAGE, "cgimage:quartz", 8, NULL, &device_features_quartz},
 #endif
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1040
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040
        {FORMAT_BMP, "bmp:quartz", 8, NULL, &device_features_quartz},
        {FORMAT_GIF, "gif:quartz", 8, NULL, &device_features_quartz},
        {FORMAT_EXR, "exr:quartz", 8, NULL, &device_features_quartz},
index 10b64c4955a7be55dd9e6524a0a806b506a353b3..ff4c1c4aaf2d73eadfa9fa7ccd174651a310e05f 100644 (file)
@@ -24,9 +24,9 @@
 #include "gvplugin_textlayout.h"
 #include "gvplugin_quartz.h"
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
 
-static CTLineRef quartz_new_layout(char* fontname, CGFloat fontsize, char* text)
+void *quartz_new_layout(char* fontname, double fontsize, char* text)
 {
        CFStringRef fontnameref = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)fontname, strlen(fontname), kCFStringEncodingUTF8, FALSE);
        CFStringRef textref = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)text, strlen(text), kCFStringEncodingUTF8, FALSE);
@@ -55,103 +55,73 @@ static CTLineRef quartz_new_layout(char* fontname, CGFloat fontsize, char* text)
                CFRelease(textref);
        if (fontnameref)
                CFRelease(fontnameref);
-       return line;
+       return (void *)line;
 }
 
+void quartz_size_layout(void *layout, double* width, double* height, double* yoffset_layout)
+{
+       /* get the typographic bounds */
+       CGFloat ascent = 0.0;
+       CGFloat descent = 0.0;
+       CGFloat leading = 0.0;
+       double typowidth = CTLineGetTypographicBounds((CTLineRef)layout, &ascent, &descent, &leading);
+       CGFloat typoheight = ascent + descent;
+       
+       *width = typowidth;
+       *height = leading == 0.0 ? typoheight * 1.2 : typoheight + leading;     /* if no leading, use 20% of height */
+       *yoffset_layout = ascent;
+}
 
-boolean quartz_textlayout(textpara_t *para, char **fontpath)
+void quartz_draw_layout(void *layout, CGContextRef context, CGPoint position)
 {
-       CTLineRef line = quartz_new_layout(para->fontname, para->fontsize, para->str);
-       if (line)
+       CGContextSetTextPosition(context, position.x, position.y);
+       
+       CFArrayRef runs = CTLineGetGlyphRuns((CTLineRef)layout);
+       CFIndex run_count = CFArrayGetCount(runs);
+       CFIndex run_index;
+       for (run_index = 0; run_index < run_count; ++run_index)
        {
-               /* get the typographic bounds */
-               CGFloat ascent = 0.0;
-               CGFloat descent = 0.0;
-               CGFloat leading = 0.0;
-               double width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
-               CGFloat height = ascent + descent;
+               CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, run_index);
+               CTFontRef run_font = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
+               CGFontRef glyph_font = CTFontCopyGraphicsFont(run_font, NULL);
+               CFIndex glyph_count = CTRunGetGlyphCount(run);
+               CGGlyph glyphs[glyph_count];
+               CGPoint positions[glyph_count];
+               CFRange everything = CFRangeMake(0, 0);
+               CTRunGetGlyphs(run, everything, glyphs);
+               CTRunGetPositions(run, everything, positions);
                
-               /* report the layout */
-               para->layout = (void*)line;
-               para->free_layout = &quartz_free_layout;
-               para->width = width;
-               para->height = leading == 0.0 ? height * 1.2 : height + leading;        /* if no leading, use 20% of height */
-               para->yoffset_layout = ascent;
-               para->yoffset_centerline = 0;
-               return TRUE;
+               CGContextSetFont(context, glyph_font);
+               CGContextSetFontSize(context, CTFontGetSize(run_font));
+               CGContextShowGlyphsAtPositions(context, glyphs, positions, glyph_count);
+               
+               CGFontRelease(glyph_font);
        }
-       else
-               return FALSE;
-};
+}
 
 void quartz_free_layout(void *layout)
 {
        if (layout)
-               CFRelease(layout);
+               CFRelease((CTLineRef)layout);
 };
 
-void quartzgen_textpara(GVJ_t *job, pointf p, textpara_t *para)
-{
-       CGContextRef context = (CGContextRef)job->context;
+#endif
 
-       /* adjust text position */
-       switch (para->just) {
-               case 'r':
-                       p.x -= para->width;
-                       break;
-               case 'l':
-                       p.x -= 0.0;
-                       break;
-               case 'n':
-               default:
-                       p.x -= para->width / 2.0;
-                       break;
-               }
-       p.y += para->yoffset_centerline;
-       
-       CTLineRef layout;
-       if (para->free_layout == &quartz_free_layout)
-               layout = (CTLineRef)para->layout;
-       else
-               layout = quartz_new_layout(para->fontname, para->fontsize, para->str);
-               
-       /* draw it */
-       CGContextSetTextPosition(context, p.x, p.y);
-       if (job->obj->pencolor.u.RGBA [0] == 0.0 && job->obj->pencolor.u.RGBA [1] == 0.0 && job->obj->pencolor.u.RGBA [2] == 0.0 && job->obj->pencolor.u.RGBA [3] == 1.0)
-               /* optimized case for foreground color black */
-               CTLineDraw(layout, context);
-       else
+boolean quartz_textlayout(textpara_t *para, char **fontpath)
+{
+       void *line = quartz_new_layout(para->fontname, para->fontsize, para->str);
+       if (line)
        {
-               /* non-black foreground color, need to disassemble line manually and color it */
-               CFArrayRef runs = CTLineGetGlyphRuns(layout);
-               CFIndex run_count = CFArrayGetCount(runs);
-               CFIndex run_index;
-               for (run_index = 0; run_index < run_count; ++run_index)
-               {
-                       CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, run_index);
-                       CTFontRef run_font = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
-                       CGFontRef glyph_font = CTFontCopyGraphicsFont(run_font, NULL);
-                       CFIndex glyph_count = CTRunGetGlyphCount(run);
-                       CGGlyph glyphs[glyph_count];
-                       CGPoint positions[glyph_count];
-                       CFRange everything = CFRangeMake(0, 0);
-                       CTRunGetGlyphs(run, everything, glyphs);
-                       CTRunGetPositions(run, everything, positions);
-                       
-                       CGContextSetFont(context, glyph_font);
-                       CGContextSetFontSize(context, CTFontGetSize(run_font));
-                       CGContextSetRGBFillColor(context, job->obj->pencolor.u.RGBA [0], job->obj->pencolor.u.RGBA [1], job->obj->pencolor.u.RGBA [2], job->obj->pencolor.u.RGBA [3]);
-                       CGContextShowGlyphsAtPositions(context, glyphs, positions, glyph_count);
-               
-                       CGFontRelease(glyph_font);
-               }
+               /* report the layout */
+               para->layout = (void*)line;
+               para->free_layout = &quartz_free_layout;
+               para->yoffset_centerline = 0;
+               quartz_size_layout((void*)line, &para->width, &para->height, &para->yoffset_layout);
+               return TRUE;
        }
-
-       if (para->free_layout != &quartz_free_layout)
-               CFRelease(layout);
-}
-
-#endif
+       else
+               return FALSE;
+};
 
 static gvtextlayout_engine_t quartz_textlayout_engine = {
     quartz_textlayout