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;
#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])
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);
UIGraphicsPopContext();
}
-- (CGSize)size
+- (void)drawInContext:(CGContextRef)context atPosition:(CGPoint)position
{
- return [_text sizeWithFont:_font];
+ UIGraphicsPushContext(context);
+ [_text drawAtPoint:position withFont:_font];
+ UIGraphicsPopContext();
}
- (void)dealloc
#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)
{
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"),
{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},
#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"
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
}
#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"
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 */
{
/* 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
}
}
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);
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 */
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 */
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},
#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);
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, ¶->width, ¶->height, ¶->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