--- /dev/null
+#include "WBMarkup.h"
+
+@class WKView;
+
+extern "C" {
+ void WebThreadLock();
+ CGContextRef WKGetCurrentGraphicsContext();
+ void WKViewLockFocus(WKView *);
+ void WKViewUnlockFocus(WKView *);
+ void WKViewDisplayRect(WKView *, CGRect);
+}
+
+@interface DOMElement : NSObject
+- (void) setInnerHTML:(NSString *)value;
+- (void) setInnerText:(NSString *)value;
+- (void) setAttribute:(NSString *)name value:(NSString *)value;
+- (void) removeAttribute:(NSString *)name;
+- (DOMElement *) firstChild;
+- (void) appendChild:(DOMElement *)child;
+- (void) removeChild:(DOMElement *)child;
+- (void) setScrollXOffset:(float)x scrollYOffset:(float)y;
+@end
+
+@interface DOMDocument : NSObject
+- (DOMElement *) getElementById:(NSString *)id;
+@end
+
+@interface WebPreferences : NSObject
+- (id) initWithIdentifier:(NSString *)identifier;
+- (void) setPlugInsEnabled:(BOOL)value;
+@end
+
+@interface WebFrameView : NSObject
+- (void) setAllowsScrolling:(BOOL)value;
+@end
+
+@interface WebFrame : NSObject
+- (WebFrameView *) frameView;
+- (void) _setLoadsSynchronously:(BOOL)value;
+- (void) loadHTMLString:(NSString *)string baseURL:(id)url;
+- (void) forceLayoutAdjustingViewSize:(BOOL)adjust;
+- (CGSize) renderedSizeOfNode:(DOMElement *)node constrainedToWidth:(float)width;
+- (DOMDocument *) DOMDocument;
+@end
+
+@interface WebView : NSObject
+- (id) initWithFrame:(CGRect)frame;
+- (WebFrame *) mainFrame;
+- (void) setDrawsBackground:(BOOL)value;
+- (void) setPreferences:(WebPreferences *)preferences;
+- (WKView *) _viewRef;
+@end
+
+@interface WAKWindow : NSObject
+- (id) initWithFrame:(CGRect)frame;
+- (void) setContentView:(WebView *)view;
+@end
+
+static WBMarkup *SharedMarkup_;
+
+@implementation WBMarkup
+
++ (BOOL) isSharedMarkupCreated {
+ return SharedMarkup_ != nil;
+}
+
++ (WBMarkup *) sharedMarkup {
+ if (SharedMarkup_ == nil)
+ SharedMarkup_ = [[WBMarkup alloc] init];
+ return SharedMarkup_;
+}
+
+- (id) init {
+ if ((self = [super init]) != nil) {
+ WebThreadLock();
+
+ SharedMarkup_ = self;
+
+ view_ = [[WebView alloc] initWithFrame:CGRectMake(0, 0, 640, 5000)];
+ [view_ setDrawsBackground:NO];
+
+ WebPreferences *preferences([[WebPreferences alloc] initWithIdentifier:@"com.apple.webkit.webmarkup"]);
+ [preferences setPlugInsEnabled:NO];
+ [view_ setPreferences:preferences];
+ [preferences release];
+
+ window_ = [[WAKWindow alloc] initWithFrame:CGRectMake(0, 0, 640, 5000)];
+ [window_ setContentView:view_];
+
+ WebFrame *frame([view_ mainFrame]);
+ [[frame frameView] setAllowsScrolling:NO];
+ [frame _setLoadsSynchronously:YES];
+
+ [frame loadHTMLString:@"<html><body style='margin: 0px; word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space'><div id='size'><div id='text'></div></div></body></html>" baseURL:nil];
+ } return self;
+}
+
+- (void) dealloc {
+ [window_ release];
+ [view_ release];
+ [super dealloc];
+}
+
+- (WebView *) _webView {
+ return view_;
+}
+
+- (void) setStringDrawingOrigin:(CGPoint)origin {
+ origin_ = origin;
+}
+
+- (void) clearStringDrawingOrigin {
+ origin_ = CGPointZero;
+}
+
+- (CGSize) sizeOfMarkup:(NSString *)markup forWidth:(float)width {
+ WebThreadLock();
+
+ if (![self _webPrepareContextForTextDrawing:NO])
+ return CGSizeZero;
+
+ [text_ setInnerHTML:markup];
+ [text_ removeAttribute:@"style"];
+
+ NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: 5000px"], width]);
+ [size_ setAttribute:@"style" value:value];
+ [value release];
+
+ [[view_ mainFrame] forceLayoutAdjustingViewSize:YES];
+ return [[view_ mainFrame] renderedSizeOfNode:text_ constrainedToWidth:width];
+}
+
+- (CGSize) sizeOfString:(NSString *)string withStyle:(NSString *)style forWidth:(float)width {
+ WebThreadLock();
+
+ if (![self _webPrepareContextForTextDrawing:NO])
+ return CGSizeZero;
+
+ [size_ removeChild:[size_ firstChild]];
+
+ WebFrame *frame([view_ mainFrame]);
+
+ [frame forceLayoutAdjustingViewSize:YES];
+ [text_ setInnerText:string];
+ [self _setupWithStyle:style width:width height:5000];
+ [frame forceLayoutAdjustingViewSize:YES];
+
+ return [[view_ mainFrame] renderedSizeOfNode:text_ constrainedToWidth:width];
+}
+
+- (NSString *) _styleFormatString:(NSString *)style {
+ return style;
+}
+
+- (void) _setupWithStyle:(NSString *)style width:(float)width height:(float)height {
+ WebThreadLock();
+
+ if (style != nil && [style length] != 0)
+ [text_ setAttribute:@"style" value:style];
+ else
+ [text_ removeAttribute:@"style"];
+
+ NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: %.0fpx"], width, height]);
+ [size_ setAttribute:@"style" value:value];
+ [value release];
+
+ [size_ appendChild:text_];
+}
+
+- (BOOL) _webPrepareContextForTextDrawing:(BOOL)drawing {
+ WebThreadLock();
+
+ if (document_ == nil) {
+ WebFrame *frame([view_ mainFrame]);
+
+ document_ = [[frame DOMDocument] retain];
+ if (document_ == nil) {
+ NSLog(@"*** ERROR: no DOM document in text-drawing webview");
+ return NO;
+ }
+
+ text_ = [[document_ getElementById:@"text"] retain];
+ size_ = [[document_ getElementById:@"size"] retain];
+
+ if (text_ == nil || size_ == nil) {
+ NSLog(@"*** ERROR: cannot find DOM element required for text drawing");
+ return NO;
+ }
+ }
+
+ context_ = NULL;
+
+ if (!drawing)
+ context_ = NULL;
+ else {
+ context_ = WKGetCurrentGraphicsContext();
+ if (context_ == NULL) {
+ NSLog(@"*** ERROR: no CGContext set for drawing");
+ return NO;
+ }
+ }
+
+ return YES;
+}
+
+- (void) drawMarkup:(NSString *)markup atPoint:(CGPoint)point {
+ [self drawMarkup:markup inRect:CGRectMake(point.x, point.y, 65535, 65535)];
+}
+
+- (void) drawMarkup:(NSString *)markup inRect:(CGRect)rect {
+ WebThreadLock();
+
+ if (![self _webPrepareContextForTextDrawing:YES])
+ return;
+
+ [text_ setInnerHTML:markup];
+ [text_ removeAttribute:@"style"];
+
+ NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: %.0fpx"], CGRectGetWidth(rect), CGRectGetHeight(rect)]);
+ [size_ setAttribute:@"style" value:value];
+ [value release];
+
+ [[view_ mainFrame] forceLayoutAdjustingViewSize:YES];
+
+ [text_ setScrollXOffset:origin_.x scrollYOffset:origin_.y];
+
+ WKView *view([view_ _viewRef]);
+
+ CGContextSaveGState(context_); {
+ CGContextTranslateCTM(context_, rect.origin.x, rect.origin.y);
+
+ WKViewLockFocus(view); {
+ WKViewDisplayRect(view, CGRectMake(0, 0, rect.origin.x, rect.origin.y));
+ } WKViewUnlockFocus(view);
+ } CGContextRestoreGState(context_);
+}
+
+- (void) drawString:(NSString *)string atPoint:(CGPoint)point withStyle:(NSString *)style {
+ [self drawString:string inRect:CGRectMake(point.x, point.y, 65535, 65535) withStyle:style];
+}
+
+- (void) drawString:(NSString *)string inRect:(CGRect)rect withStyle:(NSString *)style {
+ WebThreadLock();
+
+ if (![self _webPrepareContextForTextDrawing:YES])
+ return;
+
+ [size_ removeChild:[size_ firstChild]];
+
+ WebFrame *frame([view_ mainFrame]);
+
+ [frame forceLayoutAdjustingViewSize:YES];
+ [text_ setInnerText:string];
+ [self _setupWithStyle:style width:CGRectGetWidth(rect) height:CGRectGetHeight(rect)];
+ [frame forceLayoutAdjustingViewSize:YES];
+
+ [text_ setScrollXOffset:origin_.x scrollYOffset:origin_.y];
+
+ WKView *view([view_ _viewRef]);
+
+ CGContextSaveGState(context_); {
+ CGContextTranslateCTM(context_, rect.origin.x, rect.origin.y);
+
+ WKViewLockFocus(view); {
+ WKViewDisplayRect(view, CGRectMake(0, 0, rect.size.width, rect.size.height));
+ } WKViewUnlockFocus(view);
+ } CGContextRestoreGState(context_);
+}
+
+@end