Retired Document
Important: ATSUI is a legacy technology in Mac OS X v10.6 and later. Please use Core Text, described in Core Text Programming Guide, instead.
Direct-Access Tasks: Working With Glyph Data
ATSUI provides a set of functions that enable you to access information about glyphs during the layout process. These functions are called direct-access functions because they allow you to manipulate glyph data directly. You can use direct-access functions to control many aspects of ATSUI’s internal layout process and, as a result, control how ATSUI draws text for your application. For example, you can override one or more steps of the ATSUI layout process, exercise fine control over layout metrics, specify glyph replacements, and perform post-layout processing.
You can call ATSUI direct-access functions along with functions you’ve provided in your text layout engine. Mixing your functions with ATSUI’s reduces the processing ATSUI does by default and enables you to perform your own layout adjustments.
This chapter shows you how to use ATSUI’s direct-access functions in the following sections:
Overriding ATSUI Layout Operations shows how to write and install callbacks that obtain and modify glyph positioning information.
Retrieving and Drawing Glyph Outlines provides information on obtaining the curves that make up a glyph shape and using your own functions to draw them.
Before you read this chapter you should be familiar with the text measurement terms discussed in Chapter 2, Typography Concepts and know how to perform the tasks discussed in Chapter 4, Basic Tasks: Working With Objects and Drawing Text.
This chapter provides a number of code samples to illustrate how to use direct-access functions. You can obtain the sample applications from which this code is taken, as well as additional code samples, from the developer sample code website:
http://developer.apple.com/samplecode/
Overriding ATSUI Layout Operations
You can override ATSUI’s layout operations by modifying layout information for the glyphs associated with a text layout object. You can adjust such values as the glyph’s baseline delta, advance delta, and real position values. You can also specify a post-layout operation (such as glyph substitution) that modifies a line after ATSUI has completed its layout operations. Table 6-1 defines some of the values you can modify using direct-access functions.
Value | Definition |
---|---|
The actual drawing position on the x-axis. This position does not include the device delta. | |
The distance between the end of one glyph’s advance and the next glyph’s real position. | |
The distance between the actual drawing position on the y-axis and the baseline position. | |
A value used to adjust truncated factional values for cases in which fractional positioning can’t be used. For example, to compensate for integer drawing in QuickDraw. Device delta values are usually used when anti-aliasing is turned off. However, these values can be used when anti-aliasing is on to assure that the glyphs in a connected script (such as one that used the Zapfino font) are connected smoothly. |
The three lines of text in Figure 6-1 show an example of what you can do using the ATSUI direct-access functions. The first line is drawn as ATSUI would normally draw the text. The next two lines were drawn by ATSUI after glyph values were modified through the use of direct-access functions. The advance delta values of the text in the second line have been modified by decreasing the advance to create a condensed text appearance. Whereas the advance delta values of the text in the third line have been increased from normal to create an extended text appearance. To achieve each effect, the advance delta values are modified by a callback, then ATSUI uses the modified values during the course of its normal layout and drawing processes.
Figure 6-2 also shows an example of using ATSUI direct-access functions, but in this case the layout was modified after ATSUI already performed its normal layout process. The space characters in the text are replaced with a specified replacement character through a callback that is applied post-layout.
The text in Figure 6-3 is one line of text. The baseline delta values for the glyphs are adjusted such that every other glyph has a positive baseline delta value and alternate glyphs have a negative baseline delta value.
As you can see from Figure 6-1, Figure 6-2, and Figure 6-3, you can achieve a number of effects using the ATSUI direct-access functions. Overriding ATSUI layout operations requires that you perform these tasks:
Write a callback that obtains glyph data from ATSUI and modifies the glyph data associated with a text layout object.
Install the callback on a text layout object.
Use ATSUI as you normally would to create text layout objects, draw text, get glyph bounds, and so forth. ATSUI invokes your callback each time it lays out a line of text.
The callback definition for a direct-access callback function is as follows:
OSStatus (* ATSUDirectLayoutOperationOverrideProcPtr)( |
ATSULayoutOperationSelector iCurrentOperation, |
ATSULineRef iLineRef, |
UInt32 iRefCon, |
void *iOperationCallbackParameterPtr, |
ATSULayoutOperationCallbackStatus *oCallbackStatus); |
These are the parameters:
iCurrentOperation
, the operation that triggered the callback. This value is passed to your callback by ATSUI. If you write a callback that handles more than one layout operation, you can use this value to determine which operation you should handle.iLineRef
, a reference to the current line. This is the line of text on which your callback operates. Your callback gets called for each line of text associated with the text layout object on which you installed the callback.iRefCon
, a value set by calling the functionATSUSetTextLayoutRefCon
. This is optional, and can be any data you need to track for your application, such as user preference data related to layout operations.iOperationCallbackParameterPtr
. Currently unused and is set toNULL
.oCallbackStatus
. On output, you must supply a status value to indicate to ATSUI whether your callback handled the operation (kATSULayoutOperationCallbackStatusHandled
) or whether ATSUI needs to handle the operation (kATSULayoutOperationCallbackStatusContinue
). If you return the resultkATSULayoutOperationCallbackStatusContinue
, ATSUI may overwrite any settings that you have modified for that process.
Within the body of your callback function you need to call one of the ATSUI direct-access functions, such as ATSUDirectGetLayoutDataArrayPtrFromLineRef
, to obtain the glyph data you want to modify. You use the data selectors shown in Table 6-2 to specify which data you want to obtain. The selectors are nonexclusive; you can use the logical-Or operator to combine several data selectors if your callback handles multiple operations.
Selector | Obtains |
---|---|
The advance delta array. | |
The baseline delta array. | |
The device delta array. | |
The style-index array. The values in the array are indices to the style setting reference array. | |
The style setting array. | |
The |
To install a callback, you follow the same procedure as you would to set a layout attribute. You must do the following:
Set up a triple (tag, size, value) to specify the operation you want to override and the callback function you are providing to handle the operation.
In this case, the attribute tag is a structure (
ATSULayoutOperationOverrideSpecifier
) that specifies a selector for a layout operation and a pointer to a callback function. Typical selectors are listed in Table 6-3. See Inside Mac OS X: ATSUI Reference for a complete list.Call the function
ATSUSetLayoutControls
to associate the triple with the text layout object whose layout operation you want to override.
Selector | Specifies |
---|---|
No layout select operation selected. | |
Justification. | |
Character morphing. | |
Kerning adjustment. | |
Baseline adjustment; layout above or below current baseline. | |
Tracking adjustment. | |
Applies a layout operation after ATSUI has finished its layout operations. |
After you’ve installed the callback, you use ATSUI as you normally would to draw text. ATSUI triggers your callback each time the layout operation you specified is invoked.
You’ll see specific examples of how to write and install callbacks that manipulate the final layout in the following sections:
Extending the Space Between Glyphs. This example shows how to obtain the array of advance delta values for a line of text, and then to modify advance values to extend the space between the glyphs.
Positioning Glyphs Along a Curve. This example shows how to retrieve ATS layout records, advance delta arrays, and baseline delta arrays, and then to use real position information to calculate advance and baseline delta values that achieve the desired layout.
Before you override any of ATSUI’s layout operations, you should read the guidelines outlined in the following section.
Guidelines for Overriding Layout Operations
Follow these guidelines when you override ATSUI layout operations:
You should modify advance delta values during ATSUI’s layout process and not as a post layout operation. ATSUI uses advance delta values when it calculates the real position. The real position is available only post layout. So adjusting advance delta values post layout won’t have an effect.
You should modify the real position only at post layout.
Ideally, if you modify the real position, you should update the advance delta values to reflect the real-position modifications.
You should modify baseline delta values as a post-layout operation (
kATSULayoutOperationPostLayoutAdjustment
) or in the baseline operation (kATSULayoutOperationBaselineAdjustment
). If you modify these values during any other operation, ATSUI’s baseline operation can overwrite the values you modify.
Extending the Space Between Glyphs
You can extend the space between glyphs by providing values that adjust the positions of the glyph. To do so, you need to
inform ATSUI that you want to override the justification layout operation
obtain the advance delta array for the glyphs associated with the text layout object whose layout you want to modify
adjust advance delta values for each glyph whose layout you want to modify
Depending on the advance delta values you provide, you’ll get an effect similar to the bottom line shown in Figure 6-1. All glyphs except the first glyph in this line are drawn using the same advance delta value to achieve a uniform spacing. The advance delta values do not need to be the same for each glyph.
The following sections show how to extend the space between glyphs:
Writing a Callback to Modify Advance Delta Values
The callback shown in Listing 6-1 (MyStretchGlyphCallback
) performs one task—it modifies advance delta values. The effect is to extend the space between glyphs. A detailed explanation for each numbered line of code appears following the listing.
Listing 6-1 A callback that modifies advance delta values
static |
OSStatus MyStretchGlyphCallback ( |
ATSULayoutOperationSelector iCurrentOperation, |
ATSULineRef iLineRef, |
UInt32 iRefCon, |
void *iOperationExtraParameter, |
ATSULayoutOperationCallbackStatus *oCallbackStatus )// 1 |
{ |
OSStatus status; |
Fixed *myDeltaXArray;// 2 |
ItemCount myRecordArrayCount; |
ItemCount myItems; |
Fixed myStretchFactor; |
status = ATSUDirectGetLayoutDataArrayPtrFromLineRef (iLineRef, |
kATSUDirectDataAdvanceDeltaFixedArray, |
true, |
(void **) &myDeltaXArray, |
&myRecordArrayCount);// 3 |
require_noerr (status, StretchGlyphCallback_err); |
myStretchFactor = (Fixed) ((gFontSize*2) << 16);// 4 |
for (myItems = 1; myItems < myRecordArrayCount; myItems++ ) // 5 |
{ |
myDeltaXArray[myItems] += myStretchFactor; |
}; |
status = ATSUDirectReleaseLayoutDataArrayPtr (iLineRef, |
kATSUDirectDataAdvanceDeltaFixedArray, |
(void **) &myDeltaXArray );// 6 |
StretchGlyphCallback_err: |
*oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;// 7 |
return status; |
} |
Here’s what the code does:
Provides the parameters specified by the callback definition. As you’ll see, this callback uses only two of the parameters:
iLineRef
andoCallbackStatus
.Declares an array for the advance delta values.
Calls the function
ATSUDirectGetLayoutDataArrayPtrFromLineRef
to obtain the advance delta values for the line specified byiLineRef
. This function returns the data pointer specified by theiDataSelector
parameter (in this case,kATSUDirectDataAdvanceDeltaFixedArray
) for the line specified byiLineRef
.You should pass
true
for theiCreate
parameter. If the requested array isn’t referenced byiLineRef
, ATSUI creates and returns a zero-filled array. If the requested array has already been created, ATSUI returns the array with the current advance delta values. On output,myRecordArrayCount
specifies the number of items in the array.Sets a stretch-factor value, based on the current font size, to be used for the advance delta.
Iterates through the advance delta array, assigning a value to each element in the array.
Releases the data pointer (
myDeltaXArray
) obtained by calling the functionATSUDirectGetLayoutDataArrayPtrFromLineRef
. You must release this data pointer by calling the functionATSUDirectReleaseLayoutDataArrayPtr
. Releasing the array signals ATSUI that you are done with the data and that ATSUI can merge values you set with those set in the font.Returns the status
kATSULayoutOperationCallbackStatusHandled
to indicate the layout operation is handled successfully.
Installing a Callback for a Justification Override Operation
The MyInstallStretchGlyphCallback
function in A function that installs a justification override callback sets up ATSUI to call MyStretchGlyphCallback
each time a justification operation needs to be performed on the text associated with the specified text layout object. A detailed explanation for each numbered line of code appears following the listing.
Listing 6-2 A function that installs a justification override callback
OSStatus MyInstallStretchGlyphCallback (ATSUTextLayout myTextLayout) |
{ |
OSStatus status = noErr; |
ATSULayoutOperationOverrideSpecifier myOverrideSpec;// 1 |
ATSUAttributeTag theTag; |
ByteCount theSize; |
ATSUAttributeValuePtr theValue; |
myOverrideSpec.operationSelector = |
kATSULayoutOperationJustification;// 2 |
myOverrideSpec.overrideUPP = MyStretchGlyphCallback; |
theTag = kATSULayoutOperationOverrideTag;// 3 |
theSize = sizeof (ATSULayoutOperationOverrideSpecifier); |
theValue = &myOverrideSpec; |
status = ATSUSetLayoutControls (myTextLayout, 1, |
&theTag, &theSize, &theValue);// 4 |
require_noerr (status, InstallStrechGlyphCallback_err ); |
InstallStrechGlyphCallback_err: |
return status; |
} |
Here’s what the code does:
Declares an override specification. This structure contains a selector for a layout operation and a universal procedure pointer to the callback you supply.
Assigns the justification selector as the operation selector and the
MyStretchGlyphCallback
callback as the callback to handle justification.Sets up a triple (tag, size, value) for the layout attribute. In this case, the layout attribute is the override specification.
Calls the function
ATSUSetLayoutControls
to associate the override specification with the text layout object.
Positioning Glyphs Along a Curve
The text in Figure 6-4 shows glyphs whose baseline delta values are adjusted to track a sine curve. To achieve the smoothest look possible, the baseline delta values are calculated using the real position of the glyphs. In addition, the advance width for each glyph is adjusted.
To position glyphs along a curve, you need to do the following:
Inform ATSUI that you want to provide a post-layout operation.
Obtain the ATS layout record, advance delta array, and the baseline delta array for the glyphs associated with the text layout object whose layout you want to modify. The ATS layout record contains the real position of the glyph.
Adjust the advance delta and baseline delta values for each glyph such that the values track the specified curve.
The next sections discuss the specific tasks you need to perform to position glyphs along a curve:
Writing the Callback to Modify Baseline Delta Values
The callback (MySineCurveGlyphCallback
) shown in Listing 6-3 modifies advance and delta values so that glyphs are positioned along a sine curve. A detailed explanation for each numbered line of code appears following the listing.
Listing 6-3 A callback that positions glyphs along a sine curve
static |
OSStatus MySineCurveGlyphCallback( |
ATSULayoutOperationSelector iCurrentOperation, |
ATSULineRef iLineRef, |
UInt32 iRefCon, |
void *iOperationExtraParameter, |
ATSULayoutOperationCallbackStatus *oCallbackStatus ) |
{ |
OSStatus status = noErr; |
Fixed *deltaYArray; |
Fixed *deltaXArray; |
Fixed positionDifference; |
ATSLayoutRecord *layoutRecordArray; |
ItemCount recordArrayCount; |
ItemCount i; |
Fixed scaleFactor = 0; |
float amplitude; |
status = ATSUDirectGetLayoutDataArrayPtrFromLineRef (iLineRef, |
kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, |
false, |
(void **) &layoutRecordArray, |
&recordArrayCount );// 1 |
require_noerr (status, GlyphWaveCallback_err); |
status = ATSUDirectGetLayoutDataArrayPtrFromLineRef (iLineRef, |
kATSUDirectDataAdvanceDeltaFixedArray, |
true, |
(void **) &deltaXArray, |
&recordArrayCount );// 2 |
require_noerr (status, GlyphWaveCallbackDeltaXArray_err); |
status = ATSUDirectGetLayoutDataArrayPtrFromLineRef (iLineRef, |
kATSUDirectDataBaselineDeltaFixedArray, |
true, |
(void **) &deltaYArray, |
&recordArrayCount );// 3 |
require_noerr (status, SineCurveGlyphCallbackDeltaYArray_err); |
for ( i = 1; i < recordArrayCount; i++ )// 4 |
{ |
positionDifference = (layoutRecordArray[i].realPos - |
layoutRecordArray[i-1].realPos); |
if (positionDifference > scaleFactor) |
scaleFactor = positionDifference; |
} |
amplitude = FixedToFloat( scaleFactor );// 5 |
for ( i = 1; i < recordArrayCount - 1; i++ )// 6 |
{ |
positionDifference = scaleFactor - (layoutRecordArray[i].realPos - |
layoutRecordArray[i-1].realPos);// 7 |
layoutRecordArray[i].realPos += positionDifference;// 8 |
deltaXArray[i-1] += positionDifference;// 9 |
deltaYArray[i] = FloatToFixed (sinf ( i ) * amplitude);// 10 |
}; |
ATSUDirectReleaseLayoutDataArrayPtr (iLineRef, |
kATSUDirectDataBaselineDeltaFixedArray, |
(void **) &deltaYArray );// 11 |
SineCurveGlyphCallbackDeltaYArray_err: |
ATSUDirectReleaseLayoutDataArrayPtr (iLineRef, |
kATSUDirectDataAdvanceDeltaFixedArray, |
(void **) &deltaXArray );// 12 |
SineCurveGlyphCallbackDeltaXArray_err: |
ATSUDirectReleaseLayoutDataArrayPtr (iLineRef, |
kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, |
(void **) &layoutRecordArray );// 13 |
SineCurveGlyphCallback_err: |
*oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;// 14 |
return status; |
} |
Here’s what the code does:
Calls the function
ATSUDirectGetLayoutDataArrayPtrFromLineRef
with the layout record data selector to obtain the ATS layout records for each glyph on the line. A layout record contains the glyph real position. Calculating a sine curve based on the real positions of the glyphs results in a much smoother appearance.Calls the function
ATSUDirectGetLayoutDataArrayPtrFromLineRef
with the advance delta data selector to obtain the advance delta array for the glyphs on the line.Calls the function
ATSUDirectGetLayoutDataArrayPtrFromLineRef
with the baseline delta data selector to obtain the baseline delta array for the glyphs on the line.Iterates through the layout records to find the largest positional difference in the line. This difference will be used to set a scaling factor.
Sets the amplitude for the sine curve calculation to the scale factor.
Iterates through the glyph information to set the advance and baseline delta factors, taking into account the real position of the glyph.
Calculates a positional difference. This will be used to impose a fixed width on each glyph that takes the glyph’s point size into account.
Adds the positional difference to the real position value for the layout record array.
Adds the positional difference to the advance delta value.
Calculates the baseline delta using the previously calculated amplitude value and calling the function
sinf
.Releases the baseline delta array by calling the function
ATSUDirectReleaseLayoutDataArrayPtr
with the baseline delta data selector.Releases the advance delta array by calling the function
ATSUDirectReleaseLayoutDataArrayPtr
with the advance delta data selector.Releases the layout record array by calling the function
ATSUDirectReleaseLayoutDataArrayPtr
with the ATS layout record data selector.Returns a status value to ATSUI to indicate the layout operation is handled successfully.
Installing a Callback for a Post-Layout Operation
The MyInstallSineCuveGlyphCallback
function in Listing 6-4 sets up ATSUI to call MySineCuveGlyphCallback
. ATSUI triggers the callback for each line of text associated with the specified text layout object. Because the callback does post-layout processing, ATSUI invokes the callback at the end of its layout operations for the line. A detailed explanation for each numbered line of code appears following the listing.
Listing 6-4 Installing a callback for a post-layout override operation
OSStatus MyInstallSineCurveGlyphCallback (ATSUTextLayout textLayout ) |
{ |
OSStatus status = noErr; |
ATSULayoutOperationOverrideSpecifier myOverrideSpec;// 1 |
ATSUAttributeTag theTag; |
ByteCount theSize; |
ATSUAttributeValuePtr thePtr; |
myOverrideSpec.operationSelector = |
kATSULayoutOperationPostLayoutAdjustment;// 2 |
myOverrideSpec.overrideUPP = MySineCurveGlyphCallback; |
attrTag = kATSULayoutOperationOverrideTag;// 3 |
attrSize = sizeof (ATSULayoutOperationOverrideSpecifier); |
attrPtr = &myOverrideSpec; |
status = ATSUSetLayoutControls (textLayout, 1, |
&theTag, &theSize, &thePtr );// 4 |
require_noerr (status, InstallSineCureveGlyphCallback_err ); |
InstallSineCureveGlyphCallback_err: |
return status; |
} |
Here’s what the code does:
Declares an override specification. This structure contains a selector for a layout operation and a universal procedure pointer to the callback you supply.
Assigns the post-layout selector as the operation selector and the
MySineCurveGlyphCallback
callback as the callback to handle justification.Sets up a triple (tag, size, value) for the layout attribute. In this case, the layout attribute is the override specification.
Calls the function
ATSUSetLayoutControls
to associate the override specification with the text layout object.
Retrieving and Drawing Glyph Outlines
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 operationATSQuadraticLineUPP
, a pointer to your callback to handle the line-to operationATSQuadraticCurveUPP
, a pointer to your callback to handle the curve-to operationATSQuadraticClosePathUPP
, 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 operationATSCubicLineToUPP
, a pointer to your callback to handle the line-to operationATSCubicCurveToUPP
, a pointer to your callback to handle the curve-to operationATSCubicClosePathUPP
, 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 functionATSGlyphGetQuadraticPaths
stops parsing the path outline and returns the resultkATSOutlineParseAbortedErr
.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 functionATSGlyphGetQuadraticPaths
stops parsing the path outline and returns the resultkATSOutlineParseAbortedErr
.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 functionsMyQuadraticlineProc
andMyQuadraticCurveProc
.Returns a result code that indicates whether or not your callback executed successfully. If your callback returns any value other than
0
, the functionATSGlyphGetQuadraticPaths
stops parsing the path outline and returns the resultkATSOutlineParseAbortedErr
.Sets the flag that indicates the beginning of a curve segment to
true
. This flag is used by the functionsMyQuadraticlineProc
andMyQuadraticCurveProc
.Returns a result code that indicates whether or not your callback executed successfully. If your callback returns any value other than
0
, the functionATSGlyphGetQuadraticPaths
stops parsing the path outline and returns the resultkATSOutlineParseAbortedErr
.
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 theMyQuadraticNewPathProc
callback created in Listing 6-5.Calls the function
NewATSQuadracticLineUPP
to create a UPP for theMyQuadraticLineProc
callback created in Listing 6-5.Calls the function
NewATSQuadracticCurveUPP
to create a UPP for theMyQuadraticCurveProc
callback created in Listing 6-5.Calls the function
NewATSQuadracticClosePathUPP
to create a UPP for theMyQuadraticClosePathProc
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 callCGContextBeginPath
, 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 isNULL
, 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 functionsMyQuadraticlineProc
andMyQuadraticCurveProc
. 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 toATSUGlyphGetQuadracticPaths
. 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 than0
, theATSGlyphsGetQuadraticPaths
function stops parsing the path outline and returns the resultkATSOutlineParseAbortedErr
.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 functionATSUDirectReleaseLayoutDataArrayPtr
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.
Copyright © 2002, 2008 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2008-09-30