1 /*
2  *  QuartzTextLayout.h
3  *
4  *  Original Code by Evan Jones on Wed Oct 02 2002.
5  *  Contributors:
6  *  Shane Caraveo, ActiveState
7  *  Bernd Paradies, Adobe
8  *
9  */
10 
11 #ifndef QUARTZTEXTLAYOUT_H
12 #define QUARTZTEXTLAYOUT_H
13 
14 #include <Cocoa/Cocoa.h>
15 
16 #include "QuartzTextStyle.h"
17 
18 
19 class QuartzTextLayout {
20 public:
21 	/** Create a text layout for drawing. */
QuartzTextLayout()22 	QuartzTextLayout() : mString(NULL), mLine(NULL), stringLength(0) {
23 	}
24 
~QuartzTextLayout()25 	~QuartzTextLayout() {
26 		if (mString) {
27 			CFRelease(mString);
28 			mString = NULL;
29 		}
30 		if (mLine) {
31 			CFRelease(mLine);
32 			mLine = NULL;
33 		}
34 	}
35 
setText(std::string_view sv,CFStringEncoding encoding,const QuartzTextStyle & r)36 	CFStringEncoding setText(std::string_view sv, CFStringEncoding encoding, const QuartzTextStyle &r) {
37 		// First clear current values in case of failure.
38 		if (mString) {
39 			CFRelease(mString);
40 			mString = NULL;
41 		}
42 		if (mLine) {
43 			CFRelease(mLine);
44 			mLine = NULL;
45 		}
46 
47 		const UInt8 *puiBuffer = reinterpret_cast<const UInt8 *>(sv.data());
48 		CFStringRef str = CFStringCreateWithBytes(NULL, puiBuffer, sv.length(), encoding, false);
49 		if (!str) {
50 			// Failed to decode bytes into string with given encoding so try
51 			// MacRoman which should accept any byte.
52 			encoding = kCFStringEncodingMacRoman;
53 			str = CFStringCreateWithBytes(NULL, puiBuffer, sv.length(), encoding, false);
54 		}
55 		if (!str) {
56 			return encoding;
57 		}
58 
59 		stringLength = CFStringGetLength(str);
60 
61 		CFMutableDictionaryRef stringAttribs = r.getCTStyle();
62 
63 		mString = ::CFAttributedStringCreate(NULL, str, stringAttribs);
64 
65 		mLine = ::CTLineCreateWithAttributedString(mString);
66 
67 		CFRelease(str);
68 		return encoding;
69 	}
70 
71 	/** Draw the text layout into a CGContext at the specified position.
72 	* @param gc The CGContext in which to draw the text.
73 	* @param x The x axis position to draw the baseline in the current CGContext.
74 	* @param y The y axis position to draw the baseline in the current CGContext. */
draw(CGContextRef gc,float x,float y)75 	void draw(CGContextRef gc, float x, float y) {
76 		if (!mLine)
77 			return;
78 
79 		::CGContextSetTextMatrix(gc, CGAffineTransformMakeScale(1.0, -1.0));
80 
81 		// Set the text drawing position.
82 		::CGContextSetTextPosition(gc, x, y);
83 
84 		// And finally, draw!
85 		::CTLineDraw(mLine, gc);
86 	}
87 
MeasureStringWidth()88 	float MeasureStringWidth() {
89 		if (mLine == NULL)
90 			return 0.0f;
91 
92 		return static_cast<float>(::CTLineGetTypographicBounds(mLine, NULL, NULL, NULL));
93 	}
94 
getCTLine()95 	CTLineRef getCTLine() {
96 		return mLine;
97 	}
98 
getStringLength()99 	CFIndex getStringLength() {
100 		return stringLength;
101 	}
102 
103 private:
104 	CFAttributedStringRef mString;
105 	CTLineRef mLine;
106 	CFIndex stringLength;
107 };
108 
109 #endif
110