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.
main.c
/* |
File: main.c |
Description: |
main program file for the TextNameTool sample. |
Copyright: |
Copyright (c) 2003 Apple Computer, Inc. All rights reserved. |
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. |
Change History (most recent first): |
Wed, Aug 27, 2003 -- created |
*/ |
/* Mac headers */ |
#include <CoreServices/CoreServices.h> |
#include <ApplicationServices/ApplicationServices.h> |
#include <QuickTime/ImageCompression.h> |
#include <QuickTime/QuickTimeComponents.h> |
/* for drawing strings using ATSUI */ |
#include "DrawUnicodeString.h" |
/* a simple set of string/byte manipulation routines. */ |
#include "TextUtilities.h" |
/* our image operations */ |
#include "PixOps.h" |
/* we use the getopt() routine for parsing command line arguments */ |
#include <unistd.h> |
/* HaltWithError is a printf like function that returns an error |
status should our tool need to exit because of abnormal conditions. |
Rather than returning a non-zero status, our tool encodes the result |
string as an XML message so the caller can decode the result. */ |
static void HaltWithError(const char *fmt, ...) { |
va_list args; |
TextOutputBuffer theResult; |
LongTextStringPtr accResults; |
InitTextBuffer(&theResult); |
PrintfTextBuffer(&theResult, "<textnametool>\n"); |
PrintfTextBuffer(&theResult, "<status>error</status>\n"); |
PrintfTextBuffer(&theResult, "<message>"); |
va_start(args, fmt); |
VSPrintfAsXML(&theResult, fmt, args); |
va_end(args); |
PrintfTextBuffer(&theResult, "</message>\n"); |
PrintfTextBuffer(&theResult, "</textnametool>\n"); |
if (TextBufferLongTextString(&theResult, &accResults) == noErr) { |
fwrite(accResults->text.strp, accResults->text.len, 1, stdout); |
fflush(stdout); |
DisposeLongTextString(accResults); |
} |
DisposeTextBuffer(&theResult); |
exit(0); |
} |
/* We perform some special formatting and text wrapping depending on |
our parameters. We use this structure for maintaining a list of lines |
that are to be drawn. Using our orignial string, we use TextPartitionStruct |
structures to reference the different parts that will reside on separate |
lines. Each TextPartitionStruct describes one line of text in the resulting |
image. All of the lines of text in the image are represented using a |
linked list of TextPartitionStruct structures. */ |
typedef struct TextPartitionStruct TextPartitionStruct; |
typedef TextPartitionStruct *TextPartition; |
struct TextPartitionStruct { |
TextPartition next; /* next text partition */ |
UniChar *unicodeText; /* pointer to the unicode text */ |
long unicodeTextLength; /* length of unicode text */ |
Rect textbounds; /* box around origin for text dimensions */ |
Rect imagebounds; /* box around origin for text image */ |
Point origin; /* origin where text is drawn */ |
}; |
/* TextPartDesc describes the layout of the text string |
as it will be drawn on the image. */ |
typedef struct { |
TextPartition first, last; /*list of parts */ |
Rect textbounds, imagebounds; |
} TextPartDesc; |
/* IsSpaceChar is a utility routine used to identify white space |
characters in the message string. These characters are used for |
word wrap, etc... */ |
static int IsSpaceChar(UniChar ch) { |
return ((ch == ' ') || (ch == '\n') || (ch == '\r') || (ch == '\t')); |
} |
/* given a width, a unicode string, and some other options, the |
PartitionTextForWidth routine generates a TextPartDesc structure that |
describes the final placement of the letters in the string in the |
resulting immage. */ |
static OSStatus PartitionTextForWidth(UniChar *unicodeText, long unicodeTextLength, |
long widthmax, Rect *borders, long linespacing, int observeLineBreaks, |
ATSUStyle atsuiStyle, TextPartDesc *partitions) { |
UniChar *uXStart, *uXEnd, *uXRover; |
Rect uBounds, uImage; |
OSStatus err; |
TextPartition localtp; |
/* init partitions record */ |
partitions->first = partitions->last = NULL; |
SetRect(&partitions->imagebounds, 0, 0, 0, 0); |
/* set up our text pointers */ |
err = noErr; |
/* First, we break text into sections defined by the width in such |
a way that no line of text will exceed the maximum pixel width as |
defined by widthmax. If the caller has asked us to use carriage |
returns to find line endings, then we use those as well. */ |
uXStart = unicodeText; |
uXEnd = unicodeText + unicodeTextLength; |
while (uXStart < uXEnd && err==noErr) { |
/* trim white space from beginning and end */ |
while (uXStart < uXEnd && IsSpaceChar(uXEnd[-1])) --uXEnd; |
while (uXStart < uXEnd && IsSpaceChar(uXStart[0])) uXStart++; |
/* measure section */ |
if (uXStart < uXEnd && err==noErr) { |
/* observing line breaks? then search for one in the line */ |
if ( observeLineBreaks ) { |
for (uXRover=uXStart; uXRover < uXEnd; uXRover++) { |
if (*uXRover == '\n' || *uXRover == '\r') break; |
} |
if ((uXRover < uXEnd) && (*uXRover == '\n' || *uXRover == '\r')) { |
uXEnd = uXRover; |
} |
} |
/* measure the line width */ |
err = MeasureUnicodeString( uXStart, (uXEnd - uXStart), atsuiStyle, &uBounds, &uImage); |
if (err == noErr) { |
/* if it's too long, knock off the last word */ |
if ( (uBounds.right - uBounds.left) > widthmax ) { |
while (uXStart < uXEnd && ! IsSpaceChar(uXEnd[-1])) --uXEnd; |
} else { |
localtp = (TextPartition) malloc(sizeof(TextPartitionStruct)); |
if (localtp == NULL) { |
err = memFullErr; |
} else { |
/* init fields */ |
localtp->next = NULL; |
localtp->unicodeText = uXStart; |
localtp->unicodeTextLength = (uXEnd - uXStart); |
localtp->textbounds = uBounds; |
localtp->imagebounds = uImage; |
localtp->origin.v = localtp->origin.h = 0; |
/* add to list */ |
if (partitions->first == NULL) { |
partitions->first = partitions->last = localtp; |
} else { |
partitions->last->next = localtp; |
partitions->last = localtp; |
} |
/* move to next part of string */ |
uXStart = uXEnd; |
uXEnd = (unicodeText + unicodeTextLength); |
} |
} |
} |
} |
} |
/* Here, we align coordinates of the lines appropriately. In the |
first part, we discovered the length of the lines and positioned all |
of the lines relative to the origin. Now, we stagger the lines |
downwards starting at the origin so that they do not overlap. */ |
if (err == noErr) { |
SetRect(&partitions->imagebounds, 0, 0, 0, 0); |
SetRect(&partitions->textbounds, 0, 0, 0, (borders->top - linespacing)); |
for (localtp=partitions->first; localtp!= NULL; localtp=localtp->next) { |
short ascent, descent, hoffset, voffset; |
/* adding to the bottom of the boundary */ |
ascent = localtp->origin.v - localtp->textbounds.top; |
descent = localtp->origin.v - localtp->textbounds.top; |
hoffset = localtp->origin.h - localtp->textbounds.left + borders->left; |
voffset = partitions->textbounds.bottom + ascent + linespacing; |
OffsetRect(&localtp->textbounds, hoffset, voffset); |
OffsetRect(&localtp->imagebounds, hoffset, voffset); |
localtp->origin.v += voffset; |
localtp->origin.h += hoffset; |
UnionRect( &partitions->textbounds, &localtp->textbounds, &partitions->textbounds); |
UnionRect( &partitions->imagebounds, &localtp->imagebounds, &partitions->imagebounds); |
} |
/* after calculating the final position of all of the lines, we can |
calculate the location of the overall frame surrounding all of the |
text. Here we do the final frame adjustment. */ |
partitions->textbounds.top = 0; |
partitions->textbounds.left = 0; |
partitions->textbounds.right += borders->right; |
partitions->textbounds.bottom += borders->bottom; |
} |
return err; |
} |
/* GetListOfFonts generates XML containing a list |
of all of the fonts that are available for use. */ |
static OSErr GetListOfFonts(TextOutputBuffer *results) { |
OSStatus err; |
FontNameIDVector fiv; |
long fivlen, i; |
err = GetInstalledFontList( &fiv, &fivlen); |
if (err == noErr) { |
PrintfTextBuffer(results, "<fonts>\n"); |
for ( i = 0; i < fivlen; i++ ) { |
PrintfTextBuffer(results, "<font><name>"); |
PrintfAsXML(results, "%s", fiv[i].name); |
PrintfTextBuffer(results, "</name><id>"); |
PrintfAsXML(results, "%d", fiv[i].theFontID); |
PrintfTextBuffer(results, "</id></font>\n"); |
} |
PrintfTextBuffer(results, "</fonts>\n"); |
} else { |
HaltWithError("error retrieving font list = %d", err); |
} |
return err; |
} |
/* HTMLColor is the structure we use for mapping |
HTML color names to HTML hex color values. We match |
a few of the standard names. */ |
typedef struct { |
char* name; |
char* httpcolor; |
} HTMLColor; |
static HTMLColor gHTMLColors[] = { |
{ "aqua", "00,FF,FF" }, |
{ "black", "00,00,00" }, |
{ "blue", "00,00,FF" }, |
{ "ikb", "50,27,FF"}, |
{ "fuchsia", "FF,00,FF" }, |
{ "gray", "80,80,80" }, |
{ "green", "00,80,00" }, |
{ "lime", "00,FF,00" }, |
{ "maroon", "80,00,00" }, |
{ "navy", "00,00,80" }, |
{ "olive", "80,80,00" }, |
{ "purple", "80,00,80" }, |
{ "red", "FF,00,00" }, |
{ "silver", "C0,C0,C0" }, |
{ "teal", "00,80,80" }, |
{ "yellow", "FF,FF,00" }, |
{ "white", "FF,FF,FF" } |
}; |
#define nHTMLColors (sizeof(gHTMLColors)/sizeof(HTMLColor)) |
int main(int argc, char * const argv[]) { |
OSStatus err; |
Boolean makeFontImage; /* true if we're creating an image file */ |
long theSize = 36; /* default font size */ |
long fstyle = 0; /* default font style information - quickdraw bits */ |
long widthmax = 550; /* default bounding width for line wrapping */ |
long linespacing = 0; /* default spacing between lines */ |
int usecg = 1; /* by default, use cg drawing */ |
int jpgquality = maxQuality; /* jpg drawing quality */ |
int autobound=0; /* autobounding flag */ |
int autotrim=0; /* auto trim flag */ |
int testonly=0; /* auto trim flag */ |
char *destFile = NULL; /* destination file for saving image */ |
LongTextStringPtr messageDataString = NULL; /* raw message data input */ |
char *color = NULL; /* selected color for text drawing */ |
char *backgroundfile = NULL; /* special background file */ |
int ishtmltext = 0; /* message text is encoded as html text */ |
RGBColor textrgb = {0, 0, 0}; /* encoded color information */ |
unsigned int ared, agreen, ablue; |
char *fontname = NULL; /* name of the font requested */ |
ATSUFontID theFont = -1; /* id number for the font */ |
char *kind = "jpg"; /* output file type, jpg or png */ |
short width, height, i; |
int observeLineBreaks = 0; /* use line breaks in text to break lines in image */ |
TextOutputBuffer gResults; /* for accumulating results */ |
LongTextStringPtr accResults; /* final results buffer */ |
UniChar *unicodeText; /* message encoded as unicode text */ |
long unicodeTextLength; /* length of encoded unicode text */ |
Rect borders; /* top, left, bottom, and right borders for image */ |
/* set up */ |
makeFontImage = true; |
SetRect(&borders, 0, 0, 0, 0); |
/* We accumulate our text results in a buffer until we are ready to |
return them. If an error occurs while we are accumulating results, then |
we throw away this buffer and return another one describing the error |
instead. */ |
InitTextBuffer(&gResults); |
PrintfTextBuffer(&gResults, "<textnametool>\n"); |
PrintfTextBuffer(&gResults, "<status>ok</status>\n"); |
/* scan parameters */ |
while (getopt(argc, argv, "heLtnr:dq:x:k:b:w:a:i:f:s:o:m:c:p:") != -1) { |
switch (optopt) { |
case 'h': /* display help information */ |
PrintfTextBuffer(&gResults, "<help>\n"); |
PrintfAsXML(&gResults, "%s [options]\n", argv[0]); |
PrintfAsXML(&gResults, "-L list all fonts available\n"); |
PrintfAsXML(&gResults, "-f name, use this font\n"); |
PrintfAsXML(&gResults, "-n, observe line breaks in text (default=off)\n"); |
PrintfAsXML(&gResults, "-s size, font size (default=36)\n"); |
PrintfAsXML(&gResults, "-w width, maximum width for wrapping purposes (default=550)\n"); |
PrintfAsXML(&gResults, "-x file, read message text from a file\n"); |
PrintfAsXML(&gResults, "-e, message text is encoded as html text\n"); |
PrintfAsXML(&gResults, "-r file, use contents of this file for the background\n"); |
PrintfAsXML(&gResults, "-x -, read message text from standard input\n"); |
PrintfAsXML(&gResults, "-p linespacing (default=0, space between lines)\n"); |
PrintfAsXML(&gResults, "-q quality (default=high, min, low, normal, high, max)\n"); |
PrintfAsXML(&gResults, "-b border (default=0, nnn, trim, or auto)\n"); |
PrintfAsXML(&gResults, " trim works just like the 'trim' command in photoshop\n"); |
PrintfAsXML(&gResults, " cropping as much white space from around the text as possible\n"); |
PrintfAsXML(&gResults, " auto starts with a border of zero, but may expand it\n"); |
PrintfAsXML(&gResults, " if, say, italics or some other part of the image goes\n"); |
PrintfAsXML(&gResults, " outside of the border.\n"); |
PrintfAsXML(&gResults, "-b h,v (default=0, left/right and top/bottom borders)\n"); |
PrintfAsXML(&gResults, "-b l,t,r,b (default=0, left,top,right,bottom borders)\n"); |
PrintfAsXML(&gResults, "-a flags, font format attributes:\n"); |
PrintfAsXML(&gResults, " b bold\n"); |
PrintfAsXML(&gResults, " i italic\n"); |
PrintfAsXML(&gResults, " u underline\n"); |
PrintfAsXML(&gResults, " c condense\n"); |
PrintfAsXML(&gResults, " e extend\n"); |
PrintfAsXML(&gResults, "-o filename, output file name\n"); |
PrintfAsXML(&gResults, "-k kind, (jpg or png, default=jpg)\n"); |
PrintfAsXML(&gResults, "-d, use 4 bit quickdraw text drawing (default = use 8 bit cg drawing)\n"); |
PrintfAsXML(&gResults, "-m message, message to put in output\n"); |
PrintfAsXML(&gResults, "-c color, select text color (default=black):\n"); |
PrintfAsXML(&gResults, " RR,GG,BB - in hex (00 thru FF)\n"); |
for (i=0; i < nHTMLColors; i++) |
PrintfAsXML(&gResults, " %s = %s\n", gHTMLColors[i].name, gHTMLColors[i].httpcolor); |
PrintfAsXML(&gResults, "-i graphicsfilename - investigate file, get bounds\n"); |
PrintfAsXML(&gResults, "-h this command\n"); |
PrintfTextBuffer(&gResults, "</help>"); |
makeFontImage = false; |
break; |
case 'a': /* set text attributes */ |
{ char *p; |
for (p=optarg; *p; p++) switch(*p) { |
case 'b': fstyle |= bold; break; |
case 'i': fstyle |= italic; break; |
case 'u': fstyle |= underline; break; |
case 'c': fstyle |= condense; break; |
case 'e': fstyle |= extend; break; |
default: |
HaltWithError("unknown font attribute '%s'", optarg); |
break; |
} |
} |
break; |
case 'd': /* use quickdraw drawing - 4 bit */ |
usecg = 0; |
break; |
case 'n': /* observe line breaks in text */ |
observeLineBreaks = 1; |
break; |
case 't': /* test only, return sizes, but don't create file */ |
testonly = 1; |
break; |
case 'e': /* text is encoded as html text */ |
ishtmltext = 1; |
break; |
case 'r': /* special background */ |
backgroundfile = optarg; |
break; |
case 'k': /* output file kind */ |
kind = optarg; |
if (strcmp(optarg, "jpg") != 0 || strcmp(optarg, "png") != 0) |
HaltWithError("unknown file format: '%s'", optarg); |
break; |
case 'q': /* output file encoding quality */ |
if (strcmp(optarg, "min") == 0) |
jpgquality = minQuality; |
else if (strcmp(optarg, "low") == 0) |
jpgquality = lowQuality; |
else if (strcmp(optarg, "normal") == 0) |
jpgquality = normalQuality; |
else if (strcmp(optarg, "high") == 0) |
jpgquality = highQuality; |
else if (strcmp(optarg, "max") == 0) |
jpgquality = maxQuality; |
else HaltWithError("unknown quality: '%s'", optarg); |
break; |
case 'L': /* print a list of fonts */ |
err = GetListOfFonts(&gResults); |
makeFontImage = false; |
break; |
case 'x': /* read message text from a file */ |
{ long length, total = 0; |
unsigned char iobuffer[1024]; |
TextOutputBuffer messageAccumulator; |
InitTextBuffer(&messageAccumulator); |
if (strcmp(optarg, "-") == 0) { |
while ((length = fread(iobuffer, 1, sizeof(iobuffer)-1, stdin)) != 0) { |
err = TextBufferWrite(&messageAccumulator, iobuffer, length); |
if (err != noErr) |
HaltWithError("Error %d retrieving input data", err); |
total += length; |
} |
if (total == 0) HaltWithError("message too short"); |
} else { |
FILE* f; |
f = fopen(optarg, "r"); |
if (f == NULL) HaltWithError("error opening '%s' for input", optarg); |
while ((length = fread(iobuffer, 1, sizeof(iobuffer)-1, f)) != 0) { |
err = TextBufferWrite(&messageAccumulator, iobuffer, length); |
if (err != noErr) |
HaltWithError("Error %d retrieving input data", err); |
total += length; |
} |
fclose(f); |
if (total == 0) HaltWithError("message too short"); |
} |
err = TextBufferLongTextString(&messageAccumulator, &messageDataString); |
if (err != noErr) |
HaltWithError("Error %d allocating message buffer", err); |
DisposeTextBuffer(&messageAccumulator); |
} |
break; |
case 'f': /* set the font name */ |
err = GetNamedFontID(optarg, &theFont); /* place to return the ID */ |
if (err != noErr) { |
HaltWithError("error looking up font '%s' = %d", optarg, err); |
} |
fontname = optarg; |
break; |
case 'w': /* set the bounding width */ |
widthmax = atoi(optarg); |
if (widthmax < 30 || widthmax > 2000) |
HaltWithError("width %d is out of range", widthmax); |
break; |
case 'p': /* set the inter-line spacing */ |
linespacing = atoi(optarg); |
if (linespacing < 0 || linespacing > 100) |
HaltWithError("linespacing %d is out of range", linespacing); |
break; |
case 'b': /* bounding border type and sizes */ |
if (strcmp(optarg, "trim") == 0) { |
SetRect(&borders, 30, 30, 30, 30); |
autotrim = 1; |
} else if (strcmp(optarg, "auto") == 0) { |
autobound = 1; |
} else { |
long ba, bb, bc, bd, n; |
n = sscanf(optarg, "%ld,%ld,%ld,%ld", &ba, &bb, &bc, &bd); |
switch(n) { |
case 1: |
if (ba < 0 || ba > 2000) HaltWithError("border %d is out of range", ba); |
SetRect(&borders, ba, ba, ba, ba); |
break; |
case 2: |
if (ba < 0 || ba > 2000) HaltWithError("border %d is out of range", ba); |
if (bb < 0 || bb > 2000) HaltWithError("border %d is out of range", bb); |
SetRect(&borders, ba, bb, ba, bb); |
break; |
case 4: |
if (ba < 0 || ba > 2000) HaltWithError("border %d is out of range", ba); |
if (bb < 0 || bb > 2000) HaltWithError("border %d is out of range", bb); |
if (bc < 0 || bc > 2000) HaltWithError("border %d is out of range", bc); |
if (bd < 0 || bd > 2000) HaltWithError("border %d is out of range", bd); |
SetRect(&borders, ba, bb, bc, bd); |
break; |
default: |
HaltWithError("invalid border specification: %s", optarg); |
break; |
} |
} |
break; |
case 's': /* font size */ |
theSize = atoi(optarg); |
if (theSize < 5 || theSize > 2000) |
HaltWithError("size %d is out of range", theSize); |
break; |
case 'o': /* output file name */ |
destFile = optarg; |
break; |
case 'm': /* message text as argument */ |
err = GetLongTextStringFromBuffer((unsigned char *) optarg, strlen(optarg), &messageDataString); |
if (err != noErr) |
HaltWithError("error %d allocating message string", err); |
break; |
case 'c': /* set the text color */ |
color = optarg; |
if ( sscanf(optarg, "%x,%x,%x", &ared, &agreen, &ablue) == 3 ) { |
textrgb.red = ared * 257; |
textrgb.green = agreen * 257; |
textrgb.blue = ablue * 257; |
} else { |
for (i=0; i < nHTMLColors; i++) { |
if ((strcmp(optarg, gHTMLColors[i].name) == 0) |
&& (sscanf(gHTMLColors[i].httpcolor, "%x,%x,%x", &ared, &agreen, &ablue) == 3)) { |
textrgb.red = ared * 257; |
textrgb.green = agreen * 257; |
textrgb.blue = ablue * 257; |
break; |
} |
} |
if (i == nHTMLColors) { |
HaltWithError("unknown color '%s'", optarg); |
} |
} |
break; |
case 'i': /* investigate image file bounds */ |
err = GetGraphicsFileBounds(optarg, &width, &height); |
if (err != noErr) { |
HaltWithError("error %d getting dimensions for '%s'", err, argv[optind]); |
} else { |
PrintfTextBuffer(&gResults, "<investigated-image>\n<file>"); |
PrintfAsXML(&gResults, "%s", optarg); |
PrintfTextBuffer(&gResults, "</file><width>"); |
PrintfAsXML(&gResults, "%d", width); |
PrintfTextBuffer(&gResults, "</width><height>"); |
PrintfAsXML(&gResults, "%d", height); |
PrintfTextBuffer(&gResults, "</height>\n</investigated-image>\n"); |
} |
makeFontImage = false; |
break; |
case '?': /* wtf, complain... */ |
HaltWithError("invalid argument: %s", argv[0], argv[optind]); |
break; |
} |
} |
/* create a text image file */ |
if (makeFontImage) { |
ATSUStyle atsuiStyle; |
TextPartDesc partitions; |
TextPartition localtp; |
GWorldPtr theWorld; |
LongTextStringPtr messageTextString; |
/* verify parameters */ |
if (messageDataString == NULL || destFile == NULL || fontname == NULL) |
HaltWithError("not enough information to create image file"); |
/* convert from html */ |
if (ishtmltext) { |
messageTextString = DecodeHTMLString(messageDataString->text.strp, messageDataString->text.len); |
if (messageTextString == NULL) |
HaltWithError("error decoding html string"); |
} else { |
err = GetLongTextStringFromBuffer(messageDataString->text.strp, messageDataString->text.len, &messageTextString); |
if (err != noErr) |
HaltWithError("error %d allocating message buffer", err); |
} |
/* convert message to utf8 using CFString */ |
{ CFStringRef theCFString; |
theCFString = CFStringCreateWithBytes(NULL, messageTextString->text.strp, messageTextString->text.len, |
kCFStringEncodingUTF8, false); |
if (theCFString == NULL) |
HaltWithError("Error converting messaage to unicode"); |
unicodeTextLength = CFStringGetLength(theCFString); |
unicodeText = (UniChar*) malloc( unicodeTextLength*sizeof(UniChar) ); |
if (unicodeText == NULL) |
HaltWithError("error allocating unicode buffer"); |
CFStringGetCharacters(theCFString, CFRangeMake(0, unicodeTextLength), unicodeText); |
CFRelease(theCFString); |
} |
/* create the font style */ |
err = MakeSimpleATSUIStyle(theFont, theSize, fstyle, &textrgb, &atsuiStyle); |
if (err != noErr) |
HaltWithError("error creating style for font '%s' = %d", fontname, err); |
/* adjust wrap width if we are using an import file */ |
if (backgroundfile != NULL) { |
short width, height; |
err = GetGraphicsFileBounds(backgroundfile, &width, &height); |
if (err != noErr) |
HaltWithError("error %d getting width and height of '%s'", err, backgroundfile); |
if (borders.left > width) |
HaltWithError("borders (left=%d) places text to the right of background image file of width %d", |
borders.left, width); |
if (borders.top > height) |
HaltWithError("borders (top=%d) places text below bottom of background image file of height %d", |
borders.top, height); |
if (borders.left + borders.right + widthmax > width) |
widthmax = width - (borders.left + borders.right); |
if (widthmax <= 32+theSize) { |
HaltWithError("border does not provide enough space to wordwrap text on background image"); |
} |
} |
/* break up the text */ |
err = PartitionTextForWidth(unicodeText, unicodeTextLength, widthmax, |
&borders, linespacing, observeLineBreaks, atsuiStyle, &partitions); |
if (err != noErr) |
HaltWithError("error calculating text partitions = %d", err); |
/* adjust boundaries for image boundary */ |
if (autobound) { |
short dh, dv; |
dh = dv = 0; |
if (backgroundfile != NULL) |
HaltWithError("autobounding not allowed with background image, sorry"); |
/* shift left */ |
if (partitions.imagebounds.left < 0) |
dh = - partitions.imagebounds.left; |
/* shift down */ |
if (partitions.imagebounds.top < 0) |
dv = - partitions.imagebounds.top; |
OffsetRect(&partitions.imagebounds, dh, dv); |
/* draw the strings */ |
for (localtp=partitions.first; localtp!= NULL; localtp=localtp->next) { |
OffsetRect(&localtp->textbounds, dh, dv); |
OffsetRect(&localtp->imagebounds, dh, dv); |
localtp->origin.v += dv; |
localtp->origin.h += dh; |
} |
if (partitions.textbounds.right < partitions.imagebounds.right) |
partitions.textbounds.right = partitions.imagebounds.right; |
if (partitions.textbounds.bottom < partitions.imagebounds.bottom) |
partitions.textbounds.bottom = partitions.imagebounds.bottom; |
} |
/* draw if we're not testing */ |
if ( ! testonly ) { |
/* set up the drawing buffer */ |
if (backgroundfile != NULL) { |
err = LoadGraphicsFile(backgroundfile, &theWorld, &partitions.textbounds); |
if (err != noErr) |
HaltWithError("error %d loading background file '%s'", err, backgroundfile); |
SetGWorld(theWorld, NULL); |
} else { |
theWorld = MakeGWorld(&partitions.textbounds); |
if (theWorld == NULL) |
HaltWithError("unable to allocate drawing buffer"); |
SetGWorld(theWorld, NULL); |
EraseRect(&partitions.textbounds); |
} |
/* draw */ |
if ( usecg ) { |
CGrafPtr thePort; |
CGContextRef theContext; |
Rect portRect; |
thePort = theWorld; |
GetPortBounds( thePort, &portRect ); |
/* set up the drawing buffer */ |
err = QDBeginCGContext( thePort, &theContext); |
if (err != noErr) |
HaltWithError("error creating cg context = %d", err); |
/* draw the strings */ |
for (localtp=partitions.first; localtp!= NULL; localtp=localtp->next) { |
err = RenderUnicodeString( localtp->unicodeText, localtp->unicodeTextLength, |
atsuiStyle, localtp->origin.h, (portRect.bottom - localtp->origin.v), theContext); |
if (err != noErr) |
HaltWithError("error rendering text = %d", err); |
} |
/* we're done drawing */ |
QDEndCGContext( thePort, &theContext ); |
} else { |
/* draw the strings */ |
for (localtp=partitions.first; localtp!= NULL; localtp=localtp->next) { |
err = RenderUnicodeString( localtp->unicodeText, localtp->unicodeTextLength, |
atsuiStyle, localtp->origin.h, localtp->origin.v, NULL); |
if (err != noErr) |
HaltWithError("error rendering text = %d", err); |
} |
} |
/* done with the style record now */ |
ATSUDisposeStyle(atsuiStyle); |
/* trim image, if necessary */ |
if (autotrim) { |
Rect trimmedBounds; |
if (backgroundfile != NULL) |
HaltWithError("autotrim not allowed with background image, sorry"); |
DiscoverGWorldUsedBounds(theWorld, &partitions.textbounds, &trimmedBounds); |
if ( ! EqualRect(&partitions.textbounds, &trimmedBounds) ) { |
GWorldPtr trimmedWorld; |
trimmedWorld = MakeGSubWorld(theWorld, &trimmedBounds); |
if (trimmedWorld == NULL) |
HaltWithError("error generating trimmed image"); |
UnmakeGWorld(theWorld); |
theWorld = trimmedWorld; |
partitions.textbounds = trimmedBounds; |
} |
} |
/* save the file */ |
if (strcmp(kind, "jpg") == 0) { |
err = SaveAsJPG(theWorld, destFile, jpgquality); |
} else if (strcmp(kind, "png") != 0) { |
err = SaveAsPNG(theWorld, destFile); |
} |
if (err != noErr) |
HaltWithError("error %d saving file '%s'", err, destFile); |
/* done with the gworld */ |
UnmakeGWorld(theWorld); |
} |
/* print the results */ |
PrintfTextBuffer(&gResults, "<generated-image>\n<file>"); |
PrintfAsXML(&gResults, "%s", destFile); |
PrintfTextBuffer(&gResults, "</file>\n<font>"); |
PrintfAsXML(&gResults, "%s", fontname); |
PrintfTextBuffer(&gResults, "</font>\n<size>"); |
PrintfAsXML(&gResults, "%d", theSize); |
PrintfTextBuffer(&gResults, "</size>\n<width>"); |
PrintfAsXML(&gResults, "%d", (partitions.textbounds.right - partitions.textbounds.left)); |
PrintfTextBuffer(&gResults, "</width>\n<height>"); |
PrintfAsXML(&gResults, "%d", partitions.textbounds.bottom - partitions.textbounds.top); |
PrintfTextBuffer(&gResults, "</height>\n</generated-image>\n"); |
} |
/* finalize tool output results */ |
PrintfTextBuffer(&gResults, "</textnametool>\n"); |
if (TextBufferLongTextString(&gResults, &accResults) == noErr) { |
fwrite(accResults->text.strp, accResults->text.len, 1, stdout); |
DisposeLongTextString(accResults); |
} |
DisposeTextBuffer(&gResults); |
return 0; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-27