ATSUI provides several direct-access functions that retrieve glyph outlines—the curves that make up the shape of the glyph. You should obtain glyph outlines only when you want to handle drawing the glyph instead of letting ATSUI do it. You can make modifications to the glyph data before you draw the glyph.
Using direct-access functions, you can do the following:
Determine the native curve type of a font. TrueType fonts use quadratic curves while Type 1 (PostScript) fonts use cubic curves.
Retrieve a cubic or quadratic glyph path—that is, the segments that make up the shape of a glyph.
Determining the native curve type of a font is easy, just call the function ATSUGetNativeCurveType.
Obtaining cubic or quadratic paths for a glyph requires you to use the functions ATSUGlyphGetCubicPaths or ATSUGlyphGetQuadraticPaths, respectively. However, you can call the function ATSUGlyphGetQuadraticPaths for fonts whose native curve type is cubic, and you can call ATSUGlyphGetCubicPaths for a font whose native curve type is quadratic. In each case, the font’s curves are converted to the format specified by the function.
The curves returned by the functions are those that have been modified by hints present in the font. If you need unhinted outlines, you should use a very large point size (for example, 1000 points) and scale down the result. Alternatively, you can set the ATSUStyleRenderingOptions of the style object (ATSUStyle) to 0.
The coordinates returned for the curves and lines use the QuickDraw coordinate system. The (0,0) coordinate in QuickDraw is located in the upper-left corner. Quartz 2D has its (0,0) coordinate in the lower-left corner. If you are drawing into a Quartz context, you need to transform the QuickDraw coordinates accordingly.
If you want to handle drawing glyphs that use quadratic curves, you call the function ATSUGlyphGetQuadraticPaths. This function obtains the glyph segments for a glyph and then calls your callback functions for drawing the glyph. You must supply these universal procedure pointers (UPPs) to the function ATSUGlyphGetQuadraticPaths][]. This function obtains the glyph segments for a glyph and then calls your callback functions for drawing the glyph. You must supply these universal procedure pointers (UPPs) to the function [ATSUGlyphGetQuadraticPaths:
ATSQuadraticNewPathUPP, a pointer to your callback to handle the new-path operation
ATSQuadraticLineUPP, a pointer to your callback to handle the line-to operation
ATSQuadraticCurveUPP, a pointer to your callback to handle the curve-to operation
ATSQuadraticClosePathUPP, a pointer to your callback to handle the close-path operation
Similarly, if you want to handle drawing glyphs that use cubic curves, you call the function ATSUGlyphGetCubicPaths. This function obtains the glyph segments for a glyph and then calls your callback functions for drawing the glyph. You must supply these UPPs to the function ATSUGlyphGetCubicPaths:
ATSCubicMoveToUPP, a pointer to your callback to handle the move-to operation
ATSCubicLineToUPP, a pointer to your callback to handle the line-to operation
ATSCubicCurveToUPP, a pointer to your callback to handle the curve-to operation
ATSCubicClosePathUPP, a pointer to your callback to handle the close-path operation
Note that for cubic paths, the starting position for each curve or line is implicit from the current pen position. The start of a path is also implicit and is signaled by the move to establish the initial pen position.
Listing 6-5 shows code that sets up the four quadratic curve callbacks that handle glyph drawing for glyphs whose curve type is quadratic. Listing 6-6 shows a function (MyDrawQuadratics) that creates universal procedure pointers to the callbacks and uses the function ATSUGlyphGetQuadraticPaths. A detailed explanation for each numbered line of code in a listing appears following each listing. The code for using cubic curve callbacks to handle glyph drawing is similar to that shown in Listing 6-5 and Listing 6-6, except that you would use the function ATSUGlyphGetCubicPaths and supply to it your cubic callbacks.
Listing 6-5 Setting up the quadratic curve callbacks
OSStatus MyQuadraticLineProc (const Float32Point *pt1, |
const Float32Point *pt2, |
void *callBackDataPtr)// 1 |
{ |
float x1 = ((MyCallbackData *)callBackDataPtr)->origin.x + pt1->x;// 2 |
float y1 = ((MyCallbackData *)callBackDataPtr)->origin.y + pt1->y; |
float x2 = ((MyCallbackData *)callBackDataPtr)->origin.x + pt2->x; |
float y2 = ((MyCallbackData *)callBackDataPtr)->origin.y + pt2->y; |
y1 = ((MyCallbackData *)callBackDataPtr)->windowHeight - y1;// 3 |
y2 = ((MyCallbackData *)callBackDataPtr)->windowHeight - y2; |
if ( ((MyCurveCallbackData *)callBackDataPtr)->first ) // 4 |
{ |
CGContextMoveToPoint (gContext, x1, y1); |
((MyCurveCallbackData *)callBackDataPtr)->first = false; |
} |
CGContextAddLineToPoint (context, x2, y2);// 5 |
return noErr;// 6 |
} |
OSStatus MyQuadraticCurveProc (const Float32Point *pt1, |
const Float32Point *controlPt, |
const Float32Point *pt2, |
void *callBackDataPtr)// 7 |
{ |
float x1 = ((MyCallbackData *)callBackDataPtr)->origin.x + pt1->x;// 8 |
float y1 = ((MyCallbackData *)callBackDataPtr)->origin.y + pt1->y; |
float x2 = ((MyCallbackData *)callBackDataPtr)->origin.x + pt2->x; |
float y2 = ((MyCallbackData *)callBackDataPtr)->origin.y + pt2->y; |
float cpx = ((MyCallbackData *)callBackDataPtr)->origin.x + |
controlPt->x; |
float cpy = ((MyCallbackData *)callBackDataPtr)->origin.y + |
controlPt->y; |
y1 = ((MyCallbackData *)callBackDataPtr)->windowHeight - y1;// 9 |
y2 = ((MyCallbackData *)callBackDataPtr)->windowHeight - y2; |
cpy = ((MyCallbackData *)callBackDataPtr)->windowHeight - cpy; |
if ( ((MyCurveCallbackData *)callBackDataPtr)->first ) // 10 |
{ |
CGContextMoveToPoint(gContext, x1, y1); |
((MyCurveCallbackData *)callBackDataPtr)->first = false; |
} |
CGContextAddQuadCurveToPoint (context, cpx, cpy, x2, y2);// 11 |
return noErr;// 12 |
} |
OSStatus MyQuadraticNewPathProc (void * callBackDataPtr)// 13 |
{ |
((MyCurveCallbackData *)callBackDataPtr)->first = true;// 14 |
return noErr;// 15 |
} |
OSStatus MyQuadraticClosePathProc (void * callBackDataPtr) |
{ |
((MyCurveCallbackData *)callBackDataPtr)->first = true;// 16 |
return noErr;// 17 |
} |
Here’s what the code does:
Sets up the parameters that are passed to your callback for drawing a line. ATSUI passes two points that define a line and a pointer to any data your callback needs. You pass the pointer to your callback data to ATSUI when you call the function ATSUGlyphGetQuadraticPaths. See Listing 6-6.
Modifies the coordinate values of the points passed in by ATSUI. The four lines of code here add values supplied by the callback data pointer. These values are spacing adjustments that take into account the window height. When you write your callback, you would modify the values appropriately for your application.
Transforms the y-coordinate values from QuickDraw coordinates to Quartz coordinates. If you are using a Quartz context, you must perform this transformation because ATSUI always passes coordinates as QuickDraw coordinates.
Checks to see if this is the first point in the curve. If it is, calls the Quartz 2D function CGContextMoveToPoint to begin drawing at the specified coordinates.
Calls the Quartz 2D function CGContextAddLineToPoint to draw a straight line segment from the current point to the specified point.
Returns a result code that indicates whether or not your callback executed successfully. If your callback returns any value other than 0, the function ATSGlyphGetQuadraticPaths stops parsing the path outline and returns the result kATSOutlineParseAbortedErr.
Sets up the parameters that are passed to your callback for drawing a curve. ATSUI passes the two end points and a control point that define the curve along with a pointer to any data your callback needs. You pass the pointer to your callback data to ATSUI when you call the function ATSUGlyphGetQuadraticPaths. See Listing 6-6.
Modifies the coordinate values of the end points and control point passed in by ATSUI. The six lines of code here add origin values supplied by the callback data pointer. When you write your callback, you would modify the values of the end points and control point appropriately for your application.
Transforms the y-coordinate values from QuickDraw coordinates to Quartz coordinates. If you are using a Quartz context, you must perform this transformation because ATSUI always passes coordinates as QuickDraw coordinates.
Checks to see if this is the first point in the curve. If it is, calls the Quartz 2D function CGContextMoveToPoint to begin drawing at the specified location.
Calls the Quartz 2D function CGContextAddQuadCurveToPoint to draw a quadratic Bézier curve from the current point, using the control point and end point you specify.
Returns a result code that indicates whether or not your callback executed successfully. If your callback returns any value other than 0, the function ATSGlyphGetQuadraticPaths stops parsing the path outline and returns the result kATSOutlineParseAbortedErr.
Sets up the parameter that is passed to your callback for establishing a new path. ATSUI passes a pointer to any data your callback needs. You pass the pointer to your callback data to ATSUI when you call the function ATSUGlyphGetQuadraticPaths. See Listing 6-6.
Sets the flag that indicates the beginning of a curve segment to true. This flag is used by the functions MyQuadraticlineProc and MyQuadraticCurveProc.
Returns a result code that indicates whether or not your callback executed successfully. If your callback returns any value other than 0, the function ATSGlyphGetQuadraticPaths stops parsing the path outline and returns the result kATSOutlineParseAbortedErr.
Sets the flag that indicates the beginning of a curve segment to true. This flag is used by the functions MyQuadraticlineProc and MyQuadraticCurveProc.
Returns a result code that indicates whether or not your callback executed successfully. If your callback returns any value other than 0, the function ATSGlyphGetQuadraticPaths stops parsing the path outline and returns the result kATSOutlineParseAbortedErr.
After you have written callbacks to handle drawing operations for a quadratic curve (as shown in Listing 6-5), you need to write a function that creates universal procedure pointers (UPPs) for each callback and then pass the UPPs to the function ATSUGlyphGetQuadraticPaths. Listing 6-6 shows a function that sets up UPPs, obtains glyph data, and calls the function ATSUGlyphGetQuadraticPaths to draw each glyph in a text run. A detailed explanation for each numbered line of code appears following the listing.
Listing 6-6 A function that draws glyph outlines using quadratic curve data
void MyDrawQuadratics (ATSUTextLayout iLayout, |
ATSUStyle iStyle, |
UniCharArrayOffset start, |
UniCharCount length, |
Fixed penX, |
Fixed penY, |
float windowHeight)// 1 |
{ |
ATSLayoutRecord *layoutRecords; |
ItemCount numRecords; |
Fixed *deltaYs; |
ItemCount numDeltaYs; |
ATSQuadraticNewPathUPP newPathProc; |
ATSQuadraticLineUPP lineProc; |
ATSQuadraticCurveUPP curveProc; |
ATSQuadraticClosePathUPP closePathProc; |
MyCallbackData data; |
OSStatus status; |
int i; |
newPathProc = NewATSQuadraticNewPathUPP (MyQuadraticNewPathProc);// 2 |
lineProc = NewATSQuadraticLineUPP (MyQuadraticLineProc);// 3 |
curveProc = NewATSQuadraticCurveUPP (MyQuadraticCurveProc);// 4 |
closePathProc = NewATSQuadraticClosePathUPP (MyQuadraticClosePathProc);// 5 |
ATSUDirectGetLayoutDataArrayPtrFromTextLayout (iLayout, |
start, |
kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, |
(void *) &layoutRecords, |
&numRecords) ;// 6 |
ATSUDirectGetLayoutDataArrayPtrFromTextLayout (iLayout, |
start, |
kATSUDirectDataBaselineDeltaFixedArray, |
(void *) &deltaYs, |
&numDeltaYs);// 7 |
CGContextBeginPath (gContext);// 8 |
data.windowHeight = windowHeight;// 9 |
for (i=0; i < numRecords; i++) // 10 |
{ |
data.origin.x = Fix2X(penX) + |
Fix2X(layoutRecords[i].realPos);// 11 |
if (deltaYs == NULL) // 12 |
data.origin.y = Fix2X(penY); |
else |
data.origin.y = Fix2X(penY) - Fix2X(deltaYs[i]); |
data.first = true;// 13 |
if (layoutRecords[i].glyphID != kATSDeletedGlyphcode)// 14 |
{ |
ATSUGlyphGetQuadraticPaths (iStyle, |
layoutRecords[i].glyphID, |
newPathProc, |
lineProc, |
curveProc, |
closPathProc, |
&data, |
&status); |
} |
} |
CGContextClosePath(gContext);// 15 |
CGContextDrawPath(gContext, kCGPathStroke);// 16 |
if (deltaYs != NULL) // 17 |
ATSUDirectReleaseLayoutDataArrayPtr (NULL, |
kATSUDirectDataBaselineDeltaFixedArray, |
(void *) &deltaYs); |
ATSUDirectReleaseLayoutDataArrayPtr (NULL, |
kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, |
(void *) &layoutRecords);// 18 |
DisposeATSQuadraticNewPathUPP (newPathProc);// 19 |
DisposeATSQuadraticLineUPP (lineProc); |
DisposeATSQuadraticCurveUPP (curveProc); |
DisposeATSQuadraticClosePathUPP (closePathProc); |
} |
Here’s what the code does:
Sets up the parameters needed by this function:
a valid text layout
the style object associated with the text layout
the starting offset for the text run that will be processed by this function
the length of the text run that will be processed by this function
the x-coordinate for the pen’s starting location
the y-coordinate for the pen’s starting location
the height of the window into which the text will be drawn
Calls the function NewATSQuadracticNewPathUPP to create a UPP for the MyQuadraticNewPathProc callback created in Listing 6-5.
Calls the function NewATSQuadracticLineUPP to create a UPP for the MyQuadraticLineProc callback created in Listing 6-5.
Calls the function NewATSQuadracticCurveUPPto create a UPP for the MyQuadraticCurveProc callback created in Listing 6-5.
Calls the function NewATSQuadracticClosePathUPP to create a UPP for the MyQuadraticClosePathProc callback created in Listing 6-5.
Calls the function ATSUDirectGetLayoutDataArrayPtrFromTextLayout to obtain the glyph IDs and the real position for the glyphs associated with the text layout.
Calls the function ATSUDirectGetLayoutDataArrayPtrFromTextLayout to obtain the baseline delta values (if any) for the glyphs associated with the text layout.
Calls the Quartz 2D function CGContextBeginPath to begin a path for the glyph outlines. A Quartz graphics context can have only a single path in use at any time. If the context already contains a path when you call CGContextBeginPath, Quartz replaces the previous current path with the new path, discarding the old path and any data associated with it.
Assigns the window height to the data structure that will be passed to the callbacks. This is used to transform the y-coordinate from QuickDraw coordinate space to Quartz coordinate space.
Begins a loop over all the glyphs in the text run.
Assigns an adjusted x-coordinate to the data structure that will be passed to the callbacks. The x-coordinate is the position for the beginning of the line added to the real position.
Checks to see if the baseline delta array is NULL. If the array is NULL, the y-coordinate is the vertical position of the line; otherwise, the y-coordinate is the vertical position of the line added to the baseline delta value.
Sets the flag that indicates the start of a curve segment to true. This flag is used by the callback functions MyQuadraticlineProc and MyQuadraticCurveProc. See Listing 6-5.
Checks whether the glyph ID is not that of a deleted glyph. Deleted glyphs should not be drawn. If the glyph should be drawn, calls the function ATSUGlyphGetQuadracticPaths to draw the glyphs using the callback functions specified by the UPPs passed to ATSUGlyphGetQuadracticPaths. This function also takes as parameters the style object associated with the text run, the glyph ID, a pointer to the data structure that will be passed to the callbacks, and a pointer to a status value. The status value is used to indicate the status of your callback functions. When a callback function returns any value other than 0, the ATSGlyphsGetQuadraticPaths function stops parsing the path outline and returns the result kATSOutlineParseAbortedErr.
Calls the Quartz 2D function CGContextClosePath to close and terminate the current path.
Calls the Quartz 2D function CGContextDrawPath to paint a line along the current path.
Checks to see if the baseline delta array is NULL. If it is not, calls the function ATSUDirectReleaseLayoutDataArrayPtr to release the baseline delta array.
Calls the function ATSUDirectReleaseLayoutDataArrayPtr to release the layout record array.
Calls the appropriate ATSUI functions to dispose of the four UPPs created previously.
Last updated: 2007-07-10