Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Source/AppDrawing.c
/* |
File: AppDrawing.c |
Contains: Drawing code for our sample document |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Copyright © 1999-2001 Apple Computer, Inc., All Rights Reserved |
*/ |
#include "AppDrawing.h" |
#include "UIHandling.h" |
/**** Macros and Defines ****/ |
#define kNumDocumentPages 1 |
#define PI 3.14159265358979323846 /* pi */ |
#define kOurPDF CFSTR("Kitty.pdf") |
/* |
these macros convert from QuickDraw coordinates to CoreGraphics (Quartz coordinates) |
by relying on fact that the port height (portBounds.bottom - portBounds.top) |
connects the two coordinate systems |
*/ |
#define QDxToCGx(portBounds, qdX) ( (qdX) ) |
#define QDyToCGy(portBounds, qdY) ( ((portBounds).bottom - (portBounds).top ) - (qdY) ) |
/********** our data types ***********/ |
typedef struct DataToDraw{ |
PMPageFormat pageFormat; // the page format for this document |
PMPrintSettings printSettings; // the print settings for a given print job |
CGPDFDocumentRef pdfDoc; // our PDF document to draw |
CGRect cgRect; // the CGRect for page 1 of our PDF document |
}DataToDraw, *DataToDrawPtr; |
/**** our private prototypes ***/ |
static PageDrawProc MyPageDrawProc; |
static OSStatus RenderCFString(CGContextRef ctx, CFStringRef theString, ATSUStyle theStyle, short h, short v); |
static OSStatus MakeATSUIStyle(short fontFamily, Style fontStyle, short fontSize, ATSUStyle *theStyle); |
static OSStatus drawCircularText(CGContextRef context, short fontNum); |
UInt32 MyGetDocumentNumPagesInDoc(const void *ourDataP) |
{ |
#pragma unused (ourDataP) |
// more realistic code would use the document's page format to determine the number of pages |
// in our document |
return kNumDocumentPages; // we always have kNumDocumentPages page per document |
} |
PageDrawProc *GetMyDrawPageProc(const void *ourDataP) |
{ |
#pragma unused (ourDataP) |
return MyPageDrawProc; |
} |
void SetPageFormatOnPrivateData(void *ourDataP, PMPageFormat pageFormat) |
{ |
if(ourDataP) |
((DataToDrawPtr)ourDataP)->pageFormat = pageFormat; |
return; |
} |
void SetPrintSettingsOnPrivateData(void *ourDataP, PMPrintSettings printSettings) |
{ |
if(ourDataP) |
((DataToDrawPtr)ourDataP)->printSettings = printSettings; |
return; |
} |
PMPageFormat GetPageFormatFromPrivateData(const void *ourDataP) |
{ |
return ourDataP ? ((DataToDrawPtr)ourDataP)->pageFormat : NULL; |
} |
PMPrintSettings GetPrintSettingsFromPrivateData(const void *ourDataP) |
{ |
return ourDataP ? ((DataToDrawPtr)ourDataP)->printSettings : NULL; |
} |
OSStatus DisposeWindowPrivateData(void *ourDataP) |
{ |
DataToDrawPtr ourDataToDrawP = (DataToDrawPtr)ourDataP; |
if(ourDataToDrawP != NULL){ |
// release our page format and print settings if they exist |
if(ourDataToDrawP->pageFormat) |
(void)PMRelease(ourDataToDrawP->pageFormat); |
if(ourDataToDrawP->printSettings) |
(void)PMRelease(ourDataToDrawP->printSettings); |
// release our PDF document ref |
if(ourDataToDrawP->pdfDoc) |
CGPDFDocumentRelease(ourDataToDrawP->pdfDoc); |
free(ourDataToDrawP); // free the outer structure |
} |
return noErr; |
} |
OSStatus MakeNewDocument(void) |
{ |
OSStatus err = noErr; |
// calloc sets all our struct fields to 0 |
DataToDraw *ourDataP = (DataToDraw *)calloc(sizeof(DataToDraw), 1); |
if(ourDataP){ |
CFBundleRef mainBundle = CFBundleGetMainBundle(); |
if(mainBundle){ |
CFURLRef ourPDFurl = CFBundleCopyResourceURL(mainBundle, kOurPDF, NULL, NULL); |
if(ourPDFurl){ |
ourDataP->pdfDoc = CGPDFDocumentCreateWithURL(ourPDFurl); |
if(ourDataP->pdfDoc != NULL){ |
int pages = CGPDFDocumentGetNumberOfPages(ourDataP->pdfDoc); |
if(pages > 0){ |
// the art box specifies the bounds of our artwork. We are going to use page 1 only. |
ourDataP->cgRect = CGPDFDocumentGetArtBox(ourDataP->pdfDoc , 1); |
// make our destination rect origin at the Quartz 2d origin. |
ourDataP->cgRect.origin.x = ourDataP->cgRect.origin.y = 0.; |
}else{ |
CGPDFDocumentRelease(ourDataP->pdfDoc); |
ourDataP->pdfDoc = NULL; |
} |
}else |
err = kCantCreatePDFDocument; |
CFRelease(ourPDFurl); |
}else{ |
err = kCantGetPDFURL; |
} |
}else |
err = kCantGetMainBundle; |
}else |
err = memFullErr; |
if (err == noErr){ |
WindowRef qdWindow = MakeWindow(ourDataP); // this will be our Window |
if(qdWindow){ |
Rect bounds; |
SetPortWindowPort(qdWindow); |
GetWindowPortBounds(qdWindow, &bounds); |
ClipRect(&bounds); |
InvalWindowRect(qdWindow, &bounds); |
ShowWindow(qdWindow); |
}else |
err = kCantCreateWindow; |
} |
return err; |
} |
static OSStatus MyPageDrawProc(const void *refCon, const Rect *drawingRectP, UInt32 pageNumber) |
{ |
#pragma unused (drawingRectP, pageNumber) |
OSStatus err = noErr; |
CGContextRef context = NULL; |
CGrafPtr port; |
DataToDraw *ourDataP = (DataToDraw *)refCon; |
Rect portRect; |
SInt16 tempNum; |
char *regularBuffer = "\pThis text is supposed to draw.\r"; |
char *hiddenBuffer = "\pThis text SHOULD NOT draw!\r"; |
Point qdLinePoint1; |
Point qdLinePoint2; |
GetPort(&port); |
GetFNum("\pTimes",&tempNum); |
TextFont(tempNum); |
TextSize(20); |
GetPortBounds(port, &portRect); |
qdLinePoint1.h = 20; |
qdLinePoint1.v = 20; |
qdLinePoint2.h = 40; |
qdLinePoint2.v = 40; |
// draw the lines with QuickDraw |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
LineTo(qdLinePoint2.h, qdLinePoint2.v); |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
DrawString(regularBuffer); |
/* |
Use of QDBeginCGContext/QDEndCGContext is preferred to the use of |
CreateCGContextForPort since the use of QDBeginCGContext/QDEndCGContext |
is supported at during printing whereas the use of CreateCGContextForPort is |
only supported for drawing during non-printing situations. |
QDBeginCGContext/QDEndCGContext are only available on MacOS X v10.1 and later. QDBeginCGContext |
returns a CGContextRef corresponding to the port passed in. The graphics state is initialized |
to default values. The initial coordinate system of the context is at the lower left |
corner of the port passed in. |
Calling QDBeginCGContext on a port has the side effect of replacing the port |
bottlenecks with bottlenecks that do no imaging. Use of QuickDraw in the port |
when you can also draw to this context would fail during printing and so the |
behavior is implemented as described here. In a future version of MacOS X, |
QuickDraw drawing to a grafPort in this state will generate messages |
to the console. |
Calling QDEndCGContext gives up the CGContextRef and restores the QuickDraw |
bottlenecks on the port to their previous state. |
CGContextRelease should *NEVER* be called on a context obtained with |
QDBeginCGContext. Once an application calls QDEndCGContext on |
a context obtained with QDBeginCGContext, the application no longer |
has a valid reference to that context and should not draw to it. In order |
to again draw to a CGContextRef corresponding to a given port, again call |
QDBeginCGContext. |
Note that QDBeginCGContext cannot be called a second time without first |
calling QDEndCGContext. |
*/ |
err = QDBeginCGContext(port, &context); |
if(!err){ |
// draw the lines using Quartz |
// translate over so we draw to the right of the QD line drawn earlier |
CGContextTranslateCTM(context, qdLinePoint1.h, 0); |
// the macros QDxToCGx, QDyToCGy transform the QD coordinates to the Quartz 2d coord system |
CGContextMoveToPoint(context, QDxToCGx(portRect, qdLinePoint1.h), |
QDyToCGy(portRect, qdLinePoint1.v)); |
CGContextAddLineToPoint(context, QDxToCGx(portRect, qdLinePoint2.h), |
QDyToCGy(portRect, qdLinePoint2.v)); |
CGContextStrokePath(context); |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
DrawString(hiddenBuffer); // this will not image since QuickDraw drawing does not image |
// after QDBeginCGContext but before QDEndCGContext |
/* after QDEndCGContext, the context parameter is NULL and |
we can't do any further imaging with Quartz into the context |
corresponding to the port until we again call QDBeginCGContext. |
*/ |
err = QDEndCGContext(port, &context); |
if(err || context != NULL){ |
printf("got an error from QDEndCGContext\n"); |
} |
}else |
printf("got an error from QDBeginCGContext\n"); |
// draw more QD lines and text, translated down |
SetOrigin(-40, -40); |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
LineTo(qdLinePoint2.h, qdLinePoint2.v); |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
DrawString(regularBuffer); |
err = QDBeginCGContext(port, &context); |
if(!err){ |
/* |
QDBeginCGContext returns a context which has the initial gstate set to |
default values, NOT the values in place at the time the last QDEndCGContext |
was called. |
Also note that the CGContext and port origins are automatically synchronized after |
QDBeginCGContext, that is the Quartz 2d origin and QuickDraw port origin are such |
that CGy = (portBounds.bottom - portBounds.top) - QDy |
*/ |
CGContextTranslateCTM(context, qdLinePoint1.h, 0); |
CGContextMoveToPoint(context, QDxToCGx(portRect, qdLinePoint1.h), |
QDyToCGy(portRect, qdLinePoint1.v)); |
CGContextAddLineToPoint(context, QDxToCGx(portRect, qdLinePoint2.h), |
QDyToCGy(portRect, qdLinePoint2.v)); |
CGContextStrokePath(context); |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
DrawString(hiddenBuffer); // this will not image since QuickDraw drawing does not image |
// after QDBeginCGContext but before QDEndCGContext |
err = QDEndCGContext(port, &context); |
if(err || context != NULL){ |
printf("got an error from QDEndCGContext\n"); |
} |
}else |
printf("got an error from QDBeginCGContext\n"); |
// more lines and text, translated further down and over |
SetOrigin(-80, -80); |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
LineTo(qdLinePoint2.h, qdLinePoint2.v); |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
DrawString(regularBuffer); |
SetOrigin(0, 0); // reset our origin for illustrative purposes |
err = QDBeginCGContext(port, &context); |
if(!err){ |
SetOrigin(-80, -80); |
// at this point the QD port origin is out of sync with our CGContextRef origin. We must |
// call SyncCGContextOriginWithPort in order to maintain the "normal" relationship |
// between the two, i.e. where are macros QDxToCGx, QDyToCGy transform between the QD |
// coordinate system and the Quartz 2d coordinate system. |
(void)SyncCGContextOriginWithPort(context, port); // sync our CG context origin to the new port origin |
//CGContextSaveGState(context); // no need to save our gstate here since QDEndCGContext resets it |
// for this next drawing we translate the origin to the QD origin |
CGContextTranslateCTM(context, QDxToCGx(portRect, 0), QDyToCGy(portRect, 0)); |
// we 'flip the coordinate system' in Y so we have QD style coordinates |
CGContextScaleCTM(context, 1, -1); |
// translate over so our drawing is offset from the QD drawing |
CGContextTranslateCTM(context, qdLinePoint1.h, 0); |
// scale so that our drawing will be scaled by 1.5. The linewidth will also |
// be scaled |
CGContextScaleCTM(context, 1.5, 1.5); |
// this drawing will be scaled by 1.5. Note that our Quartz coordinate system is |
// the "QD coordinate system" so we don't need to transform our |
// coordinates |
CGContextMoveToPoint(context, qdLinePoint1.h, qdLinePoint1.v); |
CGContextAddLineToPoint(context, qdLinePoint2.h, qdLinePoint2.v); |
CGContextStrokePath(context); |
//CGContextRestoreGState(context); // no need to restore our gstate since QDEndCGContext resets it |
MoveTo(qdLinePoint1.h, qdLinePoint1.v); |
DrawString(hiddenBuffer); // this will not image since QuickDraw drawing does not image |
// after QDBeginCGContext but before QDEndCGContext |
err = QDEndCGContext(port, &context); |
if(err || context != NULL){ |
printf("got an error from QDEndCGContext\n"); |
} |
}else |
printf("got an error from QDBeginCGContext\n"); |
SetOrigin(-220, -280); |
err = QDBeginCGContext(port, &context); |
if(!err){ |
OSStatus tempErr; |
// CGContextSaveGState(context); // no need to save our gstate here since QDEndCGContext resets it |
CGContextTranslateCTM(context, 0, QDyToCGy(portRect, 0)); // translate to port origin |
err = drawCircularText(context, tempNum); // draw our pinwheel of text |
//CGContextRestoreGState(context); // no need to restore our gstate since QDEndCGContext resets it |
tempErr = QDEndCGContext(port, &context); |
if(tempErr || context != NULL) |
printf("got an error from QDEndCGContext\n"); |
}else |
printf("got an error from QDBeginCGContext\n"); |
SetOrigin(-420, -180); |
err = QDBeginCGContext(port, &context); |
if(!err){ |
OSStatus tempErr; |
/* |
This is static so that we only need to create it once since, for this |
piece of text, the style isn't changed. For this simple drawing we can benefit by |
keeping it around rather than creating the style anew each time. |
*/ |
static ATSUStyle theStyle = NULL; |
// CGContextSaveGState(context); // no need to save our gstate here since QDEndCGContext resets it |
CGContextTranslateCTM(context, 0, QDyToCGy(portRect, 0)); // translate to port origin |
/* |
Clip to our character! |
Note that we can clip to one run of characters at a given time |
and trying to clip to more than that will result in a zero clip. |
ATSUI rendering may break a given sequence of characters into more |
than one run. Here we are OK since there is only one character and |
therefore only one run |
*/ |
CGContextSetTextDrawingMode(context, kCGTextClip); |
if(!theStyle) // make the style if it doesn't already exist |
err = MakeATSUIStyle(tempNum, normal, 200, &theStyle); |
if(!err){ |
CFStringRef ourString = CFSTR("Q"); |
err = RenderCFString(context, ourString, theStyle, -70, -70); |
// we want to keep the style for the next time |
// our drawProc is called so we don't call |
// ATSUDisposeStyle(theStyle); |
}else |
printf("got an error from MakeATSUIStyle\n"); |
// set the pen mode to the fill the character shapes only |
CGContextSetTextDrawingMode(context, kCGTextFill); |
// draw our pinwheel of text, now clipped by the 'Q' character |
err = drawCircularText(context, tempNum); |
//CGContextRestoreGState(context); // no need to restore our gstate since QDEndCGContext resets it |
tempErr = QDEndCGContext(port, &context); |
if(tempErr || context != NULL) |
printf("got an error from QDEndCGContext\n"); |
}else |
printf("got an error from QDBeginCGContext\n"); |
SetOrigin(-220, -580); |
if(ourDataP->pdfDoc){ |
err = QDBeginCGContext(port, &context); |
if(!err){ |
// note that our clip is reset to the default, even though we clipped in |
// the last drawing between QDBeginCGContext/QDEndCGContext |
OSStatus tempErr; |
// again we make this static so we only need to create it once |
static ATSUStyle theStyle = NULL; |
// CGContextSaveGState(context); // no need to save our gstate here since QDEndCGContext resets it |
CGContextTranslateCTM(context, 0, QDyToCGy(portRect, 0)); // translate to port origin |
// draw our document's page 1 |
CGContextDrawPDFDocument(context, ourDataP->cgRect, ourDataP->pdfDoc, 1); |
// translate over before drawing again |
CGContextTranslateCTM(context, -ourDataP->cgRect.size.height, 0); |
// we're going to clip to a character |
CGContextSetTextDrawingMode(context, kCGTextClip); |
if(!theStyle) // make the style if it doesn't already exist |
err = MakeATSUIStyle(tempNum, normal, 150, &theStyle); |
if(!err){ |
CFStringRef ourString = CFSTR("Q"); |
err = RenderCFString(context, ourString, theStyle, 0, 40); // 100, 100 |
// we want to keep the style for the next time |
// our drawProc is called so we don't call |
// ATSUDisposeStyle(theStyle); |
}else |
printf("got an error from MakeATSUIStyle\n"); |
CGContextSetTextDrawingMode(context, kCGTextFill); |
CGContextTranslateCTM(context, ourDataP->cgRect.size.height, 0); |
// rotate by 90 degrees. Note that this routine takes its angle |
// argument in radians, not degrees |
CGContextRotateCTM(context, PI/2); |
// go ahead and draw page 1 again |
CGContextDrawPDFDocument(context, ourDataP->cgRect, ourDataP->pdfDoc, 1); |
//CGContextRestoreGState(context); // no need to restore our gstate since QDEndCGContext resets it |
tempErr = QDEndCGContext(port, &context); |
if(tempErr || context != NULL) |
printf("got an error from QDEndCGContext\n"); |
}else |
printf("got an error from QDBeginCGContext\n"); |
} |
// reset our origin in preparation for future drawing |
SetOrigin(0, 0); |
return err; |
} // MyPageDrawProc |
/* |
The approach used for ATSUI text is described in |
Technical Q&A QA1027 and the code here is based on the sample |
code shown there. |
http://developer.apple.com/qa/qa2001/qa1027.html |
*/ |
/* MakeATSUIStyle creates a simple ATSUI style record |
that based on the fontFamily ID, QD style and fontsize. That |
style record can be used in calls to the ATSUI text drawing routines. |
*/ |
static OSStatus MakeATSUIStyle(short fontFamily, Style fontStyle, short fontSize, ATSUStyle *theStyle) |
{ |
OSStatus err; |
ATSUStyle localStyle; |
ATSUFontID atsuFont; |
Fixed atsuSize; |
short atsuOrientation; |
Boolean trueVar = true, falseVar = false; |
/* Three parrallel arrays for setting up attributes. */ |
ATSUAttributeTag theTags[] = { |
kATSUFontTag, kATSUSizeTag, kATSUVerticalCharacterTag, |
kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag, |
kATSUQDCondensedTag, kATSUQDExtendedTag |
}; |
ByteCount theSizes[] = { |
sizeof(ATSUFontID), sizeof(Fixed), sizeof(UInt16), |
sizeof(Boolean), sizeof(Boolean), sizeof(Boolean), |
sizeof(Boolean), sizeof(Boolean) |
}; |
ATSUAttributeValuePtr theValues[] = { |
NULL, NULL, NULL, NULL, |
NULL, NULL, NULL, NULL |
}; |
/* set up locals */ |
localStyle = NULL; |
atsuFont = 0; |
atsuSize = FixRatio(fontSize, 1); |
atsuOrientation = kATSUStronglyHorizontal; |
/* or atsuOrientation = kATSUStronglyVertical */ |
/* set the values array to point to our locals */ |
theValues[0] = &atsuFont; |
theValues[1] = &atsuSize; |
theValues[2] = &atsuOrientation; |
theValues[3] = ((fontStyle & bold) != 0 ? &trueVar : &falseVar); |
theValues[4] = ((fontStyle & italic) != 0 ? &trueVar : &falseVar); |
theValues[5] = ((fontStyle & underline) != 0 ? &trueVar : &falseVar); |
theValues[6] = ((fontStyle & condense) != 0 ? &trueVar : &falseVar); |
theValues[7] = ((fontStyle & extend) != 0 ? &trueVar : &falseVar); |
err = ATSUFONDtoFontID( fontFamily, fontStyle, &atsuFont); |
if (err != noErr) goto bail; |
/* create a style */ |
err = ATSUCreateStyle(&localStyle); |
if (err != noErr) goto bail; |
/* set the style attributes */ |
err = ATSUSetAttributes( localStyle, |
sizeof(theTags)/sizeof(theTags[0]), |
theTags, theSizes, theValues ); |
if (err != noErr) goto bail; |
/* store the new style for the caller */ |
*theStyle = localStyle; |
return noErr; |
bail: |
if (localStyle != NULL) ATSUDisposeStyle(localStyle); |
return err; |
} |
/* RenderCFString renders a CFString at (h, v) in the |
supplied CGContextRef using ATSUI using the style specified in |
the atsui style record. */ |
static OSStatus RenderCFString(CGContextRef ctx, CFStringRef theString, |
ATSUStyle theStyle, short h, short v) |
{ |
ATSUTextLayout theLayout = NULL; |
CFIndex textLength; |
OSStatus err = noErr; |
UniChar *uniBuffer; |
CFRange uniRange; |
/* set up our locals, verify parameters... */ |
if (theString == NULL || theStyle == NULL) return paramErr; |
uniBuffer = NULL; |
textLength = CFStringGetLength(theString); |
if (textLength == 0) return noErr; |
/* get our data */ |
uniRange = CFRangeMake(0, textLength); |
uniBuffer = (UniChar *) NewPtr( textLength * sizeof(UniChar) ); |
if (uniBuffer == NULL) { err = memFullErr; goto bail; } |
CFStringGetCharacters( theString, uniRange, uniBuffer); |
/* create the ATSUI layout */ |
err = ATSUCreateTextLayoutWithTextPtr( uniBuffer, 0, |
textLength, textLength, 1, |
(unsigned long *) &textLength, &theStyle, |
&theLayout); |
if (err != noErr) goto bail; |
if(!err) |
{ |
// Assign the CGContext to the layout |
// so the text is imaged into the CG Context supplied |
ByteCount iSize = sizeof(CGContextRef); |
ATSUAttributeTag iTag = kATSUCGContextTag; |
ATSUAttributeValuePtr iValuePtr=&ctx; |
ATSUSetLayoutControls( theLayout, 1, &iTag, &iSize, &iValuePtr ); |
} |
/* draw the text */ |
err = ATSUDrawText(theLayout, 0, textLength, |
FixRatio(h, 1), FixRatio(v, 1)); |
if (err != noErr) goto bail; |
/* clean up */ |
bail: |
if (uniBuffer != NULL) DisposePtr((Ptr) uniBuffer); |
if (theLayout != NULL) ATSUDisposeTextLayout(theLayout); |
return err; |
} |
static OSStatus drawCircularText(CGContextRef context, short fontNum) |
{ |
OSStatus err = noErr; |
// we make this style static so it only needs to be created once |
static ATSUStyle theStyle = NULL; |
if(!theStyle) |
err = MakeATSUIStyle(fontNum, normal, 20, &theStyle); |
if(!err){ |
CFStringRef ourString = CFSTR("Rotated Text With Quartz"); |
int i, numTextGuys; |
float angleIncrement; |
numTextGuys = 6; |
angleIncrement = (float)2*PI/numTextGuys; |
for(i = 0; i < numTextGuys ; i++){ |
err = RenderCFString(context, ourString, theStyle, 0, 0); |
if(err) |
printf("got an error from RenderCFString\n"); |
CGContextRotateCTM(context, angleIncrement); |
} |
CGContextRotateCTM(context, angleIncrement); // one last for fun! |
// we want to keep the style for the next time |
// our drawProc is called so we don't call |
// ATSUDisposeStyle(theStyle); |
}else |
printf("got an error from MakeATSUIStyle\n"); |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14